From 21fafe04d468568f7fcac6c007ad112a01fef714 Mon Sep 17 00:00:00 2001 From: flipmcf Date: Sat, 15 Jan 2022 13:08:04 -0500 Subject: [PATCH 01/14] #1138 starting classic-ui page templates --- .gitignore | 1 + docs/classic-ui/README.md | 2 +- docs/classic-ui/page_templates/dtml.md | 7 +++++++ docs/classic-ui/page_templates/index.md | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 docs/classic-ui/page_templates/dtml.md create mode 100644 docs/classic-ui/page_templates/index.md diff --git a/.gitignore b/.gitignore index b2d6de306..bbc187b19 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ .env.development.local .env.test.local .env.production.local +.idea npm-debug.log* yarn-debug.log* diff --git a/docs/classic-ui/README.md b/docs/classic-ui/README.md index e8f19514e..19e352d31 100644 --- a/docs/classic-ui/README.md +++ b/docs/classic-ui/README.md @@ -13,7 +13,7 @@ See the following example ├── index.md └── install.md ``` -Use a file called `index.md` for creating a index (if needed) +Use a file called `index.md` for creating an index (if needed) ```shell ➜ cat index.md diff --git a/docs/classic-ui/page_templates/dtml.md b/docs/classic-ui/page_templates/dtml.md new file mode 100644 index 000000000..4162c246f --- /dev/null +++ b/docs/classic-ui/page_templates/dtml.md @@ -0,0 +1,7 @@ +# DTML + +DTML technology has been phased out many years ago. + +Do not use it. + +If you see it, report it as a bug. \ No newline at end of file diff --git a/docs/classic-ui/page_templates/index.md b/docs/classic-ui/page_templates/index.md new file mode 100644 index 000000000..6e887af8e --- /dev/null +++ b/docs/classic-ui/page_templates/index.md @@ -0,0 +1,14 @@ +# Page Templates in Plone Classic-UI + +These instructions and resources will help you develop Classic-UI pages. + +# Contents + +* Chameleon Page Templates + * Introduction + * bla + * bla + * bla +* Skin Layers +* [DTML](dtml.md) + From d6927ceb26452314acdc9a479ae345ea5d4065a4 Mon Sep 17 00:00:00 2001 From: flipmcf Date: Sat, 15 Jan 2022 13:18:12 -0500 Subject: [PATCH 02/14] Styleguide changes --- docs/classic-ui/{page_templates => page-templates}/dtml.md | 0 docs/classic-ui/{page_templates => page-templates}/index.md | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename docs/classic-ui/{page_templates => page-templates}/dtml.md (100%) rename docs/classic-ui/{page_templates => page-templates}/index.md (86%) diff --git a/docs/classic-ui/page_templates/dtml.md b/docs/classic-ui/page-templates/dtml.md similarity index 100% rename from docs/classic-ui/page_templates/dtml.md rename to docs/classic-ui/page-templates/dtml.md diff --git a/docs/classic-ui/page_templates/index.md b/docs/classic-ui/page-templates/index.md similarity index 86% rename from docs/classic-ui/page_templates/index.md rename to docs/classic-ui/page-templates/index.md index 6e887af8e..63206d51e 100644 --- a/docs/classic-ui/page_templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -6,9 +6,9 @@ These instructions and resources will help you develop Classic-UI pages. * Chameleon Page Templates * Introduction - * bla - * bla - * bla + * blah + * blah + * blah * Skin Layers * [DTML](dtml.md) From e6070d437371bd599e660392d16f1a885c4626e5 Mon Sep 17 00:00:00 2001 From: flipmcf Date: Sat, 15 Jan 2022 18:53:19 -0500 Subject: [PATCH 03/14] Saving what I got so far... still working it out in my head --- docs/classic-ui/page-templates/index.md | 186 +++++++++++++++++++++++- 1 file changed, 184 insertions(+), 2 deletions(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index 63206d51e..437c768c6 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -2,13 +2,195 @@ These instructions and resources will help you develop Classic-UI pages. +When you visit the Plone backend with a web browser, you will reach the Classic-UI. These pages are generated with Classic Plone Browser Views and Page Templates. + +This chapter covers developing Page Templates. Browser views are documented in-depth in (another chapter) # Contents -* Chameleon Page Templates - * Introduction +* Page Templates + * Introduction + * Overriding existing templates + * Creating your own templates +* Language Reference + * Template Expression Language (TAL) + * Expressions (TALES) + * Python expressions + * Path Expressions + * Macros (METAL) + * ${..} Operator + * + +* Chameleon + * blah * blah * blah * Skin Layers * [DTML](dtml.md) + +## Page Templates +### Introduction +Plone uses Zope Page Templates. These templates use well established and time-tested standards: The *Template Attribute Language* (TAL), *TAL Expression Syntax* (TALES), *Macro Expansion TAL* (METAL). + +From a python programmer's point of view, Page Templates are a tool to display a python object on the web, instead of simply what `__repr__()` shows. These files always have a `.pt` file extension + +A normal full Plone HTML page consists of: +* the *master template* defining the overall layout of the page +* *slots* defined by the master template, and filled by the object being published. +* *viewlets* and *viewlet managers* +* *tiles* + +Page templates are most frequently describing an HTML document, but they can also render XML (useful for RSS feeds or XML-based API's). + +Templates can actually render any mime type you find useful: markdown `.md` files, or latex, or `text/plain` + +You could, if you wanted, create a Page Template that rendered `application/json` but there are better ways to do that directly from a browser view. + +The point here is that you are not limited to templates creating only HTML. + + +### Overriding Existing Templates + +Your first journey in page templates may be overriding a Plone Core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. + +This document is meant to simply describe the development of templates. + +* `Create your own add-on`_ + which you can use to contain your page templates on the file system. + +* Use the `z3c.jbot`_ Plone helper add-on to override existing page + templates. + This is provided in the `sane_plone_addon_template`_ add-in, no separate + set-up needed. + +* `z3c.jbot`_ can override page templates (``.pt`` files) for views, + viewlets, old style page templates and portlets. + In fact it can override any ``.pt`` file in the Plone source tree. + + +### Creating your own Templates + +!!! admonition 'note' + + Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. + + + +### Language Reference + + + +## Chameleon +Chameleon is an HTML/XML template engine for Python. + +It’s designed to generate the document output of a web application, typically HTML markup or XML. + +The complete documentation for Chameleon can be found at [https://chameleon.readthedocs.io/](https://chameleon.readthedocs.io/) + +The language used is *page templates*, originally a Zope invention[^1] + +The template engine compiles templates into Python byte-code and is optimized for speed. For a complex template language, the performance is very good. + +The *page templates* language is used within your document structure as special element attributes and text markup. Using a set of simple language constructs, you control the document flow, element repetition, text replacement and translation. + +The basic language (known as the *template attribute language* or TAL) +is simple enough to grasp from an example: + +```xml+genshi + + +

Hello, ${'world'}!

+ + + + +
+ ${row.capitalize()} ${col} +
+ + +``` + +The ``${...}`` notation is short-hand for text insertion [3]_. The +Python-expression inside the braces is evaluated and the result +included in the output. By default, the string is escaped before +insertion. To avoid this, use the ``structure:`` prefix: + +```xml+genshi +
${structure: ...}
+``` + +Note that if the expression result is an object that implements an +``__html__()`` method [4]_, this method will be called and the result +treated as "structure". An example of such an object is the +``Markup`` class that's included as a utility + +```python + from chameleon.utils import Markup + username = Markup("%s" % username) +``` + +The macro language (known as the *macro expansion language* or METAL) +provides a means of filling in portions of a generic template. + +On the left, the macro template; on the right, a template that loads +and uses the macro, filling in the "content" slot: + +```xml+genshi + + +

${structure: document.body}

+ Example — ${document.title} + + +

${document.title}

+ +
+ +
+ + +``` + +In the example, the expression type [load](load reference) is +used to retrieve a template from the file system using a path relative +to the calling template. + +The METAL system works with TAL such that you can for instance fill in +a slot that appears in a ``tal:repeat`` loop, or refer to variables +defined using ``tal:define``. + +The third language subset is the translation system (known as the +*internationalization language* or I18N): + +```xml+genshi + + + + ... + +
+ You have ${round(amount, 2)} dollars in your account. +
+ + ... + + +``` + +Each translation message is marked up using ``i18n:translate`` and +values can be mapped using ``i18n:name``. Attributes are marked for +translation using ``i18n:attributes``. The template engine generates +`gettext `_ translation strings from +the markup:: + + "You have ${amount} dollars in your account." + +If you use a web framework such as `Pyramid `_, the +translation system is set up automatically and will negotiate on a *target +language* based on the HTTP request or other parameter. If not, then +you need to configure this manually. + + +[^1]: The template language specifications and API for the Page Templates engine are based on Zope Page Templates (see in particular zope.pagetemplate). However, the Chameleon compiler and Page Templates engine is an entirely new codebase, packaged as a standalone distribution. It does not require a Zope software environment. From 14fde61349ce20352473efc94e0edcebf4d409a4 Mon Sep 17 00:00:00 2001 From: Maik Derstappen Date: Wed, 13 Apr 2022 09:11:07 +0300 Subject: [PATCH 04/14] Update docs/classic-ui/page-templates/index.md Co-authored-by: Steve Piercy --- docs/classic-ui/page-templates/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index 437c768c6..51875729a 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -1,4 +1,4 @@ -# Page Templates in Plone Classic-UI +# Page Templates in Plone Classic UI These instructions and resources will help you develop Classic-UI pages. From 3161fe8ee9de1a29be183b62838ad2b1650cb663 Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 10:19:25 -0500 Subject: [PATCH 05/14] restore .gitignore --- .gitignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8bf8f7638 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Dependencies +/bin +/include +/lib +/lib64 + +# Generated files +pyvenv.cfg +/_build + +# symlinked from submodule +docs/volto +docs/plone.restapi +docs/plone.api \ No newline at end of file From 80019b3b7944f70ee144385429593c19d3451ca3 Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 10:24:07 -0500 Subject: [PATCH 06/14] full name of acronym DTML --- docs/classic-ui/page-templates/dtml.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/classic-ui/page-templates/dtml.md b/docs/classic-ui/page-templates/dtml.md index 4162c246f..550fd77c2 100644 --- a/docs/classic-ui/page-templates/dtml.md +++ b/docs/classic-ui/page-templates/dtml.md @@ -1,6 +1,6 @@ # DTML -DTML technology has been phased out many years ago. +Document Template Markup Language (DTML) technology has been phased out many years ago. Do not use it. From e390751ee32d0f6fb0f6679aa1ae56993bae95ca Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 10:43:35 -0500 Subject: [PATCH 07/14] Fix capitalization and assorted cleanup --- docs/classic-ui/page-templates/index.md | 35 ++++++++++++------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index 51875729a..fbf8a5d3a 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -1,10 +1,10 @@ # Page Templates in Plone Classic UI -These instructions and resources will help you develop Classic-UI pages. +These instructions and resources will help you develop classic-ui pages. -When you visit the Plone backend with a web browser, you will reach the Classic-UI. These pages are generated with Classic Plone Browser Views and Page Templates. +When you visit the Plone backend with a web browser, you will reach the classic-ui. These pages are generated with classic Plone browser views and page templates. -This chapter covers developing Page Templates. Browser views are documented in-depth in (another chapter) +This chapter covers developing page templates. Browser views are documented in-depth in [views](../views.md) # Contents * Page Templates @@ -21,7 +21,6 @@ This chapter covers developing Page Templates. Browser views are documented in * * Chameleon - * blah * blah * blah @@ -31,13 +30,13 @@ This chapter covers developing Page Templates. Browser views are documented in ## Page Templates ### Introduction -Plone uses Zope Page Templates. These templates use well established and time-tested standards: The *Template Attribute Language* (TAL), *TAL Expression Syntax* (TALES), *Macro Expansion TAL* (METAL). +Plone Classic uses Zope Page Templates (ZPT or just PT). These templates use well established and time-tested standards: The *Template Attribute Language* (TAL), *TAL Expression Syntax* (TALES), *Macro Expansion TAL* (METAL). -From a python programmer's point of view, Page Templates are a tool to display a python object on the web, instead of simply what `__repr__()` shows. These files always have a `.pt` file extension +From a python programmer's point of view, page templates are a tool to display a python object on the web, instead of simply what that object's `__repr__()` method would return. These template files always have a `.pt` file extension A normal full Plone HTML page consists of: * the *master template* defining the overall layout of the page -* *slots* defined by the master template, and filled by the object being published. +* METAL *slots* defined by the master template, and filled by the object being published. * *viewlets* and *viewlet managers* * *tiles* @@ -45,28 +44,28 @@ Page templates are most frequently describing an HTML document, but they can als Templates can actually render any mime type you find useful: markdown `.md` files, or latex, or `text/plain` -You could, if you wanted, create a Page Template that rendered `application/json` but there are better ways to do that directly from a browser view. +You could, if you wanted, create a page template that rendered `application/json` but there are better ways to do that directly from a browser view. The point here is that you are not limited to templates creating only HTML. ### Overriding Existing Templates -Your first journey in page templates may be overriding a Plone Core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. +Your first journey in page templates may be overriding a Plone backend core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. This document is meant to simply describe the development of templates. -* `Create your own add-on`_ +* `Create your own add-on` which you can use to contain your page templates on the file system. -* Use the `z3c.jbot`_ Plone helper add-on to override existing page +* Use the `z3c.jbot` Plone helper add-on to override existing page templates. - This is provided in the `sane_plone_addon_template`_ add-in, no separate + This is provided in the `sane_plone_addon_template` add-in, no separate set-up needed. -* `z3c.jbot`_ can override page templates (``.pt`` files) for views, +* `z3c.jbot` can override page templates (``.pt`` files) for views, viewlets, old style page templates and portlets. - In fact it can override any ``.pt`` file in the Plone source tree. + In fact, it can override any ``.pt`` file in the Plone source tree. ### Creating your own Templates @@ -88,7 +87,7 @@ It’s designed to generate the document output of a web application, typically The complete documentation for Chameleon can be found at [https://chameleon.readthedocs.io/](https://chameleon.readthedocs.io/) -The language used is *page templates*, originally a Zope invention[^1] +The language used is *page templates*, originally a Zope invention[^1]. The template engine compiles templates into Python byte-code and is optimized for speed. For a complex template language, the performance is very good. @@ -112,7 +111,7 @@ is simple enough to grasp from an example: ``` -The ``${...}`` notation is short-hand for text insertion [3]_. The +The ``${...}`` notation is shorthand for text insertion. The Python-expression inside the braces is evaluated and the result included in the output. By default, the string is escaped before insertion. To avoid this, use the ``structure:`` prefix: @@ -182,12 +181,12 @@ The third language subset is the translation system (known as the Each translation message is marked up using ``i18n:translate`` and values can be mapped using ``i18n:name``. Attributes are marked for translation using ``i18n:attributes``. The template engine generates -`gettext `_ translation strings from +`gettext ` translation strings from the markup:: "You have ${amount} dollars in your account." -If you use a web framework such as `Pyramid `_, the +If you use a web framework such as `Pyramid `, the translation system is set up automatically and will negotiate on a *target language* based on the HTTP request or other parameter. If not, then you need to configure this manually. From 003694204e56f2e4fd27db98596dff56a4facccd Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 10:47:28 -0500 Subject: [PATCH 08/14] line breaks around headings --- docs/classic-ui/page-templates/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index fbf8a5d3a..09a56a8b6 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -5,7 +5,9 @@ These instructions and resources will help you develop classic-ui pages. When you visit the Plone backend with a web browser, you will reach the classic-ui. These pages are generated with classic Plone browser views and page templates. This chapter covers developing page templates. Browser views are documented in-depth in [views](../views.md) -# Contents + + +## Contents * Page Templates * Introduction @@ -30,6 +32,7 @@ This chapter covers developing page templates. Browser views are documented in ## Page Templates ### Introduction + Plone Classic uses Zope Page Templates (ZPT or just PT). These templates use well established and time-tested standards: The *Template Attribute Language* (TAL), *TAL Expression Syntax* (TALES), *Macro Expansion TAL* (METAL). From a python programmer's point of view, page templates are a tool to display a python object on the web, instead of simply what that object's `__repr__()` method would return. These template files always have a `.pt` file extension @@ -75,12 +78,9 @@ This document is meant to simply describe the development of templates. Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. - ### Language Reference - - - ## Chameleon + Chameleon is an HTML/XML template engine for Python. It’s designed to generate the document output of a web application, typically HTML markup or XML. From 5c807137650a10bf8a0a6149fba70ff8c8ffec8d Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 12:29:35 -0500 Subject: [PATCH 09/14] filling stuff in and rethinking TOC --- docs/classic-ui/page-templates/index.md | 56 +++++++++++++++++-------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index 09a56a8b6..c9ffc3083 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -12,7 +12,15 @@ This chapter covers developing page templates. Browser views are documented in * Page Templates * Introduction * Overriding existing templates + * z3c.jbot + * overriding with zcml + * layering with zcml + * editing browser views * Creating your own templates + * stand-alone templates + * browser view templates + * Advanced Concepts + * calling templates in python * Language Reference * Template Expression Language (TAL) * Expressions (TALES) @@ -22,10 +30,6 @@ This chapter covers developing page templates. Browser views are documented in * ${..} Operator * -* Chameleon - * blah - * blah - * blah * Skin Layers * [DTML](dtml.md) @@ -54,29 +58,47 @@ The point here is that you are not limited to templates creating only HTML. ### Overriding Existing Templates -Your first journey in page templates may be overriding a Plone backend core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. +Your first journey in page templates will likely be overriding a Plone backend core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. -This document is meant to simply describe the development of templates. -* `Create your own add-on` - which you can use to contain your page templates on the file system. +#### z3c.jbot -* Use the `z3c.jbot` Plone helper add-on to override existing page - templates. - This is provided in the `sane_plone_addon_template` add-in, no separate - set-up needed. -* `z3c.jbot` can override page templates (``.pt`` files) for views, - viewlets, old style page templates and portlets. - In fact, it can override any ``.pt`` file in the Plone source tree. +`z3c.jbot` (https://pypi.org/project/z3c.jbot/ ) can override page templates (``.pt`` files) for views, viewlets, old style page templates and portlets. In fact, it can override any ``.pt`` file in the Plone source tree. + +Example: + +existing template.... + +copy it to your project (or your addon) and name it with a special name + +Edit it. + +Restart and done! + +#### overriding with zcml + +#### layering with zcml + +#### editing browser views ### Creating your own Templates -!!! admonition 'note' +#### stand-alone templates + +```{warning} +Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. +``` + +#### browser view templates + + +### Advanced Concepts - Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. +#### calling templates in python +#### passing data to templates ### Language Reference ## Chameleon From 2b35da991a42d3504b2752074ea981bc2d0dc54b Mon Sep 17 00:00:00 2001 From: flipmcf Date: Mon, 28 Nov 2022 17:57:04 -0500 Subject: [PATCH 10/14] rambling about templates --- docs/classic-ui/page-templates/index.md | 111 ++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index c9ffc3083..736687bab 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -66,31 +66,132 @@ Your first journey in page templates will likely be overriding a Plone backend c `z3c.jbot` (https://pypi.org/project/z3c.jbot/ ) can override page templates (``.pt`` files) for views, viewlets, old style page templates and portlets. In fact, it can override any ``.pt`` file in the Plone source tree. -Example: +To override a particular file, first determine its canonical filename. It’s defined as the path relative to the package within which the file is located; directory separators are replaced with dots. -existing template.... +As a simple example, to edit the default search page in Plone Classic we first find the existing template. In a fresh plone install, it's in the Products.CMFPlone `/Products/CMFPlone/browser/templates/search.pt` so our override file will be called `Products.CMFPlone.browser.templates.search.pt` -copy it to your project (or your addon) and name it with a special name +Create a new directory inside your local plone install for template overrides and copy the system `search.pt` into that directory, and rename it to `Products.CMFPlone.browser.templates.search.pt` -Edit it. +``` +├─ PloneSite +│ ├─ bin +│ ├─ eggs +│ │ └─Products.CMFPlone-version +│ │ └Products +│ │ └CMFPlone +│ │ └browser +│ │ └templates +│ │ └search.pt <--- override this +│ ├─ parts +│ └─ mysite + ├─ configure.zcml + └─ template-overrides + └─ Products.CMFPlone.browser.templates.search.pt <--- with this +``` + +In your configure.zcml, register the `template-overrides` directory with jbot + +```xml+genshi + -Restart and done! + +``` + +Now, edit your `Products.CMFPlone.browser.templates.search.pt` and see your changes on the plone search page. + +```{note} + The `` zcml should also contain a `layer` attribute for best practice. see the z3c.jbot documentation and browser layers documentation +``` #### overriding with zcml +Although not best practice, you can also quickly override templates by creating an `override.zcml` and adding your custom registration. For the search page, this would be finding the ZCML entry for the search view in `Products/CMFPlone/browser/configure.zcml` and copying it into your own `overrides.zcml`, but change the `template=` attribute to point to your custom template. The `class` also must change to become an absolute path to the original class path + +overrides.zcml +```xml+genshi + +``` + #### layering with zcml +A better and much more flexable way to override templates, especially when developing add-ons, is to use a browser layer in your configure.zcml. This is *highly* preferred to using an overrides.zcml file, but involves using a BrowserLayer Interface. This is extremely easy and is best practice. + +configure.zcml +```xml+genshi + +``` + #### editing browser views +Sometimes, when you look up a browser view's ZCML it does not have a `template` attribute. In this case, the template is frequently hard-coded into the python browser view. One example of this is in the control panel pages, where the templates are not added to `configure.zcml` + +`@@actions-controlpanel` is found in `Products/CMFPlone/controlpanel/browser/actions.py` + +```python +from Products.Five import BrowserView +from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile + +class ActionListControlPanel(BrowserView): + """Control panel for the portal actions.""" + + template = ViewPageTemplateFile("actions.pt") +``` + +These are harder to override. Best practice is to copy the python file (`actions.py` in this case) to your customizations folder and override the entire browser:page like above, pointing to your own `actions.py` and modifying that python code to find your new template `.pt` file + ### Creating your own Templates +If you create your own dexterity object, you probably want to display it on a web browser. Without a template, a default view will come up using plone's built-in main template and default view for a content type. + + #### stand-alone templates ```{warning} Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. ``` +The simplest possible template is an html document: + +template.pt +```html + + +

hello custom template

+ + +``` + +and registering it in ZCML: + +```xml+genshi + +``` + +if you now go to any content item on your plone site, including the site itself, and add `custom_template` to the end of the url, the above html will display. + +This isn't very useful, but does show how Plone basically works. + #### browser view templates From adb05717cbe74b2a2abcb8fee0062a59d8f35a8e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 28 Apr 2024 15:39:58 -0700 Subject: [PATCH 11/14] Update link to Chameleon docs and markup. --- docs/classic-ui/page-templates/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md index 736687bab..16f216207 100644 --- a/docs/classic-ui/page-templates/index.md +++ b/docs/classic-ui/page-templates/index.md @@ -208,7 +208,9 @@ Chameleon is an HTML/XML template engine for Python. It’s designed to generate the document output of a web application, typically HTML markup or XML. -The complete documentation for Chameleon can be found at [https://chameleon.readthedocs.io/](https://chameleon.readthedocs.io/) +```{seealso} +[Chameleon documentation](https://chameleon.readthedocs.io/en/latest/) +``` The language used is *page templates*, originally a Zope invention[^1]. From 3378db36cf51a043b0fe4c3e71e8c4898ae2e3c3 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 7 Jul 2024 21:01:27 -0700 Subject: [PATCH 12/14] Remove obsolete file --- docs/classic-ui/page-templates/dtml.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 docs/classic-ui/page-templates/dtml.md diff --git a/docs/classic-ui/page-templates/dtml.md b/docs/classic-ui/page-templates/dtml.md deleted file mode 100644 index 550fd77c2..000000000 --- a/docs/classic-ui/page-templates/dtml.md +++ /dev/null @@ -1,7 +0,0 @@ -# DTML - -Document Template Markup Language (DTML) technology has been phased out many years ago. - -Do not use it. - -If you see it, report it as a bug. \ No newline at end of file From f3dca39172c68f931da703e4691c66dd900ca013 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 8 Jul 2024 02:57:51 -0700 Subject: [PATCH 13/14] Complete review of initial document. --- docs/classic-ui/index.md | 1 + docs/classic-ui/page-templates.md | 342 ++++++++++++++++++++++++ docs/classic-ui/page-templates/index.md | 320 ---------------------- docs/glossary.md | 16 +- 4 files changed, 357 insertions(+), 322 deletions(-) create mode 100644 docs/classic-ui/page-templates.md delete mode 100644 docs/classic-ui/page-templates/index.md diff --git a/docs/classic-ui/index.md b/docs/classic-ui/index.md index 8fd0f5a9c..25f8019c7 100644 --- a/docs/classic-ui/index.md +++ b/docs/classic-ui/index.md @@ -55,6 +55,7 @@ images layers module-federation mockup +page-templates portlets recipes static-resources diff --git a/docs/classic-ui/page-templates.md b/docs/classic-ui/page-templates.md new file mode 100644 index 000000000..099a3f979 --- /dev/null +++ b/docs/classic-ui/page-templates.md @@ -0,0 +1,342 @@ +--- +myst: + html_meta: + "description": "Page templates in Plone Classic UI" + "property=og:description": "Page templates in Plone Classic UI" + "property=og:title": "Page templates in Plone Classic UI" + "keywords": "Plone 6, Classic UI, page, templates, Twitter Bootstrap 5" +--- + +(classic-ui-page-templates-index-label)= + +# Page templates + +This chapter provides an tutorial of page templates in Classic UI. + + +## Background + +When you visit the Plone backend with a web browser, you will view the Classic UI. +These pages are generated with classic Plone browser views and page templates. + +Browser views are documented in depth in {doc}`views`. + +Plone Classic UI uses {term}`Zope Page Template`s (ZPT). +These templates use well established and time-tested standards, {term}`Template Attribute Language` (TAL), {term}`Template Attribute Language Expression Syntax` (TALES), and {term}`Macro Expansion Template Attribute Language` (METAL). + +From a Python programmer's point of view, page templates are a tool to display a Python object on the web, instead of what that object's `__repr__()` method would return. +These template files always have a file extension of `.pt`. + +A normal full Plone HTML page consists of the following. + +- the _master template_ defining the overall layout of the page +- METAL _slots_ defined by the master template, and filled by the object being published +- _viewlets_ and _viewlet managers_ +- _tiles_ + +Page templates most frequently describe an HTML document. +They can also render XML, which is useful for RSS feeds or XML-based APIs. + +Templates can render any media type you find useful, including Markdown, LaTeX, or plain text. +You could, if you wanted, create a page template that rendered a media type of `application/json`, but there are better ways to do that directly from a browser view. +The point here is that you are not limited to templates that only create HTML. + + +## Override existing templates + +Your first journey in page templates could be to override a Plone backend core template or a template from an add-on. +The recommended approach is to use [`z3c.jbot`](https://pypi.org/project/z3c.jbot/), and to put your customized templates onto the filesystem and under a version control system. + + +### `z3c.jbot` + +`z3c.jbot` can override page templates for views, viewlets, old style page templates and portlets. +In fact, it can override any `.pt` file in the Plone source tree. + +To override a particular file, first determine its canonical filename. +It's defined as the path relative to the package within which the file is located. + +```{tip} +See Customizing Existing Templates – Mastering Plone 5 development training, {ref}`training:plone5-zpt2-finding-label`. +``` + +```{todo} +@stevepiercy spun up a clean Plone site via buildout. +The paths that @flipcmf originally mentioned do not exist in the `eggs` directory. +@stevepiercy found the source `search.pt` file, but has no clue where to put it in the project file system. +The information in the next few paragraphs must be verified by someone who knows what they're doing. +``` + +As an example, to override the default search page in Plone Classic UI, first find the existing template. +In a fresh plone install, it's at {file}`src/Products.CMFPlone/Products/CMFPlone/browser/templates/search.pt`. +You will name the file by replacing the directory separators (`/`) with dots (`.`). +Thus the override file will be called `Products.CMFPlone.browser.templates.search.pt`. + +Create a new directory inside your local Plone install for template overrides `mysite/template-overrides`. +Copy the Plone `search.pt` into that directory, and rename it to `Products.CMFPlone.browser.templates.search.pt` + +```text +├─ myproject +│ ├─ src +│ │ └─ Products.CMFPlone +│ │ └─ Products +│ │ └─ CMFPlone +│ │ └─ browser +│ │ └─ templates +│ │ └─ search.pt ← override this +│ └─ mysite +│ ├─ configure.zcml +│ └─ template-overrides +│ └─ Products.CMFPlone.browser.templates.search.pt ← with this +``` + +In your {file}`configure.zcml`, register the `template-overrides` directory with `z3c.jbot`. + +```xml + + + +``` + +Now edit `Products.CMFPlone.browser.templates.search.pt`, and see your changes on the Plone search page. + + +### Override with ZCML + +Although not best practice, you can also override templates by creating an {file}`override.zcml` and add your custom registration. +For the search page, use the ZCML entry for the search view in `Products/CMFPlone/browser/configure.zcml` and copy it into your own {file}`overrides.zcml`, but change the `template` attribute to point to your custom template. +You must also change the `class` attribute to become an absolute dotted path to the original class path. + +```xml + +``` + +### Layer with ZCML + +A better and much more flexible way to override templates, especially when developing add-ons, is to use a browser layer in your {file}`configure.zcml`. +This is _highly_ preferred to using an {file}`overrides.zcml` file, but involves using a `BrowserLayer` interface. + +```xml + +``` + + +### Edit browser views + +Sometimes when you look up a browser view's ZCML, it does not have a `template` attribute. +In this case, the template is frequently hard-coded into the Python browser view. +One example of this is in the control panel pages, where the templates are not added to {file}`configure.zcml`. +`@@actions-controlpanel` is found in `Products/CMFPlone/controlpanel/browser/actions.py`. + +```python +from Products.Five import BrowserView +from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile + +class ActionListControlPanel(BrowserView): + """Control panel for the portal actions.""" + + template = ViewPageTemplateFile("actions.pt") +``` + +These are harder to override. +Best practice is to copy the Python file ({file}`actions.py` in this case) to your customizations folder, and override the entire `browser:page` as shown above, pointing to your own {file}`actions.py` and modifying that Python code to find your new template `.pt` file. + + +## Create your own templates + +If you create your own content type object, you probably want to display it on a web browser. +Without a template, a default view will come up using Plone's built-in main template and default view for a content type. + + +### Stand-alone templates + +```{warning} +Although templates can be stand alone templates that render a Plone object directly, this is not best practice. +A combination of a view and page template is the correct implementation. +``` + +The simplest possible template is an HTML document. + +```html + + +

hello custom template

+ + +``` + +Register it in ZCML. + +```xml + +``` + +if you now go to any content item on your plone site, including the site itself, and add `custom_template` to the end of the url, the above html will display. + +This isn't very useful, but does show how Plone basically works. + +### Browser view templates + +```{todo} +Todo +``` + + +## Advanced Concepts + +```{todo} +Todo +``` + + +### Call templates in Python + +```{todo} +Todo +``` + + +### Pass data to templates + +```{todo} +Todo +``` + + +## Language reference + +```{todo} +Todo +``` + + +### Chameleon + +[Chameleon](https://chameleon.readthedocs.io/en/latest/) is an HTML/XML template engine for Python. +It's designed to generate the document output of a web application, typically HTML markup or XML. + +The language used is _page templates_, originally a Zope invention. + +```{note} +The template language specifications and API for the page templates engine are based on Zope Page Templates (see in particular `zope.pagetemplate`). +However, the Chameleon compiler and page templates engine is an entirely new code base, packaged as a stand alone distribution. +It does not require a Zope software environment. +``` + +The template engine compiles templates into Python byte code, and is optimized for speed. +For a complex template language, the performance is very good. + +The page templates language is used within your document structure as special element attributes and text markup. +Using a set of simple language constructs, you control the document flow, element repetition, text replacement, and translation. +The language is known as {term}`Template Attribute Language` or TAL. +The following snippet is a basic example. + +```xml + + +

Hello, ${'world'}!

+ + + + +
+ ${row.capitalize()} ${col} +
+ + +``` + +The `${...}` notation is shorthand for text insertion. +The Python expression inside the braces is evaluated, and the result included in the output. +By default, the string is escaped before insertion. +To avoid this, use the `structure:` prefix: + +```xml +
${structure: ...}
+``` + +Note that if the expression result is an object that implements an `__html__()` method, this method will be called and the result treated as "structure". +An example of such an object is the `Markup` class that's included as a utility. + +```python +from chameleon.utils import Markup +username = Markup("%s" % username) +``` + +The macro language—known as the {term}`Macro Expansion Template Attribute Language` (METAL)—provides a means of filling in portions of a generic template. + +The following two snippets work together. +First the macro template. + +```xml + + + Example — ${document.title} + + +

${document.title}

+
+ +
+ + +``` + +Next the template that loads and uses the macro, filling in the `content` slot. + +```xml + +

${structure: document.body}

+ +``` + +In the example, the expression type `load` is used to retrieve a template from the file system using a path relative to the calling template. + +The METAL system works with TAL such that you can, for instance, fill in a slot that appears in a `tal:repeat` loop, or refer to defined variables using `tal:define`. + +The third language subset is the translation system, known as the internationalization language or {term}`i18n`. + +```xml + + + ... + +

+ You have ${round(amount, 2)} dollars in your account. +
+ +... + + +``` + +Each translation message is marked up using `i18n:translate`, and values can be mapped using `i18n:name`. +Attributes are marked for translation using `i18n:attributes`. +The template engine generates `gettext ` translation strings from the markup. + +```text + "You have ${amount} dollars in your account." +``` + +If you use a web framework, such as `Pyramid `, the translation system is set up automatically and will negotiate on a _target language_ based on the HTTP request or other parameter. +If not, then you need to configure this manually. diff --git a/docs/classic-ui/page-templates/index.md b/docs/classic-ui/page-templates/index.md deleted file mode 100644 index 16f216207..000000000 --- a/docs/classic-ui/page-templates/index.md +++ /dev/null @@ -1,320 +0,0 @@ -# Page Templates in Plone Classic UI - -These instructions and resources will help you develop classic-ui pages. - -When you visit the Plone backend with a web browser, you will reach the classic-ui. These pages are generated with classic Plone browser views and page templates. - -This chapter covers developing page templates. Browser views are documented in-depth in [views](../views.md) - - -## Contents - -* Page Templates - * Introduction - * Overriding existing templates - * z3c.jbot - * overriding with zcml - * layering with zcml - * editing browser views - * Creating your own templates - * stand-alone templates - * browser view templates - * Advanced Concepts - * calling templates in python -* Language Reference - * Template Expression Language (TAL) - * Expressions (TALES) - * Python expressions - * Path Expressions - * Macros (METAL) - * ${..} Operator - * - -* Skin Layers -* [DTML](dtml.md) - - -## Page Templates -### Introduction - -Plone Classic uses Zope Page Templates (ZPT or just PT). These templates use well established and time-tested standards: The *Template Attribute Language* (TAL), *TAL Expression Syntax* (TALES), *Macro Expansion TAL* (METAL). - -From a python programmer's point of view, page templates are a tool to display a python object on the web, instead of simply what that object's `__repr__()` method would return. These template files always have a `.pt` file extension - -A normal full Plone HTML page consists of: -* the *master template* defining the overall layout of the page -* METAL *slots* defined by the master template, and filled by the object being published. -* *viewlets* and *viewlet managers* -* *tiles* - -Page templates are most frequently describing an HTML document, but they can also render XML (useful for RSS feeds or XML-based API's). - -Templates can actually render any mime type you find useful: markdown `.md` files, or latex, or `text/plain` - -You could, if you wanted, create a page template that rendered `application/json` but there are better ways to do that directly from a browser view. - -The point here is that you are not limited to templates creating only HTML. - - -### Overriding Existing Templates - -Your first journey in page templates will likely be overriding a Plone backend core template or a template from an add-on. The recommended approach is to use [z3c.jbot](https://pypi.org/project/z3c.jbot/) and to put your customized templates onto the filesystem and version controlled. - - -#### z3c.jbot - - -`z3c.jbot` (https://pypi.org/project/z3c.jbot/ ) can override page templates (``.pt`` files) for views, viewlets, old style page templates and portlets. In fact, it can override any ``.pt`` file in the Plone source tree. - -To override a particular file, first determine its canonical filename. It’s defined as the path relative to the package within which the file is located; directory separators are replaced with dots. - -As a simple example, to edit the default search page in Plone Classic we first find the existing template. In a fresh plone install, it's in the Products.CMFPlone `/Products/CMFPlone/browser/templates/search.pt` so our override file will be called `Products.CMFPlone.browser.templates.search.pt` - -Create a new directory inside your local plone install for template overrides and copy the system `search.pt` into that directory, and rename it to `Products.CMFPlone.browser.templates.search.pt` - -``` -├─ PloneSite -│ ├─ bin -│ ├─ eggs -│ │ └─Products.CMFPlone-version -│ │ └Products -│ │ └CMFPlone -│ │ └browser -│ │ └templates -│ │ └search.pt <--- override this -│ ├─ parts -│ └─ mysite - ├─ configure.zcml - └─ template-overrides - └─ Products.CMFPlone.browser.templates.search.pt <--- with this -``` - -In your configure.zcml, register the `template-overrides` directory with jbot - -```xml+genshi - - - -``` - -Now, edit your `Products.CMFPlone.browser.templates.search.pt` and see your changes on the plone search page. - -```{note} - The `` zcml should also contain a `layer` attribute for best practice. see the z3c.jbot documentation and browser layers documentation -``` - -#### overriding with zcml - -Although not best practice, you can also quickly override templates by creating an `override.zcml` and adding your custom registration. For the search page, this would be finding the ZCML entry for the search view in `Products/CMFPlone/browser/configure.zcml` and copying it into your own `overrides.zcml`, but change the `template=` attribute to point to your custom template. The `class` also must change to become an absolute path to the original class path - -overrides.zcml -```xml+genshi - -``` - -#### layering with zcml - -A better and much more flexable way to override templates, especially when developing add-ons, is to use a browser layer in your configure.zcml. This is *highly* preferred to using an overrides.zcml file, but involves using a BrowserLayer Interface. This is extremely easy and is best practice. - -configure.zcml -```xml+genshi - -``` - -#### editing browser views - -Sometimes, when you look up a browser view's ZCML it does not have a `template` attribute. In this case, the template is frequently hard-coded into the python browser view. One example of this is in the control panel pages, where the templates are not added to `configure.zcml` - -`@@actions-controlpanel` is found in `Products/CMFPlone/controlpanel/browser/actions.py` - -```python -from Products.Five import BrowserView -from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile - -class ActionListControlPanel(BrowserView): - """Control panel for the portal actions.""" - - template = ViewPageTemplateFile("actions.pt") -``` - -These are harder to override. Best practice is to copy the python file (`actions.py` in this case) to your customizations folder and override the entire browser:page like above, pointing to your own `actions.py` and modifying that python code to find your new template `.pt` file - - -### Creating your own Templates - -If you create your own dexterity object, you probably want to display it on a web browser. Without a template, a default view will come up using plone's built-in main template and default view for a content type. - - -#### stand-alone templates - -```{warning} -Although templates can be 'stand alone' templates that render a Plone object directly, this is not best practice. A combination of a View and Page Template is the correct implementation. -``` - -The simplest possible template is an html document: - -template.pt -```html - - -

hello custom template

- - -``` - -and registering it in ZCML: - -```xml+genshi - -``` - -if you now go to any content item on your plone site, including the site itself, and add `custom_template` to the end of the url, the above html will display. - -This isn't very useful, but does show how Plone basically works. - -#### browser view templates - - -### Advanced Concepts - -#### calling templates in python - -#### passing data to templates - -### Language Reference -## Chameleon - -Chameleon is an HTML/XML template engine for Python. - -It’s designed to generate the document output of a web application, typically HTML markup or XML. - -```{seealso} -[Chameleon documentation](https://chameleon.readthedocs.io/en/latest/) -``` - -The language used is *page templates*, originally a Zope invention[^1]. - -The template engine compiles templates into Python byte-code and is optimized for speed. For a complex template language, the performance is very good. - -The *page templates* language is used within your document structure as special element attributes and text markup. Using a set of simple language constructs, you control the document flow, element repetition, text replacement and translation. - -The basic language (known as the *template attribute language* or TAL) -is simple enough to grasp from an example: - -```xml+genshi - - -

Hello, ${'world'}!

- - - - -
- ${row.capitalize()} ${col} -
- - -``` - -The ``${...}`` notation is shorthand for text insertion. The -Python-expression inside the braces is evaluated and the result -included in the output. By default, the string is escaped before -insertion. To avoid this, use the ``structure:`` prefix: - -```xml+genshi -
${structure: ...}
-``` - -Note that if the expression result is an object that implements an -``__html__()`` method [4]_, this method will be called and the result -treated as "structure". An example of such an object is the -``Markup`` class that's included as a utility - -```python - from chameleon.utils import Markup - username = Markup("%s" % username) -``` - -The macro language (known as the *macro expansion language* or METAL) -provides a means of filling in portions of a generic template. - -On the left, the macro template; on the right, a template that loads -and uses the macro, filling in the "content" slot: - -```xml+genshi - - -

${structure: document.body}

- Example — ${document.title} - - -

${document.title}

- -
- -
- - -``` - -In the example, the expression type [load](load reference) is -used to retrieve a template from the file system using a path relative -to the calling template. - -The METAL system works with TAL such that you can for instance fill in -a slot that appears in a ``tal:repeat`` loop, or refer to variables -defined using ``tal:define``. - -The third language subset is the translation system (known as the -*internationalization language* or I18N): - -```xml+genshi - - - - ... - -
- You have ${round(amount, 2)} dollars in your account. -
- - ... - - -``` - -Each translation message is marked up using ``i18n:translate`` and -values can be mapped using ``i18n:name``. Attributes are marked for -translation using ``i18n:attributes``. The template engine generates -`gettext ` translation strings from -the markup:: - - "You have ${amount} dollars in your account." - -If you use a web framework such as `Pyramid `, the -translation system is set up automatically and will negotiate on a *target -language* based on the HTTP request or other parameter. If not, then -you need to configure this manually. - - -[^1]: The template language specifications and API for the Page Templates engine are based on Zope Page Templates (see in particular zope.pagetemplate). However, the Chameleon compiler and Page Templates engine is an entirely new codebase, packaged as a standalone distribution. It does not require a Zope software environment. diff --git a/docs/glossary.md b/docs/glossary.md index 4e161d317..f082d875a 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -136,9 +136,20 @@ ZMI The ZMI is a direct interface into the backend software stack of Plone. While it can still serve as a valuable tool for Plone specialists to fix problems or accomplish certain tasks, it is not recommended as a regular tool for Plone maintenance. +Template Attribute Language +TAL + The [Template Attribute Language](https://pagetemplates.readthedocs.io/en/latest/tal.html) (TAL) standard is an attribute language used to create dynamic templates. + It allows elements of a document to be replaced, repeated, or omitted. + +Template Attribute Language Expression Syntax TALES - TAL Expression Syntax (TALES) expression, which by default expects a path. - Python and string expressions are also allowed. + The [Template Attribute Language Expression Syntax](https://pagetemplates.readthedocs.io/en/latest/tales.html) (TALES) standard describes expressions that supply {term}`TAL` and {term}`METAL` with data. + Python and string expressions are allowed. + +Macro Expansion Template Attribute Language +METAL + The [Macro Expansion Template Attribute Language](https://pagetemplates.readthedocs.io/en/latest/metal.html) (METAL) standard is a facility for HTML/XML macro preprocessing. + It can be used in conjunction with or independently of {term}`TAL` and {term}`TALES`. XML The Extensible Markup Language. @@ -519,6 +530,7 @@ ZODB Zope [Zope](https://zope.readthedocs.io/en/latest/) is a Python-based application server for building secure and highly scalable web applications. +Zope Page Template ZPT Zope Page Template is a template language for Python. From 56c9b95946ebbf11f93de0423cd2dd1c60d15581 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 8 Jul 2024 03:00:49 -0700 Subject: [PATCH 14/14] Address comment https://github.com/plone/documentation/pull/1139#pullrequestreview-1316897486 --- docs/classic-ui/page-templates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/classic-ui/page-templates.md b/docs/classic-ui/page-templates.md index 099a3f979..2feef36c4 100644 --- a/docs/classic-ui/page-templates.md +++ b/docs/classic-ui/page-templates.md @@ -269,10 +269,10 @@ The following snippet is a basic example. The `${...}` notation is shorthand for text insertion. The Python expression inside the braces is evaluated, and the result included in the output. By default, the string is escaped before insertion. -To avoid this, use the `structure:` prefix: +To avoid this, use the `structure` keyword: ```xml -
${structure: ...}
+
``` Note that if the expression result is an object that implements an `__html__()` method, this method will be called and the result treated as "structure".