From 0d7afb685f845bcdeea8e5379f14906c48616dd5 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Fri, 1 Jun 2012 15:57:19 +0200 Subject: [PATCH] [gen] added obj.mayEdit, an additional condition for editing an object (similar to mayDelete); bugfix: specifying a workflow for a User class crashed because, in installer.py, Appy took into account the standard workflow on this Class instead of the custom one. --- gen/__init__.py | 3 --- gen/installer.py | 4 ++-- gen/mixins/ToolMixin.py | 13 ++++++++----- gen/mixins/__init__.py | 24 ++++++++++++++++-------- gen/ui/page.pt | 2 +- gen/ui/result.pt | 4 ++-- gen/ui/template.pt | 3 ++- gen/ui/widgets/ref.pt | 4 ++-- gen/wrappers/__init__.py | 17 ++++++++++++++++- 9 files changed, 49 insertions(+), 25 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index 3883594..8f835a1 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -2791,7 +2791,6 @@ class WorkflowAnonymous: o = 'Owner' active = State({r:(mgr, 'Anonymous', 'Authenticated'), w:(mgr,o),d:(mgr,o)}, initial=True) -WorkflowAnonymous.__instance__ = WorkflowAnonymous() class WorkflowAuthenticated: '''One-state workflow allowing authenticated users to consult and Manager @@ -2800,7 +2799,6 @@ class WorkflowAuthenticated: o = 'Owner' active = State({r:(mgr, 'Authenticated'), w:(mgr,o), d:(mgr,o)}, initial=True) -WorkflowAuthenticated.__instance__ = WorkflowAuthenticated() class WorkflowOwner: '''One-state workflow allowing only manager and owner to consult and @@ -2808,7 +2806,6 @@ class WorkflowOwner: mgr = 'Manager' o = 'Owner' active = State({r:(mgr, o), w:(mgr, o), d:mgr}, initial=True) -WorkflowOwner.__instance__ = WorkflowOwner() # ------------------------------------------------------------------------------ class Selection: diff --git a/gen/installer.py b/gen/installer.py index 50a262c..a0048ac 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -368,8 +368,8 @@ class ZopeInstaller: constructors = (ctor,), permission = self.addContentPermissions[name]) # Create workflow prototypical instances in __instance__ attributes - wf = getattr(klass.wrapperClass, 'workflow', None) - if wf and not hasattr(wf, '__instance__'): wf.__instance__ = wf() + wf = wrapper.getWorkflow() + if not hasattr(wf, '__instance__'): wf.__instance__ = wf() def installAppyTypes(self): '''We complete here the initialisation process of every Appy type of diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 53982c9..4a20344 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -992,14 +992,17 @@ class ToolMixin(BaseMixin): elem is the one-line user info as shown on every page; second line is the URL to edit user info.''' appyUser = self.appy().appyUser - res = [appyUser.title] + info = [appyUser.title] rolesToShow = [r for r in appyUser.roles \ if r not in ('Authenticated', 'Member')] if rolesToShow: - res.append(', '.join([self.translate('role_%s'%r) \ - for r in rolesToShow])) - return (' | '.join(res), appyUser.o.getUrl(mode='edit', page='main', - nav='')) + info.append(', '.join([self.translate('role_%s'%r) \ + for r in rolesToShow])) + # Edit URL for the appy user. + url = None + if appyUser.o.mayEdit(): + url = appyUser.o.getUrl(mode='edit', page='main', nav='') + return (' | '.join(info), url) def generateUid(self, className): '''Generates a UID for an instance of p_className.''' diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index 9c87ac8..28e7d87 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -879,12 +879,10 @@ class BaseMixin: '''Returns the workflow applicable for p_self (or for any instance of p_className if given), or its name, if p_name is True.''' if not className: - appyClass = self.wrapperClass.__bases__[-1] + wrapperClass = self.wrapperClass else: - appyClass = self.getTool().getAppyClass(className) - if hasattr(appyClass, 'workflow'): wf = appyClass.workflow - else: - wf = gen.WorkflowAnonymous + wrapperClass = self.getTool().getAppyClass(className, wrapper=True) + wf = wrapperClass.getWorkflow() if not name: return wf return WorkflowDescriptor.getWorkflowName(wf) @@ -957,13 +955,23 @@ class BaseMixin: return True def mayDelete(self): - '''May the currently logged user delete this object? This condition - comes as an addition/refinement to the corresponding workflow - permission.''' + '''May the currently logged user delete this object?.''' + res = self.allows('Delete objects') + if not res: return + # An additional, user-defined condition, may refine the base permission. appyObj = self.appy() if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete() return True + def mayEdit(self): + '''May the currently logged user edit this object?.''' + res = self.allows('Modify portal content') + if not res: return + # An additional, user-defined condition, may refine the base permission. + appyObj = self.appy() + if hasattr(appyObj, 'mayEdit'): return appyObj.mayEdit() + return True + def executeAppyAction(self, actionName, reindex=True): '''Executes action with p_fieldName on this object.''' appyType = self.getAppyType(actionName) diff --git a/gen/ui/page.pt b/gen/ui/page.pt index b30338b..c8d6aef 100644 --- a/gen/ui/page.pt +++ b/gen/ui/page.pt @@ -285,7 +285,7 @@ + tal:condition="contextObj/mayEdit"/> diff --git a/gen/ui/result.pt b/gen/ui/result.pt index 6629155..4de78b6 100644 --- a/gen/ui/result.pt +++ b/gen/ui/result.pt @@ -124,12 +124,12 @@ + tal:condition="obj/mayEdit"> Delete the element - diff --git a/gen/ui/template.pt b/gen/ui/template.pt index d61044d..0b9f73f 100644 --- a/gen/ui/template.pt +++ b/gen/ui/template.pt @@ -143,7 +143,8 @@ - + diff --git a/gen/ui/widgets/ref.pt b/gen/ui/widgets/ref.pt index 55cb3a6..1e9c741 100644 --- a/gen/ui/widgets/ref.pt +++ b/gen/ui/widgets/ref.pt @@ -40,7 +40,7 @@ Edit the element - + @@ -48,7 +48,7 @@ Delete the element - diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index 02a571a..394ed85 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -4,7 +4,7 @@ # ------------------------------------------------------------------------------ import os, os.path, mimetypes import appy.pod -from appy.gen import Type, Search, Ref, String +from appy.gen import Type, Search, Ref, String, WorkflowAnonymous from appy.gen.utils import createObject from appy.shared.utils import getOsTempFolder, executeCommand, \ normalizeString, sequenceTypes @@ -87,6 +87,21 @@ class AbstractWrapper(object): return customUser.__dict__[methodName](self, *args, **kwargs) def getField(self, name): return self.o.getAppyType(name) + @classmethod + def getWorkflow(klass): + '''Returns the workflow tied to p_klass.''' + # Browse parent classes of p_klass in reverse order. This way, a + # user-defined workflow will override a Appy default workflow. + i = len(klass.__bases__)-1 + res = None + while i >= 0: + res = getattr(klass.__bases__[i], 'workflow', None) + if res: break + i -= 1 + # Return a default workflow if no workflow was found. + if not res: + res = WorkflowAnonymous + return res def link(self, fieldName, obj): '''This method links p_obj (which can be a list of objects) to this one