Use collective.z3cform.rolefield dependency, removed useless code
This commit is contained in:
parent
c78c66186d
commit
36365a891a
|
@ -7,6 +7,8 @@
|
|||
.pydevproject
|
||||
.settings/
|
||||
bin/
|
||||
include/
|
||||
lib/
|
||||
develop-eggs/
|
||||
parts/
|
||||
src/*
|
||||
|
|
|
@ -8,3 +8,4 @@ auto-checkout +=
|
|||
plone.dexterity
|
||||
z3c.form
|
||||
plone.app.z3cform
|
||||
collective.z3cform.rolefield
|
||||
|
|
1
setup.py
1
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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
@ -36,13 +36,4 @@
|
|||
name="schema_policy_dmsfile"
|
||||
/>
|
||||
|
||||
<utility
|
||||
component="._field.LocalRolesToPrincipalsHandler"
|
||||
name="collective.dms.basecontent.LocalRolesToPrincipals"
|
||||
/>
|
||||
|
||||
<adapter
|
||||
factory="._field.LocalRolesToPrincipalsDataManager"
|
||||
/>
|
||||
|
||||
</configure>
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<metadata>
|
||||
<version>0001</version>
|
||||
<dependencies>
|
||||
<dependency>profile-plone.app.dexterity:default</dependency>
|
||||
<dependency>profile-plone.app.relationfield:default</dependency>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<object name="portal_types" meta_type="Plone Types Tool">
|
||||
<property name="title">Controls the available content types in your portal</property>
|
||||
<object name="testingtype" meta_type="Dexterity FTI"/>
|
||||
</object>
|
|
@ -1,55 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<object name="dmsdocument" meta_type="Dexterity FTI" i18n:domain="plone"
|
||||
xmlns:i18n="http://xml.zope.org/namespaces/i18n">
|
||||
<property name="title" i18n:translate="">TestingType</property>
|
||||
<property name="description" i18n:translate="">None</property>
|
||||
<property name="icon_expr">string:${portal_url}/folder_icon.png</property>
|
||||
<property name="factory">testingtype</property>
|
||||
<property name="add_view_expr">string:${folder_url}/++add++testingtype</property>
|
||||
<property name="link_target"></property>
|
||||
<property name="immediate_view">view</property>
|
||||
<property name="global_allow">True</property>
|
||||
<property name="filter_content_types">True</property>
|
||||
<property name="allowed_content_types">
|
||||
<element value="dmsmainfile"/>
|
||||
<element value="dmsappendixfile"/>
|
||||
</property>
|
||||
<property name="allow_discussion">False</property>
|
||||
<property name="default_view">view</property>
|
||||
<property name="view_methods">
|
||||
<element value="view"/>
|
||||
</property>
|
||||
<property name="default_view_fallback">False</property>
|
||||
<property name="add_permission">cmf.AddPortalContent</property>
|
||||
<property name="klass">plone.dexterity.content.Container</property>
|
||||
<property name="behaviors">
|
||||
<element value="plone.app.content.interfaces.INameFromTitle"/>
|
||||
<element value="plone.app.dexterity.behaviors.metadata.IBasic"/>
|
||||
</property>
|
||||
<property name="schema" />
|
||||
<property name="model_source">
|
||||
<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>
|
||||
</property>
|
||||
<alias from="(Default)" to="(dynamic view)"/>
|
||||
<alias from="edit" to="@@edit"/>
|
||||
<alias from="sharing" to="@@sharing"/>
|
||||
<alias from="view" to="(selected layout)"/>
|
||||
<action title="View" action_id="view" category="object" condition_expr=""
|
||||
description="" icon_expr="" link_target="" url_expr="string:${object_url}"
|
||||
visible="True">
|
||||
<permission value="View"/>
|
||||
</action>
|
||||
<action title="Edit" action_id="edit" category="object" condition_expr=""
|
||||
description="" icon_expr="" link_target=""
|
||||
url_expr="string:${object_url}/edit" visible="True">
|
||||
<permission value="Modify portal content"/>
|
||||
</action>
|
||||
</object>
|
|
@ -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',))
|
Reference in New Issue