Dexterity QuickStart
從功能角色來看,Dexterity 和 Archetypes 一樣,都是 Plone (CMF 應用程式) 的內容型別建置架構,差別在於 Dexterity 擁有更多擴充彈性,效率更高,而且更加輕量化。從 Plone 4.2 開始,啟用 Dexterity 模組的門檻大幅降低,David Glick 參與早期開發,在 Plone 4.3 之際,Dexterity 2.0.x 成為預設安裝的模組,可以滿足多數的實務應用。在 Plone 5 之際,Archetypes 的地位變成選項模組,Dexterity 成為建置內容型別的預設工具。詳細資訊可參考 Developer Manual 文件。
Plone 版本及預設套件列表:
Plone plone.dexterity plone.app.dexterity plone.supermodel 3.x 4.2.4 1.1.2 1.2.1 4.3.0 4.3.1 2.1.3 2.0.8 4.3.2 2.1.3 2.0.9 4.3.3 2.2.1 2.0.11 4.3.7 2.2.4 2.0.16 4.3.10 2.2.7 2.0.18 5.0.2 2.3.7 2.1.17 5.0.5 2.4.2 2.3.1 1.3.0
5.1.11 2.5.5 2.4.8 1.3.4
plone.dexterity 2.0+ only has a "soft dependency" on zope.app.content, and your build does not have it installed. Your add-on can have the desired interface provided on content type interfaces if you add zope.app.content to your setup.py install_requires and pin a version (3.5.1) in your buildout. Should you do this, all your content type schema/interface classes will provide IContentType.
Plone 4.1 或 Plone 4.2 想要搭配 Dexterity 2.0.x 的話,必須自行維護 KGS 和 backport。
Get Started
Plone 4.3.6 之後的環境,建議利用 mr.bob 來建立程式碼骨架:
$ cd src $ ../bin/mrbob -O my.pkg bobtemplates:plone_addon Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered. Answer with a question mark to display help. Values in square brackets at the end of the questions show the default value if there is no answer. --> What kind of package would you like to create? Choose between 'Basic', 'Dexterity', and 'Theme'. [Basic]: Dexterity --> Content type name [Task]: MyType --> Author's name [John Smith]: --> Author's email [jsmith@gmail.com]: --> Author's github username: jsmith --> Package description [An add-on for Plone]: --> Plone version [4.3.6]: Generated file structure at /home/jsmith/Plone/zinstance/src/my.pkg
$ tree my.pkg my.pkg/ ├── CHANGES.rst ├── CONTRIBUTORS.rst ├── MANIFEST.in ├── README.rst ├── bootstrap-buildout.py ├── buildout.cfg ├── docs │ ├── LICENSE.GPL │ ├── LICENSE.rst │ └── index.rst ├── setup.py ├── src │ └── my │ ├── __init__.py │ └── pkg │ ├── __init__.py │ ├── browser │ │ ├── __init__.py │ │ ├── configure.zcml │ │ ├── overrides │ │ └── static │ ├── configure.zcml │ ├── interfaces.py │ ├── locales │ │ ├── my.pkg.pot │ │ └── update.sh │ ├── profiles │ │ └── default │ │ ├── browserlayer.xml │ │ ├── metadata.xml │ │ ├── mypkg_default.txt │ │ ├── types │ │ │ └── MyType.xml │ │ └── types.xml │ ├── setuphandlers.py │ ├── testing.py │ └── tests │ ├── __init__.py │ ├── robot │ │ ├── test_example.robot │ │ └── test_mytype.robot │ ├── test_mytype.py │ ├── test_robot.py │ └── test_setup.py └── travis.cfg 13 directories, 32 files
使用 Dexterity 客製系統 Rename Dexterity object (id) after copy (from stackexchange)
examples: collective.polls example.ploneconf09 collective.conference collective.answers dexterity-sample
interfaces.py 裡 def myfunc(): 之類的定義,如果沒加上註釋段落,會造成 IndentationError: expected an indented block 錯誤。
Tool Script to Get All Attributes
Item or Container
依照能否包含其他型別的條件,型別分成 Item 與 Container 兩種。在 portal_catalog 的 meta_type 索引值裡,Dexterity 型別會被記錄成 Dexterity Item 或 Dexterity Container。
klass: Base Class 通常是 plone.dexterity.content Item 或 Container,可以用 MyContentType 之類的新型別,繼承 Base Class 來新增功能,再更新 klass 設定值。
Item 或 Container 轉換方法
每個內容型別至少含有一個 schema,預設的起始名稱是「未命名」(unnamed schema),它是一個 interface,加上 zope.schema 的 field,可以寫在 Python 程式碼裡,或是從 XML 載入。這個起始 schema 提供 IContentType 介面,如果呼叫 queryContentType() 函式,輸入 dexterity 物件,會傳回「未命名」的 schema,也就是說,這個 dexterity 物件將支援 schema 所定義的屬性值。Dexterity 提供基本類別程式碼,開發者很容易可以沿用。
WARNING Plone Deprecation Warning An icon for the 'controlpanel/DropdownConfiguration' action is being added to the action icons tool. The action icons tool has been deprecated and will be removed in Plone 5. You should register action icons directly on the action now, using the 'icon_expr' setting.
Dexterity 可以透過網頁介面建立內容型別,稱為 TTW (Through The Web) 方式,設定結果可以用 XML 格式儲存,記錄在 Dexterity FTI (Factory Type Information) 物件型別裡。利用 FTI 能夠動態存取 behavior 屬性值的機制,我們可以調整既有的 schema 定義,包括選用新的 widget 設定值,在這樣的情況下,behavior 扮演 adapter 角色,視需要而被啟用。Archetypes 的 schema extender 也應用 adapter 機制,但無法使用 FTI 來動態調整定義值。
collective.ambidexterity: TTW editing of views, defaults, validators, vocabulary
plone.dexterity 2.2.3 security private ?
Learning By Example
example.conference provides sample codes to demo how a dexterity application works. Here are the steps to install what you need for the demo. Assume you start with the Plone 4.0.2 UnifiedInstaller.
First, check out example.conference:
$ cd zinstance/src $ svn co http://svn.plone.org/svn/collective/example.conference/trunk example.conference
Second, edit your buildout.cfg file:
[buildout] extends = http://good-py.appspot.com/release/dexterity/1.0-next?plone=4.0.2 eggs = example.conference develop = src/example.conference
Note that example.conference
is not necessary for the zcml directive. The setup.py file of example.conference package includes plone.app.dexterity as a dependency and specify the package as a z3c.autoinclude plug-in. This feature is enabled in Plone 3.3 and later, that ensures no need to load its ZCML separately once the package is configured in buildout.cfg.
其他範例: collective.timeline collective.wfcomment apyb.conference clkss.content collective.pece
如果新增成功,但顯示時出現 This page does not seem to exist... 錯誤,可從 browser 程式碼檢查起。如果 XML 檔案有更動,記得要重新安裝啟用才能生效。
如果遇到 ExpatError: types/MyType.xml: mismatched tag 可從 <element ... /> 檢查是否有設定值少了 / 符號。
Schema Interface
基本上,Dexterity 的 Schema 是繼承自 zope.interface.Interface 的 Interface,附上 Field 定義值,標準的 Field 可由 zope.schema 取得,像 RichText、BlobFile、Relation 之類的 Field,就要另外載入模組來取得。
實務上,我們可能會使用 plone.supermodel.model.Schema 的 Marker Interface,方便日後加上 plone.autoform 的 Form Hint 功能,像 form.fieldset()、form.widget、form.omit 都是常見的應用。
FTI (Factory Type Information)
Type 有了 Schema 定義後,接著要準備註冊資訊,讓系統知道怎樣安裝它,我們利用 GenericSetup 來完成這步驟。
在 profiles/default/types.xml 檔案裡,找得到 FTI 註冊資訊:
<object name="portal_types"> <object name="my.proj.mytype" meta_type="Dexterity FTI" /> </object>
然後,要在 profiles/default/types 目錄裡新增 Type 的 XML 檔案,它的檔名要和上述名稱一致,例如 my.proj.mytype.xml。在 @@dexterity-types 管理介面,有 Export Type Profiles 和 Export Schema Models 兩個匯出按鈕,前者會下載 dexterity_export-yyyymmddhhmmss.zip 檔案,內含 types.xml 和 types/my-type.xml 定義檔,後者會下載 my-type.xml 欄位定義內容,在 dexterity-types/my-type/@@fields 管理介面,可以複製貼上匯出的欄位定義內容。
condition_expr="not:object/@@plone_lock_info/is_locked_for_current_user|python:True"
刪除 FTI 跟 Class 並不相同。其他注意事項,可以參考 Dexterity Developer Manual。
Plone uses only utf-8; dexterity content types don't have getCharset Dexterity Object as Field of Another Dexterity Object
plone.dexterity (CMF) defines the FTI and content classes, provides basic views (with forms based on z3c.form), handles security and so on. It also provides components to orchestrate the various functionality provided by the packages above in order to bring the Dexterity system together.
plone.directives.dexterity (CMF) adds convention-over-configuration support for Dexterity content and add/edit forms.
plone.app.dexterity (Plone) contains all Plone-specific aspects of Dexterity, including Ploneish UI components, behaviours and defaults.
IField: The default z3c.form data manager is z3c.form.datamanager.AttributeField which just does setattr and getattr on the (possibly adapted) context. If you want the field's get/set to be used, you'll either have to write a custom data manager for your field, or use a custom content item class that uses property descriptors.
Dexterity's getattr knows how to look up field defaults, but it doesn't know anything about widgets
Asko Soukka: Dexterity content import did not work outside site-root or Archetypes-container due to a few missing adapters agains GenericSetup content import framework. That should now be fixed for plone.app.contenttypes in c.themesitesetup 0.13.0 with extras.zcml enabled (described in README).
How did you create your export? You can create working export by:
1. Create a new site, with p.a.contenttypes and your example content
2. Upload your theme
3. Go to /Plone/++theme++example/@@export-site-setup
where "Plone" is your site and "++theme++example" your theme
4. Click export (it defaults to content export)
That was enough to export importable default Plone site content (including Folders, Documents and Collections).
表單設定值
Form Configuration with Schema Hints using Directives
Model
XML
<field name="links" type="zope.schema.List"> <title>Related Items</title> <value_type type="zope.schema.Choice"> <title>Related</title> <source>plone.supermodel.tests.dummy_binder</source> </value_type> </field>
Fields and Widgets
plone.autoform 和 plone.supermodel 都有 Directive 參數值,用來控制 z3c.form 的細節,要留意是否繼承 plone.supermodel.model.Schema 定義:
from plone.supermodel import model class ISampleSchema(model.Schema):
plone.dexterity/browser/view.py 定義了 Dexterity 預設 DefaultView 類別,它繼承了 plone.autoform/view.py 裡的 WidgetsView 類別,會主動顯示所有的 Widget,執行過程的 ++widget++ 會呼叫 update() 而不是 __call__()。參考 plone.dexterity/tests/test_views.py 的 TestDefaultView。
DefaultView 提供 Dexterity 的專有屬性值,像是 view.w 顯示所有 Widget,view.widgets 包含預設 FieldSet,view.groups 包含所有 FieldSet。
plone.app.blocks/layoutbehavior.py 的 ContentLayoutView 是使用 DefaultView 的範例。
Content Tree Popup Conflicts with Autocomplete
自訂 Custom View 和 Display Form 應用技巧 trouble viewform add CSS
Form Field Multiple-Choice Created from Parent Not Initialized on Object Creation
欄位移動位置 欄位值複製 Copy / Duplicate field values 移動到不同 Schema
SuperModel
SuperModel XML based Configuration Support: collective.themefragments
Relation
使用 IIntIds 來取代 Archetypes 的 getRelatedItems index
template display RelationList ObjPathSourceBinder MultiContentTreeFieldWidget
Supermodel, Dexterity Content Types, Named Relations between Content Types: title reference
Replace Field in plone.supermodel Schema 優先利用 plone.app.widgets 才是正解
從 ZMI 手動刪除被關聯的項目會造成 KeyError 透過 catalog.clear() 可以處理
ComponentLookupError: InterfaceClass zc.relation.interfaces.ICatalog
Compute Field Relation Behavior
Schemata vs Groups / Fieldset
FIELDSETS_KEY 是舊方法而且無法應付超過一組 Fieldset 情況 Moving Fields between Fieldsets
from plone.supermodel import model organizer = schema.TextLine() model.fieldset('ownership', label=_('label_schema_ownership', default=u'Ownership'), fields=['organizer'],)
tabbed fieldset, also known as groups
SchemaEditor and XML Definition plone.schemaeditor 是搭配 zope.schema 的編輯器,目前是 dexterity 編輯 schema 的核心模組。
Combine Model-driven Types with Schema-driven Types
Fieldset Order Behavior Fieldset Order
Hiding Description Field Computed Field
Permission
The Dexterity control panel is protected by a "Manage schemata" permission. It defaults to Managers only, but you can easily assign it to other roles. In fact I should probably change it to include Site Administrators by default.
Reference
Comparison with Archetypes. 移植結果的比較 More examples includes:
Set the Local Site Manager for Debug Session
http://www.martinaspeli.net/articles/dexterity
transmogrify.dexterity: transmogrifier blueprint for updating dexterity objects
Related Item Back Reference Dexterity References
AddForm EditForm DisplayForm
Custom Add / Edit Form: DefaultAddForm DefaultAddView
reorder the form widgets via update() method Custom Behavior on Save
getMultiadapter Error with Custom EditForm
note: Dexterity views and __init__ method
In general you shouldn't do anything in the __init__ of a view, but instead do everything in the __call__ method. This applies to all views, not just Dexterity views. When the __init__ is called, you don't have a security context set up yet, so there's certain things that won't work. It's much easier to just not do anything there, instead of debugging such problems.
從 Plone 4.1 (Dexterity 1.1) Dexterity 應該都有 UUID
理論上,讓 Content Type 都具備 Folderish 特性,會是好主意,但實務上有 Versioning 和效能的問題要處理,目前有移植方案。
Can Not Be Imported via GenericSetup: collective.cover
Dexterity 預設不需要 Archetypes 的 Accessor 或 Mutator
Plone 4.1 併用 Archetypes 和 Dexterity 的注意事項
利用 Behavior 可以建立 Field 並提供再利用效果
notes on Dexterity development
must not acquisition-wrap an objects before calling addContentToContainer
View Can Not Be Re-registered After Enabling Marker Interface
Ploneboard + TinyMCE JavaScript Code is not included
Custom Form
Custom Add Form Needs No Context
Example: imio.urbdial.notarydivision
storage strategy (attribute storage only)
Testing
Creation with File Image RichText
Validation
Detect Type On Adding Item MultiSelection / MultiValue Field with Input from Vocabulary
if INewsItem.providedBy(self.context) or (IAddForm.providedBy(self.view) and self.view.portal_type == 'News Item'):
Validating Data before Programmatic Creation
newInstance = createContentInContainer(folder, id, **schemavalues) errors = getValidationErrors(IMyType, newInstance) if errors: # Schema not validated; errors is a sequence of (field, exception) tuples # field is None if the error is for an invariant.
# ZCML <adapter factory=".behaviors.MyValidator" /> # Py from z3c.form import validator from zope.interface import Invalid class MyValidator(validator.SimpleFieldValidator): def validate(self, value): fieldsets = dict([(group.__name__, group) for group in self.widget.form.parentForm.groups]) other_value = fieldsets['myfieldset'].widgets['otherfield'].extract() # ... raise Invalid('Validation error') validator.WidgetValidatorDiscriminators(MyValidator, field=IMyBehavior['myfield'])
更新 RichText 內容
from Testing import makerequest root = makerequest.makerequest(app) site = root.mysite folder = site.myfolder['subfolder'] admin = root.acl_users.getUserById('admin') admin = admin.__of__(site.acl_users) from AccessControl.SecurityManagement import newSecurityManager newSecurityManager(None, admin) from zope.site.hooks import setHooks from zope.site.hooks import setSite setHooks() setSite(site) site.setupCurrentSkin(site.REQUEST) import csv from plone.app.textfield.value import RichTextValue import transaction with open('myfile.csv', 'rb') as f: dialect = csv.Sniffer().sniff(f.read(), delimiters=';') # f.read(1024) may fail f.seek(0) reader = csv.reader(f, dialect) for row in reader: item = folder[row[0]] item.text = RichTextValue(row[1].decode('utf-8'), 'text/html', 'text/x-html-safe', 'utf-8')
# UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 68: ordinal not in range(128)
item.reindexObject() transaction.commit()
programmatic creation: CSV example Setting Field Defaults Unauthorized, checkConstraints DateTime
folder = mysite.events import datetime from plone.dexterity.utils import createContentInContainer item = createContentInContainer(folder, 'Event', title=u"Bar") item.start = datetime.datetime(2013, 10, 10, 10, 0) item.end = datetime.datetime(2013, 10, 15, 12, 0)
使用 bin/plonectl run myscript.py 方式新增型別時,要注意 Id 決定方式,如果 Title 決定 Id 而且 Title 有重覆時,就會產生 The id "some-title" is invalid - it is already in use. 錯誤訊息。
plone.alterego: 動態衍生新的型別
Event Handler IObjectAddedEvent