Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Tips / GenericSetup

GenericSetup

GenericSetup 在 Plone 世界裡的縮寫是 GS,它是系統或擴充模組用來管理設定值的機制和工具,基本的管理方式包括 import 和 export 設定值的內容,比較不同版本的設定結果。 Stack overflow: profiles/default/actions.xml calling an adapter from url_expr

常見的 Plone 系統設定值,像是「使用哪些內容型別」「使用者擁有哪些角色權限」等資訊,這些設定值用來決定系統的功能,為了管理這些設定內容,Plone 使用 GenericSetup 的 Profile 來記錄它們。想讓設定值生效的話,透過 Quick Install 重新安裝模組,是最簡單的方法,另外也可以透過 ZMI portal_setup 的 Import 方式讓它生效,或是透過 Export 方式來下載設定內容。

QuickInstall

擴充模組要被安裝到 Plone 系統裡,要跟 QuickInstall 合作。從技術底層來看,QuickInstall 是透過 Products.CMFQuickInstallerTool 來發揮功能,擴充模組滿足兩種型式,就具備可安裝的狀態,一是使用 Extensions/install.py 來管理,Plone 3 以前的版本採用這種方式,二是使用 GenericSetup 的 Profile 來記錄它們。

值得注意的是,QuickInstall 只處理物件「新增」的狀況,並不處理「修訂」或「刪除」的狀況。

想要新增擴充模組的相依關係,並自動安裝啟用,步驟有二: 先在 setup.py 註明模組名稱,再於 profiles/default/metadata.xml 註明 Profile 資訊。

setup( ...
    install_requires=[ ...
        'collective.blog.star'
  <dependencies>
    <dependency>profile:collective.blog.star:default</dependency>
  </dependencies>

Conditionally Require Package: collective.jsonify

昇級 Plone5 將警告 QuickInstall 減少使用,在 Plone6 之際將移除

Registering a Profile

Profile 是一組 XML 格式內容的檔案,對一個 Plone Site 而言,用來描述整個系統設定狀況的檔案被稱為 Base Profile,以它們為基礎,接著再由一些稱為 Extension Profile 的檔案來調整設定狀況。GenericSetup 使用 ZCML 來註冊 Profile,通常是寫在擴充模組的 configure.zcml 檔案裡:

<configure
 xmlns="http://namespaces.zope.org/zope"
 xmlns:genericsetup="http://namespaces.zope.org/genericsetup">

 <genericsetup:registerProfile
  name="default"
  title="My Package"
  directory="profiles/default"
  description="Default profile for My Package"
  provides="Products.GenericSetup.interfaces.EXTENSION" />

</configure>

namespaces

偷懶的方法,就是照範例程式碼複製上去。

Conditional Register Profile based on Plone Versions Products.ATFlashMovie example

版本號碼 Version 可能檢查點: portal_setup.listProfileInfo()

Registry

Configuration Registry vs Resource Registry

Use Case

portal_properties/site_properties 在 Plone4 與 Plone5 作法不同 Control Panel: Security Settings

Hide Uninstall Profiles

from Products.CMFPlone.interfaces import INonInstallable

class HiddenProfiles(grok.GlobalUtility):

    grok.implements(INonInstallable)
    grok.provides(INonInstallable)
    grok.name('your.package')

    def getNonInstallableProfiles(self):
        profiles = ['your.package:uninstall']
        return profiles

Import Session Configuration

profiles/default/types/MyType.xml 更新後,可以執行 Type Tool (Products.CMFCore.exportimport.typeinfo.importTypesTool) 的 Import Step 來重啟生效。

portal_action sorting order

allowed_interface 和 allowed_attributes 不能併用

dieter: When "GenericSetup" creates the local utility registrations, it does this by way of reference to an object inside the portal object. "five.localsitemanger" implements this reference on the ZODB level: what is stored in the local component registry is the same ZODB object (as identified by its "_p_oid") as that in the portal. However, when the portal object is later recreated (e.g. by an extensions profile requiring a different implementation class for the portal object), then the portal object and the object in the local component registry are different - with surprising effects.

The best approach would be that "five.localsitemanager" would not implement the reference on the ZODB level but instead by an access path (this way, utility object would never get its own copy but always refer to the unique portal object). "five.localsitemanager" works like this in some situations (when the registered object is acquisition wrapped and not a direct child of the site).

purge_old option

As a workaround, you can rerun the "GenericSetup" import step that controls the local component registry -- this updates the local component registry with the portal's objects.

actions.xml : collective.easyform

     <property
-      name="url_expr">string:${object_url}/fields</property>
+      name="url_expr">python:object_url + '/fields' if context.restrictedTraverse('@@plone_interface_info').provides('collective.easyform.interfaces.IEasyForm') else './fields'</property>

ZCML

Conflict: zope.i18n upgradeStep

<includeDependencies package="." />

下列是舊的方式,通常會被刪除,用意是讓模組相容於 Zope2 舊版環境,確保 initialize() 在 Zope 啟動時會被執行,多數情況用不著,而且讓 installProduct() 之類的 Test Setup Code 變得複雜,像 plone.portlet.static 可能還在用

<five:registerPackage package="." initialize=".initialize" />

對應上述的 initialize() 函式,也可以從 my/package/__init__.py 刪除,通常就只留下空的內容。

Upgrade

Making profile for Upgrade Step: 如果 Upgrade Profile 存在,可以從 Site Setup Addons 看到昇級按鈕。

  1. 建立 upgrades.zcml 並透過 <include file="upgrades.zcml" /> 來引入:
    <configure
      xmlns="http://namespaces.zope.org/zope"
      xmlns:gs="http://namespaces.zope.org/genericsetup"
      i18n_domain="your.product">
      <gs:upgradeStep
        title="Upgrade your.productto 0.0.1"
        description="your.product upgrade step"
        source="*"
        destination="0.0.1"
        handler=".upgrades.upgrade_to_0_0_1"
        profile="your.product:default" />
    </configure>
  2. 建立 upgrades.py
    from Products.CMFCore.utils import getToolByName
    
    default_profile = 'profile-your.product:default'
    
    def upgrade_to_0_0_1(context):
        print "Upgrading to 0.0.1"
        context.runImportStepFromProfile(default_profile, 'controlpanel')

Dexterity Upgrade 範例

下列是額外的方便工具:

def upgrade(upgrade_product,version): 
    """ Decorator for updating the QuickInstaller of a upgrade """
    def wrap_func(fn):
        def wrap_func_args(context,*args):
            p = getToolByName(context,'portal_quickinstaller').get(upgrade_product)
            setattr(p,'installedversion',version)
            return fn(context,*args)
        return wrap_func_args
    return wrap_func

# snippet to use the above on a function
@upgrade('your.product','0.0.1')
def upgrade_to_0_0_1(context):
    print "Upgrading to 0.0.1"
    context.runImportStepFromProfile(default_profile, 'controlpanel')

collective.z3cform.widgets collective.glossary collective.mailchimp

Circular Dependency

portal_properties

Update TextField from textplain to texthtml webcouturier.dropdownmenu collective.pwexpiry

ERROR Zope.SiteErrorLog http://mysite.com/portal_quickinstaller/prefs_reinstallProducts
Traceback (innermost last):
  Module ZPublisher.Publish, line 126, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module ZPublisher.Publish, line 46, in call_object
  Module Products.CMFCore.FSPythonScript, line 127, in __call__
  Module Shared.DC.Scripts.Bindings, line 322, in __call__
  Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec
  Module Products.PythonScripts.PythonScript, line 344, in _exec
  Module script, line 11, in prefs_reinstallProducts
   - 
   - Line 11
  Module Products.CMFPlone.QuickInstallerTool, line 77, in upgradeProduct
  Module Products.GenericSetup.upgrade, line 140, in doStep
  Module webcouturier.dropdownmenu.upgrades, line 11, in upgrade_1000_to_1010
ValueError: list.index(x): x not in list

Uninstall

傳統上 Uninstall Profile 並不是必要的設定檔案,在沒有把流程簡化或提供良好範例前,核心團隊並不要求初學者要建立它。

範例: collective.geo.settings collective.upload Uninstall Removes collective.js.bootstrap Resources

IProfileImportedEvent: Log Installation and Removal of Addon componentregistry.xml helps clean

uninstall profile : hiding example collective.portlets.lineage eea.tags browserlayer.xml collective.opengraph collective.fingerpointing collective.easyform

<include package="Products.CMFCore" file="configure.zcml" />

實際上 configure.zcml 是預設值,通常不需要額外指定

z3c.unconfigure: template path subscriber

範例: example.gs 因為 Quick Installer 並不會尋找 Uninstall Profile 所以需要 Extenstions/Install.py 檔案,當 Extensions/Install.py 未定義移除安裝的步驟,才會用到 Uninstall Profile。collective.weather 利用 catalog 移除 portlet 的技巧。

Shane Hathaway: collective.lineage zope.interface breaks if you simply try to unregister both utilities. During unregistration, zope.interface finds the utility to unregister, but then raises a KeyError.

# profiles/removep4a/componentregistry.xml
   <utilities>
     <utility remove="True"
-        interface="p4a.subtyper.interfaces.IFolderishContentTypeDescriptor"
+        interface="p4a.subtyper.interfaces.IPortalTypedFolderishDescriptor"
         name="collective.lineage.childsite" />
   </utilities>

Reset profile version on uninstall add-on https://stackoverflow.com/q/51417865

順序

zope/component/registry.py line 321, in subscribers
AttributeError: adapters

initContent import step 可能在 typeinfo step 安裝之前就被執行:

<genericsetup:importStep
    name="my.package_various"
    title="my package various"
    description="Various setup steps for my package"
    handler="extranet.core.init.initContent">
  <depends name="typeinfo"/>
</genericsetup:importStep>

Problem After Upgrading from 4.0.7 to 4.2.1 1.8.0 breaks with plone.app.testing

Removing profile selection in plone-addsite view: INonInstallable Marker

Permissions Component Lookup Error

permission setup_various vs setuphandlers.post_install

zcml.condition reduce redundant information in profiles zcml:condition can only test on existance of modules, but not definitions inside modules

overrides.zcml

Renaming Profiles: dexterity.membrane

Adding optional parameter to __init__ of portal_vocabularies

CFG instead of ZCML zcml2py

Apply Profile Dependencies Only Once

Replace Import Steps to post_install Handlers

取得之前的 context

good_old_context = context._getImportContext(profile_id)

Issues

ConstraintNotSatisfied: (u'0', '')

upgradeStep 執行的歷程記錄