用于克隆升级的升级控制面板和升级助手。
项目描述
介绍
该产品旨在简化在 Plone 中运行和编写第三方通用设置升级步骤。
它基于通用设置 (portal_setup) 的升级机制,提供了一个用于一次运行多个升级的控制面板。
此外,还提供了一个用于编写升级步骤的基类,其中包含用于常见任务的各种帮助程序。
<nav class="contents" id="table-of-contents">目录
认证:01/2013
</figcaption> </figure>特征
管理升级:提供使用通用设置升级第三方 Plone 软件包的高级视图。它可以通过易于使用的用户界面一次升级多个软件包。通过解析依赖图,它能够优化升级步骤顺序,从而使升级无忧。
编写升级:该包提供了一个基本升级类,其中包含用于常见升级任务的各种帮助程序。
使用更少的 ZCML 升级目录:通过将目录注册为升级目录,每个升级步骤都不需要额外的 ZCML。通过使用时间戳作为版本号,我们减少了(合并)冲突和潜在的错误。
导入配置文件升级步骤:有时升级步骤仅包括导入一个特制的通用设置配置文件。一个新的 upgrade-step:importProfile ZCML 指令使这变得更加简单。
安装
通过将ftw.upgrade添加到构建中的鸡蛋列表来安装它。然后运行 buildout 并重新启动您的实例:
[instance]
eggs +=
ftw.upgrade
转到您的 Plone 站点的站点设置并激活ftw.upgrade附加组件。
安装 ftw.upgrade 的控制台脚本
如果您在基于 plone.recipe.zope2instance 的部分的鸡蛋列表中包含ftw.upgrade ,则应该为您自动生成bin/upgrade脚本(也就是说,如果您没有通过scripts选项限制或抑制脚本生成)。
否则,可以通过额外的构建部分来安装控制台脚本bin/upgrade :
[buildout]
parts += <s>upgrade</s>
[upgrade]
recipe = <s>zc.recipe.egg:scripts</s>
eggs = <s>ftw.upgrade</s>
兼容性
与 Plone 4.3.x 和 5.1.x 兼容。
管理升级
@@manage-upgrades视图允许一次升级多个包:
后备视图
@@manage-upgrades-plain视图充当 @@manage-upgrades 的后备视图。它不包括 plone 的主模板,因此当默认视图由于某种原因失败时可能能够呈现。
bin/升级脚本
有关如何安装bin/upgrade的说明,请参阅控制台脚本安装部分。
bin/upgrade控制台脚本可以管理文件系统上的升级(创建新升级、更改升级顺序)以及与已安装的 Plone 站点交互、列出配置文件和升级以及安装升级。
一些例子:
$ bin/upgrade create "AddCatalogIndex"
$ bin/upgrade touch my/package/upgrades/20110101000000_add_catalog_index
$ bin/upgrade sites
$ bin/upgrade list -s Plone --auth admin:admin --upgrades
$ bin/upgrade install -s Plone --auth admin:admin --proposed --intermediate-commit
bin/upgrade脚本的完整文档可通过其帮助系统获得:
$ bin/upgrade help
升级步骤助手
UpgradeStep基类提供了在编写升级步骤时有用的各种工具和助手。可以直接注册classmethod来使用。请注意,该类非常特殊:它就像一个函数并自动调用自身。
升级步骤定义示例(在upgrades.py中定义):
from ftw.upgrade import UpgradeStep
class UpdateFooIndex(UpgradeStep):
"""The index ``foo`` is a ``FieldIndex`` instead of a
``KeywordIndex``. This upgrade step changes the index type
and reindexes the objects.
"""
def __call__(self):
index_name = 'foo'
if self.catalog_has_index(index_name):
self.catalog_remove_index(index_name)
self.catalog_add_index(index_name, 'KeywordIndex')
self.catalog_rebuild_index(index_name)
在configure.zcml中注册(假设它在同一目录中):
<configure
xmlns=<s>"http://namespaces.zope.org/zope"</s>
xmlns:genericsetup=<s>"http://namespaces.zope.org/genericsetup"</s>
i18n_domain=<s>"my.package"</s>>
<genericsetup:upgradeStep
profile=<s>"my.package:default"</s>
source=<s>"4"</s>
destination=<s>"5"</s>
title=<s>"Update index 'foo'."</s>
handler=<s>".upgrades.UpdateFooIndex"</s>
/>
</configure>
使用进度记录更新对象
由于升级步骤通常会更新目录中索引的一组对象,因此有一个有用的帮助方法self.objects()将查询目录与Progress Logger结合起来。无限制地查询目录,以便我们处理所有对象。
以下是更新特定类型的所有对象的示例:
from ftw.upgrade import ProgressLogger
from ftw.upgrade import UpgradeStep
class ExcludeFilesFromNavigation(UpgradeStep):
def __call__(self):
for obj in self.objects({'portal_type': 'File'},
'Enable exclude from navigation for files'):
obj.setExcludeFromNav(True)
运行升级步骤时,您将看到一个进度日志:
INFO ftw.upgrade STARTING Enable exclude from navigation for files INFO ftw.upgrade 1 of 10 (10%): Enable exclude from navigation for files INFO ftw.upgrade 5 of 50 (50%): Enable exclude from navigation for files INFO ftw.upgrade 10 of 10 (100%): Enable exclude from navigation for files INFO ftw.upgrade DONE: Enable exclude from navigation for files
方法
UpgradeStep类有各种辅助函数:
- self.getToolByName(tool_name)
返回升级站点的名为tool_name的工具。
- self.objects(目录查询,消息,记录器=无,保存点=无)
查询目录(不受限制)和具有完整对象的迭代器。迭代器使用传递的消息配置并调用ProgressLogger。
如果设置为非零值,则savepoints参数会导致每 n 个项目创建一个事务保存点。这可用于在创建大型事务时检查内存使用情况。默认值None表示我们没有配置这个特性,它应该使用默认配置,通常是1000。有关详细信息,请参阅保存点部分。为了完全禁用保存点,您可以使用savepoints=False。
这种方法会在匹配的大脑被破坏时从目录中删除,因为大脑的对象不再存在。进度记录器不会补偿跳过的对象并在达到 100% 之前终止。
- self.catalog_rebuild_index(名称)
重新索引由name标识的portal_catalog索引。
- self.catalog_reindex_objects(查询,idxs=None,保存点=None)
使用query重新索引在目录中找到的所有对象。可以将索引列表作为idxs传递以限制索引索引。保存点参数将传递给self.objects()。
- self.catalog_has_index(名称)
返回是否有目录索引名称。
- self.catalog_add_index(name, type_, extra=None)
将新索引添加到portal_catalog工具。
- self.catalog_remove_index(名称)
从portal_catalog工具中删除索引。
- self.actions_remove_action(类别,action_id)
从portal_actions工具中删除给定 类别中由action_id标识的操作。
- self.catalog_unrestricted_get_object(大脑)
返回大脑的不受限制的对象。不再有对象的死脑将从目录中删除,并返回None。
- self.catalog_unrestricted_search(查询,full_objects=False)
搜索目录而不检查安全性。当full_objects为True时,将返回不受限制的对象而不是大脑。升级步骤通常应该使用不受限制的目录访问,因为应该升级所有对象——即使运行升级的管理器对对象没有访问权限。
使用full_objects时,不再有对象的死脑将从目录中删除并在生成器中跳过。当死脑被移除时,生成的生成器的长度将无法补偿跳过的对象,因此会太大。
- self.actions_add_type_action(self, portal_type, after, action_id, **kwargs)
从portal_type标识的类型中添加一个portal_types动作,位置可以由 after属性定义。如果找不到 after 动作,则该动作将被插入到列表的末尾。
- self.actions_remove_type_action(portal_type, action_id)
从portal_type标识的类型中删除portal_types动作,动作id 为action_id。
- self.set_property(context, key, value, data_type='string')
在给定的context上使用键key和 value安全地设置属性。如果该属性不存在,则使用类型data_type创建该属性。
- self.add_lines_to_property(上下文、键、行)
使用对象上下文 添加行上的键key更新属性。该属性预计为“线”类型。如果该属性不存在,则创建它。
- self.setup_install_profile(profileid, steps=None)
安装由profileid标识的通用设置配置文件。如果列表步骤名称与步骤一起传递(例如 ['actions']),则仅安装这些步骤。默认安装所有步骤。
- self.ensure_profile_installed(profileid)
仅在尚未安装通用设置配置文件时才安装它。
- self.install_upgrade_profile(步骤=无)
安装与此升级步骤关联的通用设置配置文件。通过使用 upgrade-step:importProfile或upgrade-step:directory指令,可以将配置文件与升级步骤相关联。
- self.is_profile_installed(profileid)
检查是否安装了通用设置配置文件。尊重通过快速安装程序卸载产品。
- self.is_product_installed(product_name)
检查是否安装了产品。
- self.uninstall_product(product_name)
使用快速安装程序卸载产品。
- self.migrate_class(obj, new_class)
更改对象的类。它对基于 BTreeFolder2Base 的容器进行了特殊处理。
- self.remove_broken_browserlayer(name, dottedname)
删除一个浏览器层注册,其接口不能再从持久注册中导入。实例启动时出现的此类消息可能表明存在此问题: WARNING OFS.Uninstalled Could not import class 'IMyProductSpecific' from module 'my.product.interfaces'
- self.update_security(obj, reindex_security=True)
更新单个对象的安全性(manage_access 中的复选框)。这与ProgressLogger结合使用很有用。可以跳过重新索引目录中的对象安全性 ( allowedRolesAndUsers )。这会加快更新速度,但只有在查看权限没有更改时才应禁用。
- self.update_workflow_security(workflow_names, reindex_security=True, savepoints=None)
更新具有工作流列表之一的所有对象。这在更新一堆工作流并且您想要确保正确更新对象安全性时很有用。
通过仅搜索可能具有此工作流程的类型,已完成的更新尽可能小。它确实支持放置工作流策略。
为了进一步加快速度,您可以传递reindex_security=False,但您需要确保您没有更改任何与安全相关的权限(对于默认 Plone,只有View需要 reindex_security=True )。
默认情况下,事务保存点每 1000 个对象创建一次。这可以防止在创建大型事务时过度消耗内存。如果您的服务器有足够的内存,您可以通过传递savepoints=None来关闭保存点。
- self.base_profile
属性base_profile包含升级配置文件的配置文件名称,包括profile-前缀。示例:u"profile-the.package:default"。此信息仅在使用 upgrade-step:directory指令时可用。
- self.target_version
属性target_version包含作为字节串的升级步骤的目标版本。升级步骤目录示例:"20110101000000"。此信息仅在使用 upgrade-step:directory指令时可用。
进度记录器
当升级步骤需要很长时间才能完成时(例如,在执行数据迁移时),管理员需要了解有关更新进度的信息。当通过网络服务器/代理访问 Zope 时,连续输出以避免代理超时也很重要。
ProgressLogger使记录进度变得非常容易:
from ftw.upgrade import ProgressLogger
from ftw.upgrade import UpgradeStep
class MyUpgrade(UpgradeStep):
def __call__(self):
objects = self.catalog_unrestricted_search(
{'portal_type': 'MyType'}, full_objects=True)
for obj in ProgressLogger('Migrate my type', objects):
self.upgrade_obj(obj)
def upgrade_obj(self, obj):
do_something_with(obj)
记录器将每 5 秒记录一次当前进度(默认)。示例日志输出:
INFO ftw.upgrade STARTING Migrate MyType INFO ftw.upgrade 1 of 10 (10%): Migrate MyType INFO ftw.upgrade 5 of 50 (50%): Migrate MyType INFO ftw.upgrade 10 of 10 (100%): Migrate MyType INFO ftw.upgrade DONE: Migrate MyType
工作流链更新器
当某个内容类型的工作流发生更改时,该类型的每个现有对象的工作流状态将重置为新工作流的初始状态。这真的很烦人。
WorkflowChainUpdater负责在更改链后将每个对象设置为正确的状态(类型的工作流):
from ftw.upgrade.workflow import WorkflowChainUpdater
from ftw.upgrade import UpgradeStep
class UpdateWorkflowChains(UpgradeStep):
def __call__(self):
query = {'portal_type': ['Document', 'Folder']}
objects = self.catalog_unrestricted_search(
query, full_objects=True)
review_state_mapping={
('intranet_workflow', 'plone_workflow'): {
'external': 'published',
'pending': 'pending'}}
with WorkflowChainUpdater(objects, review_state_mapping):
# assume that the profile 1002 does install a new workflow
# chain for Document and Folder.
self.setup_install_profile('profile-my.package.upgrades:1002')
工作流链更新程序默认迁移工作流历史。可以通过将 migrate_workflow_history设置为False来禁用工作流历史迁移:
with WorkflowChainUpdater(objects, review_state_mapping,
migrate_workflow_history=False):
# code
如果提供了转换映射,则会根据映射迁移工作流历史记录条目中的操作,以便转换适用于新工作流:
transition_mapping = {
('intranet_workflow', 'new_workflow'): {
'submit': 'submit-for-approval'}}
with WorkflowChainUpdater(objects, review_state_mapping,
transition_mapping=transition_mapping):
# code
Placeful Workflow Policy Activator
手动激活放置式工作流程规则时,所有具有新工作流程的对象都可能重置为新工作流程的初始状态。
ftw.upgrade 有一个工具,可以通过将旧工作流映射到新工作流来启用放置式工作流策略,而不会破坏审查状态:
from ftw.upgrade.placefulworkflow import PlacefulWorkflowPolicyActivator
from ftw.upgrade import UpgradeStep
class ActivatePlacefulWorkflowPolicy(UpgradeStep):
def __call__(self):
portal_url = self.getToolByName('portal_url')
portal = portal_url.getPortalObject()
context = portal.unrestrictedTraverse('path/to/object')
activator = PlacefulWorkflowPolicyActivator(context)
activator.activate_policy(
'local_policy',
review_state_mapping={
('intranet_workflow', 'plone_workflow'): {
'external': 'published',
'pending': 'pending'}})
上面的示例在“path/to/object”下的对象上递归地激活了一个放置工作流策略,启用了放置工作流策略“local_policy”。
然后,映射通过定义哪些旧状态(键、intranet_workflow)应更改为新状态(值、plone_workflow)将“intranet_workflow”映射到“plone_workflow”。
选项
activate_in:激活传入对象的位置工作流策略(默认为True)。
activate_below:以递归方式为传入对象的子级激活位置工作流策略(默认为True)。
update_security:更新对象安全性并重新索引 allowedRolesAndUsers (默认为True)。
就地迁移器
就地迁移器为在升级步骤中迁移内容提供了一种快速简便的方法。例如,它可以用于从原型迁移到敏捷。
Plone 的标准迁移和就地迁移之间的区别在于,标准迁移创建一个新的兄弟节点并移动子节点,而就地迁移只是替换树中的对象并将子节点附加到新的父节点。这是一种更快的方法,因为不会触发移动/重命名事件。
示例用法:
from ftw.upgrade import UpgradeStep
from ftw.upgrade.migration import InplaceMigrator
class MigrateContentPages(UpgradeStep):
def __call__(self):
self.install_upgrade_profile()
migrator = InplaceMigrator(
new_portal_type='DXContentPage',
field_mapping={'text': 'content'},
)
for obj in self.objects({'portal_type': 'ATContentPage'},
'Migrate content pages to dexterity'):
migrator.migrate_object(obj)
论据:
new_portal_type(必需):目标类型的 portal_type 名称。
field_mapping:旧字段名到新字段名的映射。
options:一个或多个选项(二进制标志)。
ignore_fields:应忽略的字段列表。
attributes_to_migrate:属性列表(不是字段!),应该从旧对象复制到新对象。这默认为 DEFAULT_ATTRIBUTES_TO_COPY。
选项:
选项是二进制标志:可以对多个选项进行或运算。例子:
from ftw.upgrade.migration import IGNORE_STANDARD_FIELD_MAPPING
from ftw.upgrade.migration import IGNORE_UNMAPPED_FIELDS
from ftw.upgrade.migration import InplaceMigrator
migrator = InplaceMigrator(
'DXContentPage',
options=IGNORE_UNMAPPED_FIELDS | IGNORE_STANDARD_FIELD_MAPPING,
})
DISABLE_FIELD_AUTOMAPPING:默认情况下,旧实现和新实现上具有相同名称的字段会自动映射。此标志禁用自动映射。
IGNORE_UNMAPPED_FIELDS:默认情况下,当检测到未映射的字段时会引发FieldsNotMappedError 。此标志禁用此行为,未映射的字段将被忽略。
BACKUP_AND_IGNORE_UNMAPPED_FIELDS:忽略未映射的字段,但将未映射字段的值存储在新对象的注释中(使用来自常量UNMAPPED_FIELDS_BACKUP_ANN_KEY的键),以便稍后处理这些值。这在有附加字段(模式扩展器)时很有用。
IGNORE_STANDARD_FIELD_MAPPING默认情况下,STANDARD_FIELD_MAPPING 被合并到每个字段映射中,包含从 Archetypes 到 Dexterity 的标准 Plone 字段映射。此标志禁用此行为。
IGNORE_DEFAULT_IGNORE_FIELDS默认情况下,会跳过DEFAULT_IGNORED_FIELDS中列出的字段 。此标志禁用此行为。
SKIP_MODIFIED_EVENT当True时,不触发修改事件。
升级目录
upgrade-step:directory ZCML 指令允许我们使用具有以下优点的新升级步骤定义语法:
该目录一旦注册 (ZCML) 并在 Zope 启动时自动扫描。这减少了用于每个升级步骤的 ZCML ,并避免了由于必须在多个位置指定配置文件版本而产生的冗余。
使用时间戳而不是版本号。因此,我们的合并冲突更少。
配置文件的metadata.xml中的版本被删除,并在 Zope 启动时动态设置为最新升级步骤的版本。我们不再需要在升级中维护此版本。
每次升级都会自动成为一个通用设置配置文件。UpgradeStep类的实例知道它属于哪个配置文件,并且可以使用self.install_upgrade_profile()轻松导入该配置文件。 self.install_upgrade_profile()。
当我们不小心合并了时间戳比已经执行的升级步骤更旧的升级步骤时,管理升级视图会向我们显示。这有助于我们检测长期分支合并问题。
设置升级目录
为您的配置文件注册一个升级目录(例如my/package/configure.zcml):
<configure
xmlns=<s>"http://namespaces.zope.org/zope"</s>
xmlns:upgrade-step=<s>"http://namespaces.zope.org/ftw.upgrade"</s>
i18n_domain=<s>"my.package"</s>>
<include package=<s>"ftw.upgrade"</s> file=<s>"meta.zcml"</s> />
<upgrade-step:directory
profile=<s>"my.package:default"</s>
directory=<s>"./upgrades"</s>
/>
</configure>
创建配置的升级步骤目录(例如my/package/upgrades)并在此目录中放置一个空的__init__.py(防止一些 python 导入警告)。
从配置了此升级步骤目录的配置文件的metadata.xml中删除版本(例如my/package/profiles/default/metadata.xml):
<?xml version="1.0"?>
<metadata>
<dependencies>
<dependency>profile-other.package:default</dependency>
</dependencies>
</metadata>
声明升级软依赖
当有可选依赖项(extras_require)时,我们有时需要告诉 ftw.upgrade在安装我们的升级之前需要安装我们的可选依赖项的升级。
我们通过在upgrade-step:directory 指令中声明一个软依赖来做到这一点。可以通过用空格分隔多个依赖项来声明多个依赖项。
<configure
xmlns=<s>"http://namespaces.zope.org/zope"</s>
xmlns:upgrade-step=<s>"http://namespaces.zope.org/ftw.upgrade"</s>
i18n_domain=<s>"my.package"</s>>
<include package=<s>"ftw.upgrade"</s> file=<s>"meta.zcml"</s> />
<upgrade-step:directory
profile=<s>"my.package:default"</s>
directory=<s>"./upgrades"</s>
soft_dependencies=<s>"other.package:default
collective.fancy:default"</s>
/>
</configure>
创建升级步骤
升级步骤可以使用ftw.upgrade的bin/upgrade控制台脚本生成。这个想法是使用 zc.recipe.egg安装这个脚本。
安装后,升级步骤可以简单地使用脚本搭建:
$ bin/upgrade create AddControlpanelAction
create命令通过解析 *.egg-info/top_level.txt文件来搜索您的升级目录。如果您没有 egg-infos 或者您的升级目录命名不同,则自动发现不起作用,您可以使用--path参数提供升级目录的路径。
如果您想在终端中进行彩色输出,您可以安装颜色附加功能(ftw.upgrade[colors])。
重新排序升级步骤
bin/upgrade控制台脚本提供了对生成的升级步骤重新排序的方法。使用可选参数--before和--after升级步骤可以移动到特定位置。当省略可选参数时,升级步骤时间戳设置为当前时间。
例子:
$ bin/upgrade touch upgrades/20141218093045_add_controlpanel_action
$ bin/upgrade touch 20141218093045_add_controlpanel_action --before 20141220181500_update_registry
$ bin/upgrade touch 20141218093045_add_controlpanel_action --after 20141220181500_update_registry
手动创建升级步骤
在升级目录中为升级步骤创建一个目录。目录名称必须包含时间戳和描述,并用下划线连接,例如YYYYMMDDHHMMII_description_of_what_is_done:
$ mkdir my/package/upgrades/20141218093045_add_controlpanel_action
接下来,在上述目录的upgrade.py中创建升级步骤代码。需要创建此文件,否则升级步骤未注册。
# my/package/upgrades/20141218093045_add_controlpanel_action/upgrade.py
from ftw.upgrade import UpgradeStep
class AddControlPanelAction(UpgradeStep):
"""Adds a new control panel action for the package.
"""
def __call__(self):
# maybe do something
self.install_upgrade_profile()
# maybe do something
您必须从UpgradeStep继承。
给你的班级起一个合适的名字,尽管它不会出现在任何地方。
向类添加描述性文档字符串,前连续行用作升级步骤描述。
如果您在升级中有基于通用设置的更改,请不要忘记执行self.install_upgrade_profile() 。
将通用设置文件放在同一升级步骤目录中,它会自动充当此升级步骤的通用设置配置文件。install_upgrade_profile知道要导入什么。
对于我们的示例,这意味着我们将文件放在 my/package/upgrades/20141218093045_add_controlpanel_action/controlpanel.xml 中,该文件添加了新的控制面板操作。
生成的目录结构应该是这样的:
my/
package/
configure.zcml # registers the profile and the upgrade directory
upgrades/ # contains the upgrade steps
__init__.py # prevents python import warnings
20141218093045_add_controlpanel_action/ # our first upgrade step
upgrade.py # should contain an ``UpgradeStep`` subclass
controlpanel.xml # Generic Setup data to import
20141220181500_update_registry/ # another upgrade step
upgrade.py
*.xml
profiles/
default/ # the default Generic Setup profile
metadata.xml
可延期升级
可延期升级是一种特殊类型的升级,可以按需省略。默认情况下,它们仍然会被建议和安装,但可以通过设置标志从安装中排除。可延迟升级可用于将不需要立即运行但最终需要运行的升级与关键升级路径分离。这对于长时间运行的数据迁移或修复脚本特别有用。
通过在UpgradeStep的子类上设置类属性deferable可以将升级步骤标记为可 延迟:
#