I have a dexterity content type in Plone 4.2.4, that uses the contenttree widget to manage referenced objects, but only in the editForm.
I realized, that the referenced items had to be external_visible to get displayed by the widget, which means that anonymous users could View and AccessContentsInformation. Thats not what I wanted. So I dug in the contenttree widget source and added the following to my products browser/configure.zcml
<include package="Products.CMFCore" file="permissions.zcml"
zcml:condition="installed plone.app.upgrade" />
<browser:page
for="*"
name="contenttree-fetch"
class="my.product.content.bikemetamodel.EditForm"
permission="cmf.ModifyPortalContent"
/>
<adapter factory="my.product.browser.widgets.MetamodellContenttreeAdapter" />
and an adapter
class MetamodellContenttreeAdapter(object):
implements(IBikeMetaModel)
adapts(Interface)
def __init__(self, context):
self.context = context
def _get_allowed_modeltypes(self):
return None
def _set_allowed_modeltypes(self, value):
print "setting", value
allowed_modeltypes = property(_get_allowed_modeltypes, _set_allowed_modeltypes)
[...]
But this seems to be not enough. The underlying catalog search returns no result, If the permissions are set to deny View and AccessContentsInformation to anonymous users. So I guess, I must use the view permission to construct some kind of proxy user.
Would it be ok, to use a SecurityManager in the newly created view to fetch the results as a different user? Or am I just missing something?
Ok, here is how I solved the mystery.
After digging around a while, I realized, that I missed the point with my previous idea to override the ##contenttree-fetch view. The solution I came up with is quite simple and seems elegant (enough) for me. I do a sudo style sidestep now, to gather the required items.
Class EditForm(dexterity.EditForm):
grok.context(IBikeMetaModel)
# If it would be another than the edit view, we could manage
# permisssions here. Not neccessary in edit view, because the
# edit permission defined in this content types *.xml counts
# grok.require("cmf.ModifyPortalContent")
#property
def acl_users(self):
return getToolByName(getSite(), 'acl_users')
def updateWidgets(self):
# This is the magic. A sudo style sidestep to a user
# with the id "system" that has permission to gather
# the required lists in the updateWidgets function of
# the base class
proxy_user = self.acl_users.getUserById("system")
oUser = getSecurityManager()
newSecurityManager(self.request, proxy_user)
super(EditForm, self).updateWidgets()
# custom widget updates
self.widgets['title'].mode = DISPLAY_MODE
self.widgets['year'].mode = HIDDEN_MODE
self.widgets['brand'].mode = HIDDEN_MODE
self.widgets['model'].mode = HIDDEN_MODE
# Very Important! Switch back to the original user.
setSecurityManager(oUser)
Related
OK, so I've managed to create a custom project flavor with a custom property page. It all works and the values are being saved to the .csproj file like such:
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{880389B4-B814-4796-844B-F0E1678C31D1}" Configuration="Debug|Any CPU">
<ServiceLibraryProjectFlavorCfg>
<BooleanProperty>True</BooleanProperty>
</ServiceLibraryProjectFlavorCfg>
</FlavorProperties>
<FlavorProperties GUID="{880389B4-B814-4796-844B-F0E1678C31D1}" Configuration="Release|Any CPU">
<ServiceLibraryProjectFlavorCfg />
</FlavorProperties>
</VisualStudio>
What I cant seem to figure out is how to access this custom property from, say, a menu item callback in my package. I can get the project that the selected item in the solution explorer which was right clicked belongs to, but I'm stuck after that...
Any help will be appreciated
Thanx
Hein
OK, I figured it out.
As part of creating a custom project flavor, you inherit from FlavoredProjectBase and implement the IVsProjectFlavorCfgProvider interface.
the IVsProjectFlavorCfgProvider has one implementable method
int CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg)
So here I implemented a static mapping between my custom IVsProjectFlavorCfg and the specified IVsCfg
Already having a EnvDTE.Project reference, I could then use the following to get a IVsCfg reference:
IVsHierarchy hierarchy1 = null;
var sol = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution;
sol.GetProjectOfUniqueName(project.UniqueName, out hierarchy1);
IVsSolutionBuildManager bm = Package.GetGlobalService(typeof(IVsSolutionBuildManager)) as IVsSolutionBuildManager;
IVsProjectCfg[] cfgs = new IVsProjectCfg[1];
bm.FindActiveProjectCfg(IntPtr.Zero, IntPtr.Zero, hierarchy1, cfgs);
IVsCfg cfg = cfgs[0] as IVsCfg;
I could then use the IVsCfg reference to look up my custom configuration provider.
If you can access the project node instance (and if your project system is based on MPF), you can just use the GetProjectProperty method of the ProjectNode class. It obtains a ProjectPropertyInstance and returns its evaluated value, or null if the property does not exist.
I'm using Plone 4.3 with relstorage and I've somehow managed to lose the cmf_uid annotation on some of my content objects. This prevents collective.iterate from being able to check in content. Is there an easy way to have Plone walk through the database and re-add cmf_uid where it is missing? Already tried collective.catalogcleanup to no avail.
Here is a script that searches the portal (passed as context) for any Document that has a non-unique cmf_uid. Many of these documents actually have no cmf_uid but the indexed cmf_uid actually comes from the parent folder via Acquisition. Since the manifestation of the problem was that plone.app.iterate was unable to check in Document, the script adds a unique cmf_uid to just the Document types that appear to have non-unique cmf_uid but actually have no cmf_uid.
Although this adds cmf_uid to all Document, it would probably be sufficient to only add the attribute to documents currently being edited in checkouts.
"""
Add missing cmf_uid to Archetypes content.
cmf_uid is required to check in working copies of content.
"""
from Products.CMFUid.UniqueIdHandlerTool import UniqueIdError
from Acquisition import aq_inner
from collections import Counter
def add_missing_uids(context):
"""
context: the portal
"""
portal_uidhandler = context.portal_uidhandler
portal_uidgenerator = context.portal_uidgenerator
catalog = context.portal_catalog
brains = catalog.unrestrictedSearchResults()
freq = Counter(x.cmf_uid for x in brains)
for brain in brains:
# If it's only in use once then it's unique enough. Otherwise it's
# probably inheriting its indexed cmf_uid via Acquisition.
if freq[brain.cmf_uid] < 2 or brain.portal_type != 'Document':
continue
ob = aq_inner(brain.getObject())
if not portal_uidhandler.queryUid(ob):
print brain.Type, brain.portal_type, brain.getPath()
for i in range(3):
try:
portal_uidhandler.setUid(ob, portal_uidgenerator())
ob.reindexObject()
ob.reindexObject(idxs=['modified'])
break
except UniqueIdError:
print "RETRY"
else:
print "FAIL"
I have been working on a dexterity based plone application.
I have created a couple of new types. This is what I did to activate comments on a specific dexterity content type named "activity_report":
In Plone Control Panel
In the Discussion section I enabled the following:
globally enable comments
enable anonymous comments
In the Types Section
I chose the "Activity Report" type from the drop down and enabled the "Allow comments" option.
On the file system
In the FTI file activityreport.xml:
<property name="allow_discussion">True</property>
I have restarted the instance and even reinstalled the product, but I can not activate the comments section in the dexterity type.
It is worth mentioning that a standard type (ex. Page) can have the discussion module activated.
Is there something I am missing?
plone.app.discussion currently disables commenting for all containers (see https://dev.plone.org/ticket/11245 for discussion).
I used a monkey patch like the following in one project to short-circuit the normal check and make sure that commenting was enabled for my folderish content type:
from Acquisition import aq_inner
from Products.highcountrynews.content.interfaces import IHCNNewsArticle
from plone.app.discussion.conversation import Conversation
old_enabled = Conversation.enabled
def enabled(self):
parent = aq_inner(self.__parent__)
if parent.portal_type == 'my_portal_type':
return True
return old_enabled(self)
Conversation.enabled = enabled
where 'my_portal_type' is, of course, the portal_type you want commenting enabled for.
The David response is not accurate. The class to be monkeypatched is plone.app.discussion.browser.conversation.ConversationView :
from Acquisition import aq_inner
from plone.app.discussion.browser.conversation import ConversationView
old_enabled = ConversationView.enabled
def enabled(self):
parent = aq_inner(self.__parent__)
if parent.portal_type == 'My_type':
return True
return old_enabled(self)
It works for Plone 4.2 at least. However, thanks David for the hint.
As David and Victor already pointed out, you can just override the enable method of the conversation class. I would recommend using the following approach which is a bit cleaner than monkey patching the conversation class:
https://github.com/plone/plone.app.discussion/blob/master/docs/source/howtos/howto_override_enable_conversation.txt
I also added support for dexterity types to plone.app.discussion recently, so as soon as there is a new release you won't need to customize the conversation class any longer:
https://github.com/plone/plone.app.discussion/commit/0e587a7d8536125acdd3bd385e880b60d6aec28e
Note that this method supports commenting ON folderish objects. There is no support to enable/disable commenting for objects INSIDE a folderish object yet.
In case you want to be able to switch on/off commenting with a behavior field/widget:
https://github.com/plone/plone.app.dexterity/commit/0573df4f265a39da9efae44e605e3815729457d7
This will hopefully make it into the next plone.app.dexterity release as well.
I solved in configure.zcml:
<interface interface="Products.CMFPlone.interfaces.INonStructuralFolder" />
<class class="Products.PloneHelpCenter.types.Definition.HelpCenterDefinition">
<implements interface="Products.CMFPlone.interfaces.INonStructuralFolder" />
</class>
UPDATE: this is not a good idea. I had problems with missing Add menu for each content type having this fix.
I am trying to implement a basic Zope2 content type directly without using dexterity or Archetypes because I need this to be extremely lean.
from OFS.SimpleItem import SimpleItem
from Products.ZCatalog.CatalogPathAwareness import CatalogAware
from persistent.list import PersistentList
class Doculite(SimpleItem, CatalogAware):
""" implement our class """
meta_type = 'Doculite'
def __init__(self, id, title="No title", desc=''):
self.id = id
self.title = title
self.desc = desc
self.tags = PersistentList()
self.default_catalog = 'portal_catalog'
def add_tags(self, tags):
self.tags.extend(tags)
def Subject(self):
return self.tags
def indexObject(self):
self.reindex_object()
From an external method I am doing this:
def doit(self):
pc = self.portal_catalog
res1 = pc.searchResults()
o1 = self['doc1']
o1.add_tags(['test1', 'test2'])
o1.reindex_object()
res2 = pc.searchResults()
return 'Done'
I clear the catalog and run my external method. My object does not get into the catalog. But from the indexes tab, when I browse the Subject index, I can see my content item listed with the values. Both res1 and res2 and empty.
Why is my content item not showing up inside the searchResuts() of the catalog?
Plone is a full-fat content management system, if you're after something lean it's probably not the right choice (perhaps try Pyramid.)
For your content type to be a full part of a Plone site it has to fulfil a number of requirements across the Zope2, CMF and Plone layers. plone.app.content.item.Item is about the simplest base class you can get for a content item for a Plone site, though a simpler base class in itself will not really make instances of your content type any more 'lean' - an instance of a class in Python is basically just a dict and a pointer to it's class.
Most of the work on a page view will be rendering the various user interface features of a site. Rendering the schema based add/edit forms of frameworks like Archetypes and Dexterity is also relatively expensive.
I'd spend a little time profiling your application using one of the supported content type systems before putting time into building your own.
In order to see your objects in the "Catalog" tab of the portal_catalog your objects need to have a "getPhysicalPath()" method that returns a tuple representing their path (ex. ('','Plone','myobject')).
Also try to use this:
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
as base class.
You need to register your type with the catalog multiplexer. Look at the configuration in the zmi -> archetypes_tool.
I'm not sure, but you may also need a portal_type registration also...
Like Lawrence said though, you're better off just using one of the current content type frameworks if you want to be able to catalog your data with plone's portal catalog. If you can deal with a separate catalog, take a look at repoze.catalog.
Plone needs every content object to provide an "allowedRolesAndUsers" index to return the object in searchResults.
There is probably a zcml snippet that will enable this for my content type. But I was able to get things working by adding another method as follows:
def allowedRolesAndUsers(self):
return ['Manager', 'Authenticated', 'Anonymous']
CatalogAware will be removed in Zope 4 and then can't be used any more.
cf https://github.com/zopefoundation/Products.ZCatalog/issues/26
I am customizing Plone Archetypes content type by overriding Schemata() method to have vocabulary contents dynamically:
def Schemata(self):
""" Overrides field definitions in fly.
"""
# XXX: Cache this method?
schemata = getSchemata(self).copy()
settings = self.getResearchSettings()
for row in settings.getFieldCustomizations():
name = row.get("fieldName", None)
vocab = row.get("vocabToUse", None)
field = schemata.get(name, None)
if field and vocab:
# Modify field copy ion
field.vocabulery = vocab
return schemata
Do I need to use cache decorator for Schemata() function or is Archetypes handling Schemata() internally so smart so that it calls it only once per request?
Plone 4.0.
forgot Schemata... you should not touch that stuff.
you can have dynamic vocabularies using object methods or zope vocabularies.
first hit on google
You definitely want to cache the results of Schema() and Schemata(), as at least Schema() is called multiple times during the same request.