跳转到主要内容

页面、内容规则、组件和电子邮件的动态HTML生成和脚本编写

项目描述

简介

Easy Template (collective.easytemplate) 产品将简单的动态文本引入Plone。您不再需要只为几个动态页面创建完整的产品 - 最简单的事情可以直接从可视化编辑器中输入。

模板化是将简单的编程逻辑添加到文本输出的方式。此产品在Plone站点的各个部分添加或增强了对模板化的支持。

动机

Plone缺少为内容编辑器提供自定义、可扩展的模板化支持的内置支持。

用例

可能的用例包括:

  • 在页面主体中使用未过滤的HTML(<script>等)

  • 在页面上添加动态列表和表格,如新闻列表

  • 在内容规则操作中添加动态的电子邮件正文、标题和接收者

  • 在内容规则操作电子邮件中添加生成的内容

  • 向登录用户和匿名用户显示不同的文本

  • 动态创建简单的文本部件

示例

以下示例演示了在模板文档编辑模式下输入的文本如何转换为查看模式下的生成HTML片段。

您在Kupu中编写

Hello user!

Please select one course from below:

{{ list_folder("courses") }}

将产生以下输出

用户你好!

请从以下选项中选择一门课程

更多信息及示例.

安装

添加到您的buildout

eggs =
        collective.easytemplate

zcml =
        collective.easytemplate

运行Easy Template产品的附加产品安装程序。

collective.easytemplate依赖于collective.templateenginesJinja2模板引擎。

安全通知

由于collective.easytemplate默认允许在页面上输入不安全的HTML,如<script>,因此其创建仅限于具有管理员角色的用户。

运行单元测试

必须安装Python eggs Cheetah、Jinja2和Products.LinguaPlone才能运行所有单元测试。

作者

赞助

本产品的开发由London School of Marketing赞助。

关于模板引擎后端

默认情况下,使用Jinja 2模板引擎。有关语法,请参阅Jinja 2文档。这种语法非常简单的非XML语法,类似于Django模板。

简单的语法意味着对于基本用法,不需要HTML知识。您可以在HTML源代码和可视化编辑器视图中使用模板逻辑。

模板引擎可以在应用程序代码中切换。除了Jinja,Django和Cheetah引擎之外,collective.templateengines后端还支持collective.templateengines

只有在Jinja引擎上,Zope安全功能才得到支持。其他模板引擎不提供兼容的沙盒。

模板元素

Easy Template产品使得以下位置可以使用模板。

文档

使用模板文档内容类型添加可脚本化的Plone页面。

此内容项有一个名为模板的标签页,允许您调整高级模板属性

  • 显示错误:选择此选项后,错误消息将可供用户查看,无论他是管理员还是普通用户

  • 未过滤的模板:用于输入原始HTML代码的字段。此代码不会被WYSIWYG编辑或HTML过滤功能混淆或过滤。

您可以通过将/testTemplate附加到对象URL来调试模板代码,查看直接模板引擎输出。它将返回纯文本视图,显示生成的模板消耗的内容。

字段和组件

collective.easytemplate.fields.TemplatedTextField 允许您在 Kupu 中编辑模板文档代码,模板以查看模式运行。它作为您自定义内容类型的 Archetypes 的 TextField() 的替代品。

部件

使用模板端口令牌来向您的端口令牌添加脚本。模板端口令牌基于 `

在可视化编辑器中输入模板代码。目前尚不支持原始 HTML 编辑。

端口令牌有 TALES 表达式 字段,用于确定端口令牌是否可见。这允许有条件地显示和隐藏端口令牌。

这对于特殊案例很有用,如

  • 按语言显示端口令牌

  • 仅对特定用户显示端口令牌

  • 在特定时间内显示端口令牌

  • 等等。

在此处阅读更多有关如何编写表达式的信息.

电子邮件

使用模板邮件操作来根据基于出站电子邮件消息的内容规则添加脚本。

模板扩展适用于所有字段: 收件人主题消息。您可以基于上下文对象动态查找接收者的电子邮件地址 - 它不需要是固定地址。

以下是一个高级示例,说明如何定义由工作流操作触发的模板出站电子邮件。

profiles/defaul/contentrules.xml

<?xml version="1.0"?>
<contentrules>

 <rule name="email_local_coordinator_about_local_user_approval" title="Send email to LC when new LU needs approval"
    description="Send email to local coordinators that management has approved new member to their center and local coordinator actions are needed"
    enabled="True" event="Products.CMFCore.interfaces.IActionSucceededEvent"
    stop-after="False">
  <conditions>
   <condition type="plone.conditions.WorkflowTransition">
    <property name="wf_transitions">
     <element>my_workflow_transition_id_here</element>
    </property>
   </condition>
  </conditions>
  <actions>

   <action type="collective.easytemplate.actions.Mail">
    <property name="source">{{ portal.getProperty('email_from_address') }}</property>
    <property name="message">
New local user {{ title }} needs approval.

Please approve local user at

{{ context.absolute_url() }}

    </property>
    <property name="recipients">{{ context.getTheReceiverEmailAddressFromTheContextSomehow() }}</property>
    <property name="subject">New local user {{ title }} needs approval</property>
   </action>
  </actions>
 </rule>


<assignment
    location="/"
    name="email_local_coordinator_about_local_user_approval"
    enabled="True"
    bubbles="True"
    />


</contentrules>

然后是用于测试此功能的示例单元测试代码

from zope.component import getUtility, getMultiAdapter, getSiteManager
from Products.MailHost.interfaces import IMailHost
from Products.SecureMailHost.SecureMailHost import SecureMailHost

class DummySecureMailHost(SecureMailHost):
    meta_type = 'Dummy secure Mail Host'
    def __init__(self, id):
        self.id = id
        self.sent = []

        self.mto = None

    def _send(self, mfrom, mto, messageText, debug=False):
        self.sent.append(messageText)
        self.mto = mto

...

class BaseTestCase:
    """We use this base class for all the tests in this package. If necessary,
    we can put common utility or setup code in here.

    Mix-in class, also include FunctionalTestCase or PloneTestCase.
    """

    def afterSetUp(self):
                ...
        self.loginAsPortalOwner()
        sm = getSiteManager(self.portal)
        sm.unregisterUtility(provided=IMailHost)
        self.dummyMailHost = DummySecureMailHost('dMailhost')
        sm.manage_changeProperties({'email_from_address': 'moo@isthemasteofuniverse.com'})
        sm.registerUtility(self.dummyMailHost, IMailHost)
        ...

        def test_my_email_on_workflow_transition(self):
                self.workflow = self.portal.portal_workflow
                self.portal.invokeFactory("MyContentType", "myobject")
                myobject = self.portal.myobject

                self.dummyMailHost.sent = []

                self.workflow.doActionFor(myobject, "my_workflow_transition_id_here")
                review_state = self.workflow.getInfoFor(myobject, 'review_state')
                self.assertEqual(review_state, "my_new_workflow_state")

                # Check that the email has been send
                self.assertEqual(len(self.dummyMailHost.sent), 1) # Outgoing emails increased by one

                self.assertEqual(self.dummyMailHost.mto, ["receiver@dummy.host"])
                ...

安全

默认情况下,模板函数受限于当前用户的权限 - 这意味着输出可能会根据您登录的用户而有所不同。用户不应能够绕过 Zope 沙盒。

然而,一些标签并不完全安全(逃脱查看权限)并且您可能希望在多用户生产网站上禁用它们。

本产品不保证安全性。对于具有高安全要求站点,请咨询作者。

安全单元测试可在 此处 找到。

模板编写指南

本文档描述了 Easy 模板元素中可用的变量和函数。包含一些示例模板片段。

默认的 Jinja 后端将标签公开为函数。由于 Jinja 在变量和函数之间做出了明确的区分,您需要在标签后添加 () 以进行渲染。

模板上下文

模板上下文包含您在模板中可用的顶级变量。

变量在 context/plone.py 源代码文件中定义。

上下文变量

Plone 使用称为 Archetypes 的子系统来定义内容类型。内容类型是由模式中定义的字段构建的。所有默认的 Plone 内容类型(文档、文件夹、事件、新闻等)都是基于 Archetypes 的。

基于 Archetypes 的对象以 “原样” 暴露给模板引擎的 上下文 变量。其他上下文变量函数由运行对象的 Python 类定义。

您可以通过调用 getXXX 访问器函数来查询单个字段的值。公开的字段在 Archetypes 对象的模式源代码中定义。

以下是一些示例。

打印内容标题

{{ context.Title().decode("utf-8") }}

打印文档正文文本(HTML)

{{ context.getBody() }}

获取当前对象的 URL

{{ context.absolute_url() }}

如果您对该对象具有写访问权限,您甚至可以在模板中设置值,尽管这并不很有用

{{ context.setTitle('Moo the novel') }}

Unicode和UTF-8

Jinja,就像 Python 2.x 软件一样,假设所有字符串都是 ASCII 或 Unicode。

如果您正在输出文本,它

  • 包含国际字符

  • 已知为 UTF-8

您必须在模板中解码输入文本。对于 Plone,以下内容已知为 UTF-8

  • 所有 Archetypes 文本字段访问器,如 Title()、Description(),都返回 UTF-8 字节字符串

  • portal_catalog 条目,如 Title、Description,直接反映 Archetypes 值并包含 UTF-8 字节字符串

  • 对于其他字符串,请查阅 Plone 源代码

要输出此类文本,必须执行解码。您可以通过直接调用 Python 字节码字符串的 decode() 方法来完成此操作。

对于类似访问器的函数

{{ context.Title().decode("utf-8") }}

对于目录大脑数据

{{ brain.Title.decode("utf-8") }}

否则,当遇到国际字符时,您将看到类似这样的内容

Traceback (innermost last):
  Module collective.templateengines.utils, line 104, in wrapExceptions
  Module collective.templateengines.backends.jinja, line 104, in applier
  Module jinja2.environment, line 705, in render
  Module <template>, line 3, in top-level template code
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 1: ordinal not in range(128)

遍历

遍历是一种在 Zope 的对象图中查找对象的方法。

要访问除当前模板文档之外的其他对象,您可以使用 Zope 的遍历机制在文件夹层次结构中进行遍历。

获取父文件夹

{{ context.aq_parent }}

可以使用对象 ID 遍历文件夹内容对象。

获取当前文件夹中 URL ID 为 'sister' 的姐妹页面

{{ context.aq_parent.sister }}

门户

门户是您站点的根 Plone 对象。您可以使用它作为遍历起点来查询您站点的其他对象。例如:

一些可用的方法在 IPortal 接口中有描述。

要访问顶级新闻文件夹

{{ portal.news }}

门户状态

portal_state 存储有关系统当前状态的信息。例如,用户是否登录,导航基础,门户标题,活动语言等。

该对象实现了 IPortalState 接口。

如何为匿名用户和登录用户分别输出示例

{% if portal_state.anonymous() %}
        anon
{% else %}
        logged in
{% endif %}

用户

用户变量持有当前用户安全信息。

这实现了 Basic user 接口。

最有用的功能是通过 getUserName() 获取当前用户名。

成员

用户成员信息。此信息取决于使用的成员后端(Plone 默认,LDAP,SQL,自定义等)。

门户URL

当调用时,portal_url 返回当前门户根 URL。

示例

<a href="{{ portal_url() }}">Home</a>

标签

标签是您可以在模板中使用的自定义函数。它们提供了一种轻松地将自己的 Python 函数扩展到模板的方法。标签在 collective.easytemplate 包的 tagconfig.py 文件中注册。

Easy Template 随带了一些有用的标签,下面将进行说明。

探索

转储对象方法和变量以供开发者使用。

警告。此标签不是多用户安全的。您希望在生产网站上禁用此标签,因为它是一种读取权限提升。

探索标签帮助您通过公开对象内部的变量和方法来构建脚本。它打印了可用方法和变量的表格输出。

参数:

object:要探索的对象

显示当前模板文档对象的内部结构

{{ explore(context) }}

显示在门户根目录中可用的内容

{{ explore(portal) }}

显示由返回列表的函数返回的内容 - 取第一个元素

{{ explore(query({"portal_type":"Folder})[0]) }}

查询

根据搜索条件返回站点对象。

查询返回 portal_catalog 搜索返回的站点对象列表。对象是目录大脑:包含元数据 ID 作为键的字典。

请参阅 ZMI 中的 portal_catalog 工具,了解可用的查询索引和返回的元数据字段。

键值对作为参数传递,并直接传递给 portal_catalog 搜索。

输出受限于当前用户权限。

参数:

  • searchParameters:portal_catalog 查询参数的 Python 字典。索引 -> 查询映射。错误的索引 ID 似乎不会引发任何错误。

返回值:

  • 一组 ZCatalog 大脑对象。大脑对象具有 getURL、getPath 和 getObject 方法以及用于目录元数据列的字典查找。

示例

返回按日期排序的前三个最新新闻项目对象

{{ query({"portal_type":"News Item","sort_on":"Date","sort_order":"reverse","sort_limit":3,"review_state":"published"}) }}

返回特定文件夹中的项

{{ query({path={"query" : "/folder", depth: 0}}) }}

有关可能的查询格式的更多信息,请参阅这份旧文档

查看

渲染浏览器:页面视图。如果没有注册视图,则返回占位符字符串。

参数 name:视图ID,它在browser/configure.zcml中显示。

参数 function:可选。要调用的视图实例方法名称。如果省略,则使用__call__()。

示例(渲染网站地图)

{{ view("sitemap_view", "createSiteMap") }}

小部件

渲染视图小部件。

参数 name:视图小部件ID,它在portal_view_customizations ZMI页面上显示。

示例

{{ viewlet("portal.logo") }}

提供者

这等价于用于渲染视图小部件和端口小部件管理员的TAL提供者表达式。

要渲染所有左侧列小部件,请调用

{{ provider("plone.leftcolumn") }}

RSS源

该函数读取RSS源。您可以手动遍历条目并格式化输出。这主要适用于处理HTML源代码的情况。

参数

  • url:RSS或RSS的URL

  • cache_timeout:可选,默认值60。HTTP GET请求应该执行的秒数。

返回

  • 包含以下键的字典列表:titlesummaryurlupdatedfriendly_date

示例(原始HTML编辑)

{% for entry rss_feed("http://blog.redinnovation.com/feed/") %}
        <p>
                <b>Title:</b>
                <span>{{ entry.title }}
        </p>

        <p>
                <b>Summary:</b>
                <span>{{ entry.summary }}
        </p>
{% endfor %}

plone.app.portlets.rss.RSSFeed用作RSS阅读器后端。

列表文件夹

列出文件夹内容并作为

这里提供的格式化选项不是特别强大。您可能想使用query()标签进行更强大的输出格式化。

参数

  • folder:列出文件夹的路径。与网站的URI路径相同。

  • title:将此标题渲染为列表的副标题

  • filters:应用于输出的portal_catalog查询参数。有关示例,请参阅下面的query()。

  • exclude_self:如果为True,则不在输出中渲染上下文模板文档

  • extra_items:以逗号分隔的URI字符串,这些URI位于目标文件夹之外,但应附加到列表中。

示例(从课程文件夹创建课程模块列表):

{{ list_folder("courses/marketing/cim-professional-certificate-in-marketing", title="Other modules in this course:", filters={ "portal_type" : "Module"}) }}

最新新闻

渲染来自网站的最新发布的新闻列表。使用collective.easytemplate.tags/latest_news.pt模板。

latest_news还作为如何将自定义视图放入视觉编辑器的示例。

参数

  • count:渲染的项目数量

示例:

{{ latest_news(3) }}

翻译

使用消息ID进行翻译目录查找。

将消息翻译成另一种语言。该函数假定翻译在gettext po文件中可用。

参数

  • message:要翻译的gettext msgid

  • domain:消息所属的gettext域,可选,默认为“plone”

  • language:目标语言代码,例如fi,可选,默认为当前选定的语言

  • default:如果所选语言中缺少msgid,则显示的默认值

返回

  • 翻译后的字符串

示例

{{ translate("missing_id", default="Foobar")  }}

{{ translate("box_more_news_link", "plone", "fi")  }}

有关可用的默认Plone msgid,请参阅PloneTranslations产品源

当前语言

获取用户的当前语言。

这可以通过语言进行条件显示。

参数

  • 无参数

返回

  • 当前语言代码作为字符串,例如“fi”

示例

{% if current_language() == "fi" %}
        Paivaa
{% else %}
        Hello
{% endif %}

高级示例

新闻和博客表

以下片段将创建一个两列的表格。左列包含摘要和链接到网站上所有已发布的新闻。右列包含从RSS源获取的外部博客条目的链接。新闻查询是语言敏感的 - 只显示当前活动语言中的新闻。

两列都限制为三个条目。

文本已翻译,并且当默认Plone翻译目录缺少合适的msgids时,使用自定义翻译目录twinapex

此示例必须放入未过滤的模板输入框中,因为Kupu似乎在代码中插入了不需要的&nbsp;字符。

示例

<table class="front-page two-column">
        <tbody>
                <tr>
                        <td class="column-2">
                                <h2>
                                        <a href="{{ portal_url() }}/news">
                                                {{ translate("news", "twinapex") }}
                                        </a>
                                </h2>
                                {% for item in query({"portal_type":"News Item", "review_state" : "published", "sort_on":"Date", "sort_order":"reverse", "sort_limit":3}) %}
                                        <div class="fp-item">
                                                <a href="{{ item.getURL() }}">{{ item.Title }}</a>
                                                <p>
                                                        {{ item.Description }}
                                                </p>

                                                <p class="timestamp">{{ item.Date }}</p>
                                        </div>
                                {% endfor %}

                                <p class="more">
                                        <a class="more" href="{{ portal_url() }}/news">
                                                {{ translate("box_more_news_link", default="More news...") }}
                                        </a>
                                </p>
                        </td>
                        <td class="column-2">
                                <h2>
                                        <a href="{{ portal_url() }}/news">
                                                {{ translate("blog", "twinapex") }}
                                        </a>
                                </h2>
                                {% for item in rss_feed("http://blog.redinnovation.com/feed/")[0:3] %}
                                        <div class="fp-item">
                                                <a href="{{ item.url }}">{{ item.title }}</a>
                                                <p class="timestamp">{{ item.friendly_date }}</p>
                                        </div>
                                {% endfor %}

                                <p class="more">
                                        <a class="more" href="http://blog.twinapex.fi">
                                                {{ translate("box_morelink", default="More...") }}
                                        </a>
                                </p>
                        </td>
                </tr>
        </tbody>
</table>

调试技巧

如果模板编译失败,您可能已经犯了复制粘贴错误。请以原始HTML模式查看模板以跟踪错误

  • 模板表达式中的HTML标签

  • 模板表达式中的硬换行符

注册新标签

如果您想添加自己的模板函数,必须将它们添加到collective.easytemplate.tagconfig模块中(注意:将来可以使用Zope配置指令和ZCML来注册标签)。

所有标签都实现了collective.templateengine.interfaces.ITag接口。

例如,请参阅collective.easytemplate.tags包中现有的标签。

如何注册自定义标签的示例(在您的产品的initialize()方法中运行)

from collective.easytemplate import tagconfig

from myproducts import MyTag

tagconfig.tags.append(MyTag())

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定该选择哪个,请了解更多关于安装包的信息。

源代码分发

collective.easytemplate-0.8.0.zip (106.6 kB 查看散列值)

上传时间 源代码

由以下组织支持