I am creating a folderish type (archetype 开发者_C百科1) and I want to have possibility to add only a single object of (archetype 2) to this folder.
You can restrict the addable types inside your folderish type ("archetype 1") to "archetype 2" by amending the "archetypes 1" types definition (profiles/default/archetype1.xml):
<?xml version="1.0"?>
<object name="archetype1">
...
<property name="filter_content_types">True</property>
<property name="allowed_content_types">
<element value="archetype2" />
</property>
...
</object>
Ok, so you want your second type archetype 2 to be addable only once inside archetype 1?
I would do it in such a way that the Add New dropdown on the green edit bar only shows archetype 2 if it can be added (the other solutions here require the user to first render the add form and then be told that this is not allowed).
You need to make sure that your folderish archetype 1 subclasses ConstrainTypesMixin.
I think if you use the folderish content types in Products.ATContentTypes you will automatically subclass this mixin class, but it helps to make sure.
Then, inside archetype 1, add the method: getLocallyAllowedTypes. This method is declared in the ConstrainTypesMixin class in Products/ATContentTypes/lib/constraintypes.py
In this method you can now add the logic to check if an instance of archetype 2 has already been added. If it has, don't return it as one of the locally allowed types. If it hasn't, then return it (with the other types if they exist).
Make sure to first call super() in this method to get the locally added types from the superclass's method.
To understand how this works, you can look at the *_addableTypesInContext* method in the FactoriesSubMenuItem class in plone/app/contentmenu/menu.py to see when and how this getLocallyAllowedTypes method is called.
You are probably best off creating a custom add form (perhaps using z3c.form) and making the placing the restriction there.
You can override the createObject.cpy script and add a check there:
this_type = REQUEST.form.get('type_name')
if this_type == 'MyATContentType':
# MyATContentType needs a special check
ctool = getToolByName(context, 'portal_catalog')
this_path = '/'.join(context.getPhysicalPath())
# Query the Catalog to see we already have an instance of this object here
results = ctool.searchResults({'portal_type': this_type, 'path': this_path})
if results:
context.plone_utils.addPortalMessage(_(
u'Sorry, but there already is an object of type %s here.' % this_type
))
# Redirect to the edit form of the first found object.
cv = results[0]
cv_path = cv.getPath()
return context.REQUEST.RESPONSE.redirect(cv_path + "/edit")
Provide the customized script, together with the associated .metadata file, in the skins/templates folder of your product.
Bonus tip: In Dexterity, you would add this check in dexterity.AddForm.update()
精彩评论