diff --git a/.gitignore b/.gitignore index 9f69d63..8cc088f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ .pydevproject .settings/ bin/ +include/ +lib/ develop-eggs/ parts/ src/* diff --git a/checkouts.cfg b/checkouts.cfg index cbc482e..f73c7e3 100644 --- a/checkouts.cfg +++ b/checkouts.cfg @@ -8,3 +8,4 @@ auto-checkout += plone.dexterity z3c.form plone.app.z3cform + collective.z3cform.rolefield diff --git a/setup.py b/setup.py index 0dab4af..00e2136 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ setup(name='collective.dms.basecontent', 'z3c.blobfile', 'plone.app.relationfield', 'plone.formwidget.contenttree', + 'collective.z3cform.rolefield', ], extras_require={ 'test': ['plone.app.testing', diff --git a/sources.cfg b/sources.cfg index 3d2c952..f0d9a64 100644 --- a/sources.cfg +++ b/sources.cfg @@ -17,6 +17,7 @@ entrouvert_push = git+ssh://git@repos.entrouvert.org [sources] collective.dms.thesaurus = git ${remotes:collective}/collective.dms.thesaurus.git pushurl=${remotes:collective_push}/collective.dms.thesaurus.git collective.dms.basecontent = git ${remotes:collective}/collective.dms.basecontent.git pushurl=${remotes:collective_push}/collective.dms.basecontent.git +collective.z3cform.rolefield = git ${remotes:collective}/collective.z3cform.rolefield.git pushurl=${remotes:collective_push}/collective.z3cform.rolefield.git plone.app.z3cform = git ${remotes:plone}/plone.app.z3cform.git pushurl=${remotes:plone_push}/plone.app.z3cform.git plone.dexterity = git ${remotes:cedricmessiant}/plone.dexterity.git pushurl=${remotes:cedricmessiant_push}/plone.dexterity.git z3c.form = git ${remotes:zopefoundation}/z3c.form pushurl=${remotes:zopefoundation_push}/z3c.form diff --git a/src/collective/dms/basecontent/__init__.py b/src/collective/dms/basecontent/__init__.py index 8248407..a627705 100644 --- a/src/collective/dms/basecontent/__init__.py +++ b/src/collective/dms/basecontent/__init__.py @@ -2,8 +2,6 @@ from zope.i18nmessageid import MessageFactory _ = MessageFactory("collective.dms.basecontent") -from ._field import LocalRolesToPrincipals - def initialize(context): """Initializer called when used as a Zope 2 product.""" diff --git a/src/collective/dms/basecontent/_field.py b/src/collective/dms/basecontent/_field.py deleted file mode 100644 index be7a0d1..0000000 --- a/src/collective/dms/basecontent/_field.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -from zope.component import adapts -from zope.interface import implementer -from zope.interface import Interface - -from zope.schema.interfaces import IList -from zope.schema import List, Tuple -from zope.interface import Invalid - -from z3c.form.datamanager import AttributeField - -import logging -logger = logging.getLogger('collective.dms.basecontent._field') - - -class ILocalRolesToPrincipals(IList): - """Field that list principals depending on a vocabulary (by default list every available groups) - and that assign local roles defined in the roles_to_assign attribute.""" - - # this attribute will contains a tuple of principal to assign when the value is set - roles_to_assign = Tuple( - title=u"Roles to assign", - description=u"""\ - Roles that will be automatically assigned as local roles to selected principals. - """, - required=True) - - -@implementer(ILocalRolesToPrincipals) -class LocalRolesToPrincipals(List): - """Field that list principals depending on a vocabulary (by default list every available groups) - and that assign local roles defined in the roles_to_assign attribute.""" - - def __init__(self, roles_to_assign=(), **kw): - self.roles_to_assign = roles_to_assign - super(LocalRolesToPrincipals, self).__init__(**kw) - - def validate(self, value): - """Check that we have roles to assign, this is mendatory and - that roles we want to assign actually exist.""" - super(LocalRolesToPrincipals, self)._validate(value) - - # the field must specify some roles to assign as this is a required value - if not self.roles_to_assign: - raise Invalid(u'The field is not configured correctly, roles_to_assign is required. Contact system administrator!') - - # check that roles we want to assign actually exist - existingRoles = [role for role in self.context.acl_users.portal_role_manager.listRoleIds()] - for role_to_assign in self.roles_to_assign: - if not role_to_assign in existingRoles: - raise Invalid(u'The field is not configured correctly, the defined role \'%s\' does not exist. Contact system administrator!' % role_to_assign) - - -class LocalRolesToPrincipalsDataManager(AttributeField): - """A data manager which set local roles when saving the field.""" - adapts(Interface, ILocalRolesToPrincipals) - - def set(self, value): - """See z3c.form.interfaces.IDataManager""" - # set local roles before setting the value so we still have access to the old value - roles_to_assign = self.field.roles_to_assign - # ---1 --- first find assigned roles to remove - # it is not that easy to remove local roles because no helper method exists for removing - # some specific local roles, only a method for removing every local roles for a list of principals... - old_value = self.field.get(self.context) or () - # now check between old_value and value (new value) what is missing - removed_principals = set(old_value).difference(set(value)) - # remove local_roles for removed_principals - for local_role in self.context.get_local_roles(): - # a local_role is like ('Administrators', (u'Contributor', u'Reviewer')) - principal = local_role[0] - if principal in removed_principals: - cleaned_local_roles = list(local_role[1]) - for role_to_assign in roles_to_assign: - try: - cleaned_local_roles.remove(role_to_assign) - except ValueError: - # if a role to remove was already removed (???) pass - logger.warn("Failed to remove role '%s' for principal '%s' on object '%s'" \ - % (role_to_assign, principal, '/'.join(self.context.getPhysicalPath()))) - # if there are still some local_roles, use manage_setLocalRoles - if cleaned_local_roles: - self.context.manage_setLocalRoles(principal, cleaned_local_roles) - else: - # either use manage_delLocalRoles - self.context.manage_delLocalRoles((principal,)) - # ---2 --- now add new local roles - added_principals = set(value).difference(set(old_value)) - for added_principal in added_principals: - self.context.manage_addLocalRoles(added_principal, roles_to_assign) - # finally set the value - super(LocalRolesToPrincipalsDataManager, self).set(value) - -import plone.supermodel.exportimport - -LocalRolesToPrincipalsHandler = plone.supermodel.exportimport.BaseHandler(LocalRolesToPrincipals) - - diff --git a/src/collective/dms/basecontent/configure.zcml b/src/collective/dms/basecontent/configure.zcml index c30e3ae..757442c 100644 --- a/src/collective/dms/basecontent/configure.zcml +++ b/src/collective/dms/basecontent/configure.zcml @@ -36,13 +36,4 @@ name="schema_policy_dmsfile" /> - - - - diff --git a/src/collective/dms/basecontent/dmsdocument.py b/src/collective/dms/basecontent/dmsdocument.py index 281c7ce..fa55c7e 100644 --- a/src/collective/dms/basecontent/dmsdocument.py +++ b/src/collective/dms/basecontent/dmsdocument.py @@ -9,7 +9,7 @@ from five import grok from collective.dms.basecontent.relateddocs import RelatedDocs from . import _ -from ._field import LocalRolesToPrincipals +from collective.z3cform.rolefield.field import LocalRolesToPrincipals from zope.schema.interfaces import IVocabularyFactory @@ -20,27 +20,27 @@ class IDmsDocument(model.Schema): notes = schema.Text( title=_(u"Notes"), required=False, - ) + ) treating_groups = LocalRolesToPrincipals( title=_(u"Treating groups"), required=False, roles_to_assign=('Editor',), value_type=schema.Choice(vocabulary=u'collective.dms.basecontent.treating_groups',) - ) + ) recipient_groups = LocalRolesToPrincipals( title=_(u"Recipient groups"), required=False, roles_to_assign=('Reader',), value_type=schema.Choice(vocabulary=u'collective.dms.basecontent.recipient_groups',) - ) + ) related_docs = RelatedDocs( title=_(u"Related documents"), required=False, - display_backrefs=True) - + display_backrefs=True, + ) class DmsDocument(Container): diff --git a/src/collective/dms/basecontent/profiles/testing/metadata.xml b/src/collective/dms/basecontent/profiles/testing/metadata.xml index b93fe26..000197f 100644 --- a/src/collective/dms/basecontent/profiles/testing/metadata.xml +++ b/src/collective/dms/basecontent/profiles/testing/metadata.xml @@ -1,8 +1,4 @@ 0001 - - profile-plone.app.dexterity:default - profile-plone.app.relationfield:default - diff --git a/src/collective/dms/basecontent/profiles/testing/types.xml b/src/collective/dms/basecontent/profiles/testing/types.xml deleted file mode 100644 index 44b57c4..0000000 --- a/src/collective/dms/basecontent/profiles/testing/types.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - Controls the available content types in your portal - - diff --git a/src/collective/dms/basecontent/profiles/testing/types/testingtype.xml b/src/collective/dms/basecontent/profiles/testing/types/testingtype.xml deleted file mode 100644 index a0f6f8b..0000000 --- a/src/collective/dms/basecontent/profiles/testing/types/testingtype.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - TestingType - None - string:${portal_url}/folder_icon.png - testingtype - string:${folder_url}/++add++testingtype - - view - True - True - - - - - False - view - - - - False - cmf.AddPortalContent - plone.dexterity.content.Container - - - - - - -<model xmlns="http://namespaces.plone.org/supermodel/schema"> - <schema> - <field name="testingField" type="collective.dms.basecontent.LocalRolesToPrincipals"> - <title>testingField</title> - <required>False</required> - <roles_to_assign>('Reader',)</roles_to_assign> - </field> - </schema> -</model> - - - - - - - - - - - - diff --git a/src/collective/dms/basecontent/tests/test_fields.py b/src/collective/dms/basecontent/tests/test_fields.py deleted file mode 100644 index ad879a0..0000000 --- a/src/collective/dms/basecontent/tests/test_fields.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf8 -*- - -import unittest2 as unittest - -from zope.interface import Invalid - -from plone.app.testing.helpers import setRoles - -from ecreall.helpers.testing.base import BaseTest - -from collective.dms.basecontent.testing import DMS_TESTS_PROFILE_FUNCTIONAL -from collective.dms.basecontent._field import LocalRolesToPrincipalsDataManager - - -class TestFields(unittest.TestCase, BaseTest): - """Tests adapters""" - - layer = DMS_TESTS_PROFILE_FUNCTIONAL - - def setUp(self): - super(TestFields, self).setUp() - self.portal = self.layer['portal'] - - def _getTargetClass(self): - from collective.dms.basecontent._field import LocalRolesToPrincipals - return LocalRolesToPrincipals - - def _makeOne(self, *args, **kw): - field = self._getTargetClass()(*args, **kw) - # this is needed to initialize the vocabulary - return field.bind(self.portal) - - def test_roles_to_assign_attribute(self): - """If the field is not correctly configured, it fails upon validation.""" - field = self._makeOne() - # the roles_to_assign attribute is required, if empty, validate fails - self.assertEquals(field.roles_to_assign, ()) - self.assertRaises(Invalid, field.validate, []) - # if we want to assign role but one does not exist, validate fails too - field = self._makeOne(roles_to_assign=('Editor', 'WrongRole',)) - self.assertRaises(Invalid, field.validate, []) - # if we have valid values, it works like a charm ;-) - field = self._makeOne(roles_to_assign=('Editor', 'Reader',)) - field.validate([]) - - def test_datamanager(self): - """Test the local_roles assignment mechanism managed by the datamanager.""" - testingfield = self.portal.portal_types.testingtype.lookupSchema()['testingField'] - testingfield.roles_to_assign = ('Editor', 'Contributor',) - # first create a sample object - # make the default user a Manager - member = self.portal.portal_membership.getAuthenticatedMember() - setRoles(self.portal, member.getId(), ('Manager',)) - # create an object - self.portal.invokeFactory('testingtype', id='testingobj') - testingobj = getattr(self.portal, 'testingobj') - datamanager = LocalRolesToPrincipalsDataManager(testingobj, testingfield) - self.failIf('Administrators' in testingobj.__ac_local_roles__.keys()) - datamanager.set(('Administrators',)) - # now we have local_roles for 'Administrators' - self.failUnless('Administrators' in testingobj.__ac_local_roles__.keys()) - # moreover, local_roles for 'Administrators' are ('Editor', 'Contributor',) - self.assertEquals(tuple(testingobj.__ac_local_roles__['Administrators']), testingfield.roles_to_assign) - # add a principal, test that local_roles are adapted - self.failIf('Reviewers' in testingobj.__ac_local_roles__.keys()) - # the value is now ('Administrators', 'Reviewers',) - datamanager.set(('Administrators', 'Reviewers',)) - self.failUnless('Reviewers' in testingobj.__ac_local_roles__.keys()) - self.assertEquals(tuple(testingobj.__ac_local_roles__['Reviewers']), testingfield.roles_to_assign) - # remove a group, check managed roles - # removing a group is made by passing new value where an existing group is no more present - self.failUnless('Administrators' in testingobj.__ac_local_roles__.keys()) - datamanager.set(('Reviewers',)) - self.failIf('Administrators' in testingobj.__ac_local_roles__.keys()) - # if an external manipulation added a local_role not managed by the field, it is kept - # add a local_role for 'Reviewers' not managed by our field - self.assertEquals(tuple(testingobj.__ac_local_roles__['Reviewers']), testingfield.roles_to_assign) - testingobj.manage_addLocalRoles('Reviewers', ('Reader',)) - self.assertEquals(tuple(testingobj.__ac_local_roles__['Reviewers']), testingfield.roles_to_assign + ('Reader',)) - # remove the 'Reviewers' local_roles - datamanager.set(()) - # not managed local_roles are kepts - self.assertEquals(tuple(testingobj.__ac_local_roles__['Reviewers']), ('Reader',))