line34
Coding, Scripting, Administration

A new property for groups

This took me a while to puzzle out: If you want your user groups in Plone to have a custom property (in my case an "alias" of type string), you create a PAS property plugin that implements IPropertiesPlugin.

from Products.PluggableAuthService.interfaces.plugins import IPropertiesPlugin
from Products.PluggableAuthService.plugins.BasePlugin import BasePlugin

class GroupAliasPlugin(BasePlugin):
""" Provides the property 'alias' for groups """
    meta_type = 'Group Alias Plugin'
    implements(IPropertiesPlugin)

Basically you only need to implement the method getPropertiesForUser. To make the property editable, it should return a MutablePropertySheet instance and you also should implement setPropertiesForUser.

from Products.PlonePAS.sheet import MutablePropertySheet
...
    def getPropertiesForUser(self, user, request=None):
        ...
        return MutablePropertySheet(self.id, schema=[('alias', 'string'),], **data)

    def setPropertiesForUser(self, user, propertysheet):
        ...

Now we need to make out plugin addable via the ZMI:

from App.special_dtml import DTMLFile

def manage_addGroupAliasPlugin(self, id, title='',
                               RESPONSE=None, schema=None, **kw):
    """ Add a Group Alias Plugin. Comes with custard or ice cream """
    o = GroupAliasPlugin(id, title, schema, **kw)
    self._setObject(o.getId(), o)

    if RESPONSE is not None:
        RESPONSE.redirect('manage_workspace')

manage_addGroupAliasPluginForm = DTMLFile('zmi/GroupAliasPluginForm', 
                                          globals(),
                                          __name__='manage_addGroupAliasPluginForm')

And in __init__.py:

from AccessControl.Permissions import add_user_folders
from Products.PluggableAuthService import registerMultiPlugin
from ... import groupalias

registerMultiPlugin(groupalias.GroupAliasPlugin.meta_type)

def initialize(context):
    """Initializer called when used as a Zope 2 product."""
    context.registerClass(groupalias.GroupAliasPlugin,
                         permission=add_user_folders,
                         constructors=(groupalias.manage_addGroupAliasPluginForm,
                                       groupalias.manage_addGroupAliasPlugin),
                         visibility=None)

We're almost there. To use the plugin, you have to add it to acl_users and activate it, AND you have to tell portal_groupdata that it should handle an additional property. If you want it active by default, you can do it in setuphandlers.py like this:

def addGroupProperties(context):
    """ Add a GroupDataPlugin to acl_users and add property to
        portal_groupdata
    """
    site = context.getSite()

    # most of this was borrowed from pas.plugins.ldap, thanks!
    pluginid = 'groupalias'
    pas = site.acl_users
    installed = pas.objectIds()
    if pluginid in installed:
        log.info("%s already installed." % pluginid)
    else:
        plugin = GroupAliasPlugin(pluginid, title="Group Alias Plugin")
        pas._setObject(pluginid, plugin)
        plugin = pas[plugin.getId()] # get plugin acquisition wrapped!
        for info in pas.plugins.listPluginTypeInfo():
            interface = info['interface']
            if not interface.providedBy(plugin):
                continue
            pas.plugins.activatePlugin(interface, plugin.getId())
            pas.plugins.movePluginsDown(
                interface,
                [x[0] for x in pas.plugins.listPlugins(interface)[:-1]],
            )

    # Our PAS property plugin already provides additional properties,
    # but we need to let portal_groupdata know about them.
    gd_tool = getToolByName(site, 'portal_groupdata')
    if gd_tool.hasProperty('alias'):
        gd_tool._delProperty('alias')
    gd_tool._setProperty('alias', '', 'string')
9th November 2014Filed under: Plone   user management   PAS