Plone 5.2.1 -> 5.2.2 Page Template Issues

We are encountering several issues with our page templates after upgrading Plone 5.2.1 -> 5.2.2.

It seems like the pipe | syntax does not work anymore, e.g.:

<span tal:attributes="value view/getDefaultAddCount|1"/>

we had to change it to:

<span tal:attributes="value python:view.getDefaultAddCount() or 1"/>

Also we are still in migration from Archetypes -> Dexterity and encountered this Traceback:

2020-08-31 13:30:56 ERROR Zope.SiteErrorLog 1598873456.460.00705242728537 http://localhost:8080/senaite/clients/client-1/H2O-0001/base_view
Traceback (innermost last):
  Module ZServer.ZPublisher.Publish, line 144, in publish
  Module ZPublisher.mapply, line 85, in mapply
  Module ZServer.ZPublisher.Publish, line 44, in call_object
  Module bika.lims.browser.analysisrequest.view, line 43, in __call__
  Module bika.lims.browser.header_table, line 84, in __call__
  Module Products.Five.browser.pagetemplatefile, line 126, in __call__
  Module Products.Five.browser.pagetemplatefile, line 61, in __call__
  Module zope.pagetemplate.pagetemplate, line 135, in pt_render
  Module Products.PageTemplates.engine, line 367, in __call__
  Module z3c.pt.pagetemplate, line 176, in render
  Module chameleon.zpt.template, line 307, in render
  Module chameleon.template, line 214, in render
  Module chameleon.template, line 192, in render
  Module c5ab0b357ddc16e8692496fb4a5ff01c, line 539, in render
  Module 04cd26ecd3fd941f07315df23e6b4c28, line 1098, in render_edit
  Module 1057452b2f8fbd423d2becf6bd18f50c, line 289, in render_edit
  Module zope.tales.pythonexpr, line 73, in __call__
   - __traceback_info__: ( test(error_id, 'field error ' + 'Archetypes' + widget.getName(), 'field ' + 'Archetypes' + widget.getName()) + ' ' + kss_class)
  Module <string>, line 1, in <module>
NameError: name 'test' is not defined

 - Expression: "python: test(error_id, 'field error ' + 'Archetypes' + widget.getName(), 'field ' + 'Archetypes' + widget.getName()) + ' ' + kss_class"
 - Filename:   ... gg/Products/Archetypes/skins/archetypes/widgets/field.pt
 - Location:   (line 82: col 35)
 - Source:     ... python: test(error_id, 'field error ' + 'Archetypes' + widget.getName(), 'field ' + 'Archetypes' + widget.getName()) + ' ' + kss_class ...
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "field_macro | context/widgets/field/macros/edit"
 - Filename:   ... ins/senaite_templates/senaite_widgets/referencewidget.pt
 - Location:   (line 52: col 30)
 - Source:     ... se-macro="field_macro | context/widgets/field/macros/edit">
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "python:context.widget(fieldName, mode=mode)"
 - Filename:   ... ite.core/src/bika/lims/browser/templates/header_table.pt
 - Location:   (line 54: col 40)
 - Source:     ... use-macro="python:context.widget(fieldName, mode=mode)"/>
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Arguments:  request: <HTTPRequest, URL=http://localhost:8080/senaite/clients/client-1/H2O-0001/base_view>
               attrs: {}
               loop: {u'action': <Products.PageTemplates.engine.RepeatItem object at 0x10ef340d0>}
               errors: {}
               container: <AnalysisRequest at /senaite/clients/client-1/H2O-0001>
               UID: '6614dd69853546c895a616a388ebb100'
               traverse_subpath: []
               field: <Field Contact(uidreference:rw)>
               template: <Products.Five.browser.pagetemplatefile.ViewPageTemplateFile object at 0x10eb01110>
               translate: <function translate at 0x10ef37938>
               macroname: u'python:context.widget(fieldName, mode=mode)'
               primary_bound: False
               widget: <senaite.core.browser.widgets.referencewidget.ReferenceWidget object at 0x10a3cf890>
               repeat: <Products.PageTemplates.engine.RepeatDictWrapper object at 0x10ae54550>
               views: <Products.Five.browser.pagetemplatefile.ViewMapper object at 0x10ef48290>
               args: ()
               editable: True
               here: <AnalysisRequest at /senaite/clients/client-1/H2O-0001>
               fieldName: 'Contact'
               user: <PropertiedUser 'admin'>
               nothing: None
               accessor: <bound method AnalysisRequest.getContact of <AnalysisRequest at /senaite/clients/client-1/H2O-0001>>
               field_macro: <chameleon.zpt.template.Macro object at 0x10ec83bb0>
               prominent: [{'html': "<div id='client-1' class='field reference'>  <a class='link' uid='ee57f95d6d4642828afd3cfefde74476' href='http://localhost:8080/senaite/clients/client-1'>    Happy Hills  </a></div>", 'fieldName': 'Client', 'mode': 'structure'}, {'fieldName': 'Contact', 'mode': 'edit'}, {'fieldName': 'CCContact', 'mode': 'edit'}, {'fieldName': 'CCEmails', 'mode': 'edit'}, {'fieldName': 'DateSampled', 'mode': 'edit'}, {'fieldName': 'EnvironmentalConditions', 'mode': 'edit'}]
               dummy: ([{'html': "<div id='client-1' class='field reference'>  <a class='link' uid='ee57f95d6d4642828afd3cfefde74476' href='http://localhost:8080/senaite/clients/client-1'>    Happy Hills  </a></div>", 'fieldName': 'Client', 'mode': 'structure'}, {'fieldName': 'Contact', 'mode': 'edit'}, {'fieldName': 'CCContact', 'mode': 'edit'}, {'fieldName': 'CCEmails', 'mode': 'edit'}, {'fieldName': 'DateSampled', 'mode': 'edit'}, {'fieldName': 'EnvironmentalConditions', 'mode': 'edit'}], [[{'fieldName': 'Batch', 'mode': 'edit'}, {'fieldName': 'SubGroup', 'mode': 'edit'}, {'html': '', 'fieldName': 'Template', 'mode': 'structure'}, {'html': "<div id='analysisprofile-2' class='field reference'>  <a class='link' uid='0025f8089d8b4a9f8090fd99cbbcab33' href='http://localhost:8080/senaite/bika_setup/bika_analysisprofiles/analysisprofile-2'>    Total Hardness  </a></div>", 'fieldName': 'Profiles', 'mode': 'structure'}, {'html': "<div id='sampletype-6' class='field reference'>  <a class='link' uid='d9b64b7f240f4911bc6ea91f1a6c4cbe' href='http://localhost:8080/senaite/bika_setup/bika_sampletypes/sampletype-6'>    Water  </a></div>", 'fieldName': 'SampleType', 'mode': 'structure'}, {'fieldName': 'Container', 'mode': 'edit'}, {'fieldName': 'Preservation', 'mode': 'edit'}, {'fieldName': 'Specification', 'mode': 'edit'}], [{'fieldName': 'PublicationSpecification', 'mode': 'edit'}, {'fieldName': 'SamplePoint', 'mode': 'edit'}, {'fieldName': 'StorageLocation', 'mode': 'edit'}, {'fieldName': 'ClientOrderNumber', 'mode': 'edit'}, {'fieldName': 'ClientReference', 'mode': 'edit'}, {'fieldName': 'ClientSampleID', 'mode': 'edit'}, {'fieldName': 'SamplingDeviation', 'mode': 'edit'}, {'fieldName': 'SampleCondition', 'mode': 'edit'}], [{'fieldName': 'Priority', 'mode': 'edit'}, {'fieldName': 'Composite', 'mode': 'edit'}, {'fieldName': 'InvoiceExclude', 'mode': 'edit'}, {'html': '', 'fieldName': 'Invoice', 'mode': 'structure'}, {'html': u'05.08.2020 21:43', 'fieldName': 'DateReceived', 'mode': 'structure'}, {'fieldName': 'DatePublished', 'mode': 'view'}, {'fieldName': 'MemberDiscount', 'mode': 'edit'}, {'fieldName': 'InternalUse', 'mode': 'edit'}]])
               sublists: [[{'fieldName': 'Batch', 'mode': 'edit'}, {'fieldName': 'SubGroup', 'mode': 'edit'}, {'html': '', 'fieldName': 'Template', 'mode': 'structure'}, {'html': "<div id='analysisprofile-2' class='field reference'>  <a class='link' uid='0025f8089d8b4a9f8090fd99cbbcab33' href='http://localhost:8080/senaite/bika_setup/bika_analysisprofiles/analysisprofile-2'>    Total Hardness  </a></div>", 'fieldName': 'Profiles', 'mode': 'structure'}, {'html': "<div id='sampletype-6' class='field reference'>  <a class='link' uid='d9b64b7f240f4911bc6ea91f1a6c4cbe' href='http://localhost:8080/senaite/bika_setup/bika_sampletypes/sampletype-6'>    Water  </a></div>", 'fieldName': 'SampleType', 'mode': 'structure'}, {'fieldName': 'Container', 'mode': 'edit'}, {'fieldName': 'Preservation', 'mode': 'edit'}, {'fieldName': 'Specification', 'mode': 'edit'}], [{'fieldName': 'PublicationSpecification', 'mode': 'edit'}, {'fieldName': 'SamplePoint', 'mode': 'edit'}, {'fieldName': 'StorageLocation', 'mode': 'edit'}, {'fieldName': 'ClientOrderNumber', 'mode': 'edit'}, {'fieldName': 'ClientReference', 'mode': 'edit'}, {'fieldName': 'ClientSampleID', 'mode': 'edit'}, {'fieldName': 'SamplingDeviation', 'mode': 'edit'}, {'fieldName': 'SampleCondition', 'mode': 'edit'}], [{'fieldName': 'Priority', 'mode': 'edit'}, {'fieldName': 'Composite', 'mode': 'edit'}, {'fieldName': 'InvoiceExclude', 'mode': 'edit'}, {'html': '', 'fieldName': 'Invoice', 'mode': 'structure'}, {'html': u'05.08.2020 21:43', 'fieldName': 'DateReceived', 'mode': 'structure'}, {'fieldName': 'DatePublished', 'mode': 'view'}, {'fieldName': 'MemberDiscount', 'mode': 'edit'}, {'fieldName': 'InternalUse', 'mode': 'edit'}]]
               root_sample: False
               default: <DEFAULT>
               modules: <Products.PageTemplates.ZRPythonExpr._SecureModuleImporter object at 0x1059efe90>
               mode: 'edit'
               context: <AnalysisRequest at /senaite/clients/client-1/H2O-0001>
               action: {'fieldName': 'Contact', 'mode': 'edit'}
               view: <bika.lims.browser.header_table.HeaderTableView object at 0x10ef48190>
               root: <Application at >
               options: {}
               target_language: None

Any hints where this test method was defined?

Also a hint where to find the syntax changes of the page templates would be great, I can't find the code changes at the moment.

Thanks a lot
Ramon

Another code change that we encountered is that start is not callable anymore:

class python:repeat['fieldset'].start() and 'nav-link active' or 'nav-link'"

had to be changed to:

class python:repeat['fieldset'].start and 'nav-link active' or 'nav-link'"

I know that I have the following working in Plone 5.2 for theme fragments (but they are also templates (?) )

tal:define="value view/getDefaultAddCount|None"

That said: in newer Plone you usually dont need to use attributes anymore, since you can do

 <span value="${view/getDefaultAddCount|None}">

… not sure if that would make any difference, though.

test was a very old wrapper around a issue with a and b or c. You should use b if a else c instead.

Hi Philip, thanks for your reply.
Can you remember where this function was defined? Or was it a skin script Python? Couldn't find the code yet and I want to avoid overriding Products/Archetypes/skins/archetypes/widgets/field.pt.
Thanks again, Ramon

The function test is defined in RestrictedPython.Utilities.test and injected in DocumentTemplate.DT_Util.

The docs say:

  - A special function, 'test', that supports if-then expressions.
    The 'test' function accepts any number of arguments.  If the
    first argument is true, then the second argument is returned,
    otherwise if the third argument is true, then the fourth
    argument is returned, and so on.  If there is an odd number of
    arguments, then the last argument is returned in the case that
    none of the tested arguments is true, otherwise None is
    returned.

See https://github.com/zopefoundation/DocumentTemplate/blob/master/src/DocumentTemplate/DT_Util.py#L340

1 Like

When you say "The function test is defined.." - does test still work?

I thought several magic functions/imports were removed while migration from Zope 2 to 4? AFAIR @icemac told me test does no longer work and needs to be removed.

Also see https://github.com/zopefoundation/Zope/issues/813

Thanks Philip, Jürgen for your answers.

I actually found the function in RestrictedPython.Utilities.test as well, but was wondering why it works in Plone 5.2.1 but not in 5.2.2 anymore...

5.2.1 uses Zope 4.1.3 whereas 5.2.2 uses Zope 4.5.1. I think that the template engine for skin templates has switched to chameleon. The changelogs of Plone and Zope (https://plone.org/download/releases/5.2.2 and https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst) mention changes regarding the code that decides which engine is used. See also
https://github.com/plone/Products.CMFPlone/issues/3141 and https://github.com/plone/Products.CMFPlone/pull/3149

This is why I doubted if we should stick to Zope 4.3 instead (but I got at least one test failure when I tried downgrading to that). Zope 4.4. has template engine changes, and Zope 4.5 did extra fixes for that. The links from Philip are helpful there. Also the changelog of chameleon can help.

Also, it all started with this Zope issue about a design weakness in Products.PageTemplates, where an engine argument was mostly ignored.

Most templates should work with no change, especially after the fixes in Zope 4.5, but we are encountering a few bugs.

You mention this failing:

<span tal:attributes="value view/getDefaultAddCount|1"/>

Until recently I did not know that this could work at all. Best for compatibility would be:

<span tal:attributes="value view/getDefaultAddCount|python:1"/>

You saw this fail in tal statements:

python:repeat['fieldset'].start() and 'a' or 'b'

Best for compatibility with slightly differing template engines, is to split it like this:

start repeat/fieldset/start;
python:start and 'a' or 'b'

Same for index and probably others:

index repeat/fieldset/index;
python:index and 'a' or 'b'

Related PRs that fix some instances of this code:

For some 'light' background reading:

I think somewhere in there is a list of which variables are defined. There were differences between what the documentation said, and what was really happening in Products.PageTemplates, zope.pagetemplate and chameleon. It should all be more alike now, but there are a few cases left after all that need polishing, sadly.

Thank you everybody for your answers.

Indeed the problems came from the changed engine in Zope 4.5.1,
but it seems only come when I try to render a widget directly in a template, e.g.:

 <metal:field use-macro="python:context.widget(fieldName, mode=mode)"/>

In other places id do not get the NameError: name 'test' is not defined.

I got it working by defining test in the page template where I use the macros:

    <metal:content fill-slot="main">
      <div tal:define="test nocall:view/test;
                       errors python:{};">
        <metal:field use-macro="python:context.widget('Contact', mode='edit')"></metal:field>
      </div>
    </metal:content>

However, I'm wondering where this got injected before in the Python expression.

I have a lot of templates with syntax like

tal:condition="view/get_something|view/get_somehtingelse|None"

Does not look like this works anymore, also tried suggested syntaxes, like

view/get_something|view/get_somehtingelse|python: None

python: view.get_something or view.get_somehtingelse

Looks like the workaround is to add another view/checkstuff which check for something and somethingelse.

python: view.get_something or view.get_somehtingelse

In this casse you have to be aware that you should call the method: python: view.get_something() or view.get_somethingelse() .

Plain TALES expressions do this automatically for you (try both) so then the distinction if it's an attribute or a method on an object doesn't matter. with python: you need to be precise. :slight_smile:

Thanks, my bad…

I ran into another problem with page templates and Plone 5.2.2.

It seems there are problems with nested curly braces and chameleon expressions. This could easily happen if you try to build a data-attribute with some JSON data in a page template.

Steps to reproduce:

<span data-test='{"foo": "${view/somemethod}"}'/>

-> does not work. Throws "zope.location.interfaces.LocationError .. (view object, 'somemethod)'"

<span data-test='"foo": "${view/somemethod}"'/>

-> works fine (but it's not valid JSON)

<span data-test='{"foo": "${python: view.somemethod()}"'/>

-> works fine

The issues does not exist in Plone 5.2.0.

I also think

<section id="section-${view/id}"

stopped working, so you need (something like)

<section id="section-${python: view.id}"

I tried this and can confirm it. I have copied your message to an issue:

I cannot confirm this. I tried it in two page templates, and got an error with both spellings. So apparently this particular view really has no id. I tried with Products.CMFPlone/browser/templates/search.pt.

1 Like

Just for the record: I checked one of mine, and it returns

<section id="section-fe238730bd74c3080c30fc3ee3ca8a3"

Probably OT here, but by the way:
The python: variant is much faster and I recommend to use it almost everywhere. TALES triggers traversal, which is expensive. So do yourself, the user, the power-consumption/environment a favour and use TALES only for real traversal tasks and otherwise Python-expressions.

5 Likes