implement workspace-based security management

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3891 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2010-06-12 10:51:17 +00:00
parent 6fbf8a08ae
commit 3d7c87b7b9
18 changed files with 313 additions and 90 deletions

View file

@ -120,13 +120,17 @@ class BaseView(GenericView, I18NView):
self.context = removeSecurityProxy(context)
try:
if not self.checkPermissions():
raise Unauthorized('%r: title' % (self.context))
raise Unauthorized(str(self.contextInfo))
except ForbiddenAttribute: # ignore when testing
pass
def checkPermissions(self):
return canAccessObject(self.context)
@Lazy
def contextInfo(self):
return dict(view=self, context=getName(self.context))
@Lazy
def conceptMacros(self):
return concept_macros.macros

View file

@ -554,7 +554,7 @@
name="create_object.html"
for="loops.interfaces.INode"
class="loops.browser.form.CreateObjectForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
@ -568,49 +568,49 @@
name="edit_object.html"
for="loops.interfaces.INode"
class="loops.browser.form.EditObjectForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="create_concept.html"
for="loops.interfaces.INode"
class="loops.browser.form.CreateConceptForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="edit_concept.html"
for="loops.interfaces.INode"
class="loops.browser.form.EditConceptForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="edit_concept_page.html"
for="loops.interfaces.INode"
class="loops.browser.form.EditConceptPage"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="inner_form.html"
for="loops.interfaces.INode"
class="loops.browser.form.InnerForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="inner_concept_form.html"
for="loops.interfaces.INode"
class="loops.browser.form.InnerConceptForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<page
name="inner_concept_edit_form.html"
for="loops.interfaces.INode"
class="loops.browser.form.InnerConceptEditForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<zope:adapter
@ -618,7 +618,7 @@
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.CreateObject"
permission="zope.ManageContent"
permission="zope.View"
/>
<zope:adapter
@ -626,7 +626,7 @@
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.EditObject"
permission="zope.ManageContent"
permission="zope.View"
/>
<zope:adapter
@ -634,14 +634,14 @@
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.CreateConcept"
permission="zope.ManageContent" />
permission="zope.View" />
<zope:adapter
name="edit_concept"
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.browser.form.EditConcept"
permission="zope.ManageContent" />
permission="zope.View" />
<!-- inner HTML views -->

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -35,7 +35,9 @@ from zope.cachedescriptors.property import Lazy
from zope.contenttype import guess_content_type
from zope.publisher.browser import FileUpload
from zope.publisher.interfaces import BadRequest
from zope.security.interfaces import ForbiddenAttribute, Unauthorized
from zope.security.proxy import isinstance, removeSecurityProxy
from zope.traversing.api import getName
from cybertools.ajax import innerHtml
from cybertools.browser.form import FormController
@ -58,6 +60,7 @@ from loops.i18n.browser import I18NView
from loops.query import ConceptQuery, IQueryConcept
from loops.resource import Resource
from loops.schema.field import relation_macros
from loops.security.common import canAccessObject, canListObject, canWriteObject
from loops.type import ITypeConcept
from loops import util
from loops.util import _
@ -77,12 +80,20 @@ class ObjectForm(NodeView):
isPopup = False
showAssignments = True
def __init__(self, context, request):
super(ObjectForm, self).__init__(context, request)
# target is the object the view acts upon - this is not necessarily
# the same object as the context (the object the view was created for)
self.target = context
#self.registerDojoForm()
def checkPermissions(self):
obj = self.target
if obj is None:
obj = self.context
return canWriteObject(obj)
@Lazy
def target(self):
return self.virtualTargetObject or self.context
@Lazy
def contextInfo(self):
return dict(view=self, context=getName(self.context),
target=getName(self.target))
def closeAction(self, submit=False):
if self.isPopup:
@ -196,18 +207,13 @@ class ObjectForm(NodeView):
class EditObjectForm(ObjectForm):
@Lazy
def macro(self):
return self.template.macros['edit']
title = _(u'Edit Resource')
form_action = 'edit_resource'
dialog_name = 'edit'
def __init__(self, context, request):
super(EditObjectForm, self).__init__(context, request)
#self.url = self.url # keep virtual target URL (???)
self.target = self.virtualTargetObject
@Lazy
def macro(self):
return self.template.macros['edit']
@property
def assignments(self):
@ -251,13 +257,13 @@ class EditConceptPage(EditConceptForm):
class CreateObjectForm(ObjectForm):
@property
def macro(self): return self.template.macros['create']
defaultTitle = u'Create Resource, Type = '
form_action = 'create_resource'
dialog_name = 'create'
@property
def macro(self): return self.template.macros['create']
@Lazy
def fixedType(self):
return self.request.form.get('fixed_type')
@ -445,6 +451,25 @@ class EditObject(FormController, I18NView):
prefix = 'form.'
conceptPrefix = 'assignments.'
def __init__(self, context, request):
super(EditObject, self).__init__(context, request)
try:
if not self.checkPermissions():
raise Unauthorized(str(self.contextInfo))
except ForbiddenAttribute: # ignore when testing
pass
def checkPermissions(self):
return canWriteObject(self.target)
@Lazy
def contextInfo(self):
return dict(formcontroller=self, view=self.view, target=getName(self.target))
@Lazy
def target(self):
return self.view.virtualTargetObject or self.context
@Lazy
def adapted(self):
return adapted(self.object, self.languageInfoForUpdate)
@ -476,7 +501,7 @@ class EditObject(FormController, I18NView):
def update(self):
# create new version if necessary
target = self.view.virtualTargetObject
target = self.target
obj = self.checkCreateVersion(target)
if obj != target:
# make sure new version is used by the view

View file

@ -176,12 +176,15 @@ standard checker defined in the test setup.
The automatic assignment of the blog post is done in the form controller
used for creating the blog post.
>>> home = views['home']
>>> home.target = myBlog
>>> from loops.compound.blog.browser import CreateBlogPostForm, CreateBlogPost
>>> input = {'title': u'John\'s first post', 'text': u'Text of John\'s post',
... 'date': '2008-02-02T15:54:11',
... 'privateComment': u'John\'s private comment',
... 'form.type': '.loops/concepts/blogpost'}
>>> cbpForm = CreateBlogPostForm(myBlog, TestRequest(form=input))
>>> cbpForm = CreateBlogPostForm(home, TestRequest(form=input))
>>> cbpController = CreateBlogPost(cbpForm, cbpForm.request)
>>> cbpController.update()
False

View file

@ -36,7 +36,7 @@ from loops.browser.form import CreateConceptForm, EditConceptForm
from loops.browser.form import CreateConcept, EditConcept
from loops.common import adapted
from loops.organize.party import getPersonForUser
from loops.security.common import checkPermission
from loops.security.common import checkPermission, canAccessObject
from loops import util
from loops.util import _
@ -175,20 +175,30 @@ class EditBlogPostForm(EditConceptForm):
title = _(u'Edit Blog Post')
form_action = 'edit_blogpost'
def checkPermissions(self):
return canAccessObject(self.target)
class CreateBlogPostForm(CreateConceptForm):
title = _(u'Create Blog Post')
form_action = 'create_blogpost'
def checkPermissions(self):
return canAccessObject(self.target)
class EditBlogPost(EditConcept):
pass
def checkPermissions(self):
return canAccessObject(self.target)
class CreateBlogPost(CreateConcept):
def checkPermissions(self):
return canAccessObject(self.target)
def collectAutoConcepts(self):
#super(CreateBlogPost, self).collectConcepts(fieldName, value)
person = getPersonForUser(self.container, self.request)

View file

@ -46,14 +46,14 @@
name="create_blogpost.html"
for="loops.interfaces.INode"
class="loops.compound.blog.browser.CreateBlogPostForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<browser:page
name="edit_blogpost.html"
for="loops.interfaces.INode"
class="loops.compound.blog.browser.EditBlogPostForm"
permission="zope.ManageContent"
permission="zope.View"
/>
<zope:adapter
@ -62,7 +62,7 @@
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.compound.blog.browser.CreateBlogPost"
permission="zope.ManageContent"
permission="zope.View"
/>
<zope:adapter
@ -70,7 +70,7 @@
for="loops.browser.node.NodeView
zope.publisher.interfaces.browser.IBrowserRequest"
factory="loops.compound.blog.browser.EditBlogPost"
permission="zope.ManageContent"
permission="zope.View"
/>
</configure>

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: $Id$\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
"PO-Revision-Date: 2010-05-16 12:00 CET\n"
"PO-Revision-Date: 2010-06-11 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
@ -159,16 +159,16 @@ msgid "Create a new event"
msgstr "Einen neuen Termin anlegen"
msgid "Create Task..."
msgstr "Aufgabe anlegen..."
msgstr "Crear tarea..."
msgid "Create a new task"
msgstr "Eine neue Aufgabe anlegen"
msgstr "Crear una nueva tarea"
msgid "Edit Task..."
msgstr "Aufgabe bearbeiten..."
msgstr "Editar tarea..."
msgid "Modify task"
msgstr "Aufgabe bearbeiten"
msgstr "Modificar la tarea"
msgid "Create Work Item..."
msgstr "Aktivität anlegen..."
@ -243,7 +243,7 @@ msgid "Topic"
msgstr "Thema"
msgid "Task"
msgstr "Aufgabe"
msgstr "Tarea"
msgid "Domain"
msgstr "Bereich"

View file

@ -35,6 +35,7 @@ from loops.browser.node import NodeView
from loops.organize.comment.base import Comment
from loops.organize.party import getPersonForUser
from loops.organize.tracking.report import TrackDetails
from loops.security.common import canAccessObject
from loops.setup import addObject
from loops import util
from loops.util import _
@ -91,6 +92,9 @@ class CreateCommentForm(ObjectForm):
template = comment_macros
def checkPermissions(self):
return canAccessObject(self.target)
@Lazy
def macro(self):
return self.template.macros['create_comment']
@ -98,6 +102,9 @@ class CreateCommentForm(ObjectForm):
class CreateComment(EditObject):
def checkPermissions(self):
return canAccessObject(self.target)
@Lazy
def personId(self):
p = getPersonForUser(self.context, self.request)

View file

@ -58,8 +58,15 @@ def getPrincipalFolder(context=None, authPluginId=None, ignoreErrors=False):
return plugin
def getGroupsFolder(context=None, name='gloops'):
return getPrincipalFolder(authPluginId=name, ignoreErrors=True)
def getGroupsFolder(context=None, name='gloops', create=False):
gf = getPrincipalFolder(authPluginId=name, ignoreErrors=True)
if gf is None and create:
pau = component.getUtility(IAuthentication, context=context)
gf = pau[name] = PrincipalFolder()
gf.prefix = name + '.'
pau.authenticatorPlugins = tuple(
list(pau.authenticatorPlugins) + ['name'])
return gf
def getGroupId(group):

View file

@ -47,6 +47,7 @@ from loops.organize.stateful.browser import StateAction
from loops.organize.tracking.browser import BaseTrackView
from loops.organize.tracking.report import TrackDetails
from loops.organize.work.base import WorkItem
from loops.security.common import canAccessObject, canListObject, canWriteObject
from loops import util
from loops.util import _
@ -277,10 +278,19 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
template = work_macros
def checkPermissions(self):
return canAccessObject(self.task or self.target)
@Lazy
def macro(self):
return self.template.macros['create_workitem']
@Lazy
def task(self):
uid = self.track.taskId
if uid:
return util.getObjectForUid(uid)
@Lazy
def track(self):
id = self.request.form.get('id')
@ -352,6 +362,17 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
class CreateWorkItem(EditObject, BaseTrackView):
def checkPermissions(self):
return canAccessObject(self.task or self.target)
@Lazy
def task(self):
if self.track is None:
return None
uid = self.track.taskId
if uid:
return util.getObjectForUid(uid)
@Lazy
def track(self):
id = self.request.form.get('id')
@ -368,7 +389,8 @@ class CreateWorkItem(EditObject, BaseTrackView):
@Lazy
def object(self):
return self.view.virtualTargetObject
return self.target
#return self.view.virtualTargetObject
def processForm(self):
form = self.request.form

View file

@ -50,7 +50,7 @@
name="create_workitem.html"
for="loops.interfaces.INode"
class="loops.organize.work.browser.CreateWorkItemForm"
permission="zope.ManageContent" />
permission="zope.View" />
<zope:adapter
name="create_workitem"

View file

@ -31,6 +31,10 @@
id="loops.EditRestricted"
title="[loops-edit-restricted-permission] loops: Edit Restricted Information" />
<permission
id="loops.AssignAsParent"
title="[loops-assign-as-parent-permission] loops: Assign as Parent" />
<permission
id="loops.Execute"
title="[loops-execute-permission] loops: Execute" />
@ -42,13 +46,19 @@
<grant role="loops.SiteManager" permission="loops.ManageSite" />
<grant role="loops.SiteManager" permission="loops.ManageTypes" />
<grant role="loops.SiteManager" permission="loops.ManageWorkspaces" />
<grant role="loops.SiteManager" permission="loops.AssignAsParent" />
<grant role="loops.SiteManager" permission="loops.xmlrpc.ManageConcepts" />
<grant role="loops.SiteManager" permission="zope.ManageContent" />
<grant role="loops.SiteManager" permission="zope.View" />
<role id="loops.xmlrpc.ConceptManager"
title="[xmlrpc-manage-concepts-role] loops: Concept Manager (XML-RPC)" />
<grant role="loops.xmlrpc.ConceptManager" permission="loops.xmlrpc.ManageConcepts" />
<role id="loops.Staff"
title="[loops-staff-role] loops: Staff" />
<grant role="loops.Staff" permission="loops.ManageWorkspaces" />
<grant role="loops.Staff" permission="loops.AssignAsParent" />
<grant role="loops.Staff" permission="loops.EditRestricted" />
<grant role="loops.Staff" permission="zope.ManageContent" />
<grant role="loops.Staff" permission="zope.View" />
@ -56,19 +66,19 @@
<role id="loops.Master"
title="[loops-master-role] loops: Master" />
<grant role="loops.Master" permission="zope.ManageContent" />
<grant role="loops.Master" permission="loops.AssignAsParent" />
<role id="loops.Member"
title="[loops-member-role] loops: Member" />
<grant role="loops.Member" permission="zope.View" />
<role id="loops.xmlrpc.ConceptManager"
title="[xmlrpc-manage-concepts-role] loops: Concept Manager (XML-RPC)" />
<grant role="loops.xmlrpc.ConceptManager" permission="loops.xmlrpc.ManageConcepts" />
<role id="loops.Owner"
title="[loops-owner-role] Owner" />
<grant role="loops.Owner" permission="zope.ManageContent" />
<grant role="loops.Owner" permission="loops.ViewRestricted" />
<grant role="loops.Owner" permission="zope.View" />
<!-- moved to etc/securitypolicy.zcml: -->
<!--<grant role="zope.ContentManager" permission="loops.AssignAsParent" />-->
</configure>

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -22,8 +22,10 @@ Security-related views.
$Id$
"""
from zope.app.authentication.groupfolder import GroupInformation
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.app.security.interfaces import IPermission
from zope.app.security.settings import Allow, Deny, Unset
from zope.app.securitypolicy.browser import granting
from zope.app.securitypolicy.browser.rolepermissionview import RolePermissionView
from zope.app.securitypolicy.interfaces import IPrincipalRoleManager, \
@ -32,13 +34,17 @@ from zope.app.securitypolicy.interfaces import IPrincipalPermissionManager, \
IPrincipalPermissionMap
from zope.app.securitypolicy.zopepolicy import SettingAsBoolean
from zope import component
from zope.event import notify
from zope.interface import implements
from zope.cachedescriptors.property import Lazy
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.security.proxy import removeSecurityProxy
from zope.traversing.api import getParent, getParents
from zope.traversing.api import getName, getParent, getParents
from loops.common import adapted
from loops.organize.util import getGroupsFolder
from loops.security.common import WorkspaceInformation
from loops.security.common import localPermissions, localRoles, setPrincipalRole
from loops.security.interfaces import ISecuritySetter
@ -162,7 +168,11 @@ class PermissionView(object):
return result
def getPermissions(self):
return sorted(name for name, perm in component.getUtilitiesFor(IPermission))
return sorted(name for name, perm in component.getUtilitiesFor(IPermission)
if name in localPermissions)
def hideRole(self, role):
return role not in localRoles
class ManageWorkspaceView(PermissionView):
@ -176,12 +186,86 @@ class ManageWorkspaceView(PermissionView):
wi = context.workspaceInformation = WorkspaceInformation(context)
PermissionView.__init__(self, wi, request)
def update(self, testing=None):
if 'SUBMIT_PERMS' in self.request.form:
super(ManageWorkspaceView, self).update(testing)
elif 'save_wsinfo' in self.request.form:
self.saveWSInfo()
def saveWSInfo(self):
gn = {}
form = self.request.form
gfName = self.context.workspaceGroupsFolderName
gf = getGroupsFolder(self.context, gfName, create=True)
parentRM = IPrincipalRoleManager(self.parent)
wsiRM = IPrincipalRoleManager(self.context)
for pn in form.get('predicate_name', []):
groupName = form.get('group_name_' + pn)
gn[pn] = groupName
if groupName and groupName not in gf:
group = GroupInformation(groupName)
notify(ObjectCreatedEvent(group))
gf[groupName] = group
notify(ObjectModifiedEvent(group))
roleParent = bool(form.get('role_parent_' + pn))
roleWSI = bool(form.get('role_wsi_' + pn))
roleName = 'loops.' + pn.lstrip('is').title()
gid = '.'.join((gfName, groupName))
setPrincipalRole(parentRM, roleName, gid,
roleParent and Allow or None)
setPrincipalRole(wsiRM, roleName, gid,
roleWSI and Allow or None)
self.context.workspaceGroupNames = gn
@Lazy
def permission_macros(self):
return permission_template.macros
@Lazy
def parent(self):
return self.context.getParent()
@Lazy
def adapted(self):
return adapted(getParent(self.context))
return adapted(self.parent)
def getGroupsInfo(self):
root = self.parent.getLoopsRoot()
conceptManager = root.getConceptManager()
def getDefaultGroupName(predicateName):
rootName = '_'.join([getName(obj) for obj in
reversed(getParents(conceptManager)[:-1])])
objName = getName(self.parent)
return '.'.join((rootName, objName, predicateName.strip('is')))
apn = [pn for pn in self.context.allocationPredicateNames
if pn in conceptManager]
gn = self.context.workspaceGroupNames
if not isinstance(gn, dict): # backwards compatibility
gn = {}
result = [dict(predicateName=pn,
predicateTitle=conceptManager[pn].title,
groupName='', groupExists=False,
roleParent=False, roleWSI=False)
for pn in apn]
gfName = self.context.workspaceGroupsFolderName
gf = getGroupsFolder(self.context, gfName)
if gf is None:
return result
parentRMget = IPrincipalRoleManager(self.parent).getPrincipalsForRole
wsiRMget = IPrincipalRoleManager(self.context).getPrincipalsForRole
for item in result:
pn = item['predicateName']
groupName = item['groupName'] = gn.get(pn, getDefaultGroupName(pn))
roleName = 'loops.' + pn.lstrip('is').title()
if gf is not None and groupName in gf:
item['groupExists'] = True
gid = '.'.join((gfName, groupName))
item['roleParent'] = isSet(parentRMget(roleName), gid)
item['roleWSI'] = isSet(wsiRMget(roleName), gid)
return result
def isSet(entry, id):
for name, setting in entry:
if name == id:
return SettingAsBoolean[setting]

View file

@ -1,5 +1,5 @@
#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# Copyright (c) 2010 Helmut Merz helmutm@cy55.de
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -23,7 +23,6 @@ $Id$
"""
from persistent import Persistent
from persistent.list import PersistentList
from zope import component
from zope.annotation.interfaces import IAttributeAnnotatable
from zope.app.container.interfaces import IObjectAddedEvent
@ -52,6 +51,15 @@ allRolesExceptOwner = (
'loops.Member', 'loops.Master',)
allRolesExceptOwnerAndMaster = tuple(allRolesExceptOwner[:-1])
minorPrivilegedRoles = ('zope.Anonymous', 'zope.Member',)
localRoles = ('zope.Anonymous', 'zope.Member', 'zope.ContentManager',
'loops.Staff', 'loops.Member', 'loops.Master', 'loops.Owner')
localPermissions = ('zope.ManageContent', 'zope.View', 'loops.ManageWorkspaces',
'loops.ViewRestricted', 'loops.EditRestricted', 'loops.AssignAsParent',)
allocationPredicateNames = ('ismaster', 'ismember')
workspaceGroupsFolderName = 'gloops_ws'
# checking and querying functions
@ -70,6 +78,9 @@ def canWriteObject(obj):
def canEditRestricted(obj):
return checkPermission('loops.EditRestricted', obj)
def canAssignAsParent(obj):
return checkPermission('loops.AssignAsParent', obj)
def checkPermission(permission, obj):
return baseCheckPermission(permission, obj)
@ -174,11 +185,15 @@ class WorkspaceInformation(Persistent):
__name__ = u'workspace_information'
propagateRolePermissions = 'workspace'
allocationPredicateNames = allocationPredicateNames
workspaceGroupsFolderName = workspaceGroupsFolderName
def __init__(self, parent):
self.__parent__ = parent
self.workspaceGroupNames = PersistentList()
self.workspaceGroupNames = {}
def getName(self):
return self.__name__
def getParent(self):
return self.__parent__

View file

@ -70,6 +70,6 @@ class IWorkspaceInformation(Interface):
security-related stuff for sub-objects.
"""
propagateRolePermissions = Attribute('Which role permissions should be '
'propagated to children?')
propagateRolePermissions = Attribute('Whose role permissions should be '
'propagated to children (workspace_informaton or parent)?')

View file

@ -37,37 +37,32 @@
tal:attributes="value permId" />
<div class="form-element">
<table width="100%" cellspacing="0" cellpadding="2" border="0"
<table class="listing" cellspacing="0" cellpadding="2" border="0"
nowrap="nowrap">
<tr class="list-header">
<td><strong i18n:translate="">Role</strong></td>
<td><strong i18n:translate="">Users/Groups</strong></td>
<td><strong i18n:translate="">Acquired Setting</strong></td>
<td><strong i18n:translate="">Setting</strong></td>
<th i18n:translate="">Role</th>
<th i18n:translate="">Users/Groups</th>
<th i18n:translate="">Acquired Setting</th>
<th i18n:translate="">Setting</th>
</tr>
<tal:role tal:repeat="setting perm/roleSettings">
<tr class="row-normal"
tal:repeat="setting perm/roleSettings"
tal:attributes="class python:
path('repeat/setting/even') and 'row-normal' or 'row-hilite'">
<tal:role define="ir repeat/setting/index;
roleId python:path('view/roles')[ir].id">
tal:define="ir repeat/setting/index;
roleId python:path('view/roles')[ir].id"
tal:attributes="style python:view.hideRole(roleId) and
'visibility: collapse' or ''">
<td align="left" valign="top"
tal:content="roleId">
Manager
</td>
tal:content="roleId" />
<td>
<span tal:define="users python: view.listUsersForRole(roleId)"
tal:replace="structure users">
User xy
</span>
</td>
<td>
tal:replace="structure users" /></td>
<td class="center">
<span tal:replace="python:
view.getAcquiredPermissionSetting(roleId, permId)" />
</td>
<td>
<td class="center">
<select name="settings:list">
<option value="Unset"
tal:repeat="option view/availableSettings"
@ -77,8 +72,8 @@
i18n:translate="">+</option>
</select>
</td>
</tal:role>
</tr>
</tal:role>
<tr tal:define="principals view/getPrincipalPermissions"
tal:condition="principals">
<td>

View file

@ -2,10 +2,51 @@
i18n:domain="zope">
<body>
<div metal:fill-slot="body" i18n:domain="zope">
<h2 i18n:translate="">Assign Permissions to Roles for Children of this Object</h2>
<h1 i18n:translate="">Define Workspace Properties</h1>
<p tal:define="status view/update"
tal:content="status" i18n:translate=""/>
<form method="post">
<table class="listing">
<tr>
<th colspan="3"></th>
<th colspan="2">Assign role in</th>
</tr>
<tr>
<th>Predicate</th>
<th>Group name</th>
<th>Exists</th>
<th>Parent</th>
<th>WS Info</th>
</tr>
<tr tal:repeat="gi view/getGroupsInfo">
<td>
<input type="hidden" name="predicate_name:list"
tal:attributes="value gi/predicateName" />
<span tal:content="gi/predicateTitle" />
</td>
<td>
<input type="text" size="40"
tal:attributes="name string:group_name_${gi/predicateName};
value gi/groupName">
</td>
<td class="center"
tal:content="python:gi['groupExists'] and 'yes' or 'no'" />
<td class="center">
<input type="checkbox" value="true"
tal:attributes="name string:role_parent_${gi/predicateName};
checked gi/roleParent" /></td>
<td class="center">
<input type="checkbox" value="true"
tal:attributes="name string:role_wsi_${gi/predicateName};
checked gi/roleWSI" /></td>
</tr>
</table>
<br />
<input type="submit" name="save_wsinfo" value="Save Settings" />
</form>
<br />
<h2 i18n:translate="">Assign Permissions to Roles for Children of this Object</h2>
<br />
<metal:permissions use-macro="view/permission_macros/permission_form" />
</div>