[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.
This commit is contained in:
parent
e3b7f5364f
commit
0d7afb685f
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
info.append(', '.join([self.translate('role_%s'%r) \
|
||||
for r in rolesToShow]))
|
||||
return (' | '.join(res), appyUser.o.getUrl(mode='edit', page='main',
|
||||
nav=''))
|
||||
# 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.'''
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -285,7 +285,7 @@
|
|||
<img title="Edit" style="cursor:pointer"
|
||||
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
|
||||
src string: $appUrl/ui/editBig.png"
|
||||
tal:condition="python: contextObj.allows('Modify portal content')"/>
|
||||
tal:condition="contextObj/mayEdit"/>
|
||||
</tal:edit>
|
||||
|
||||
<tal:refresh condition="contextObj/isDebug">
|
||||
|
|
|
@ -124,12 +124,12 @@
|
|||
<td>
|
||||
<a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);"
|
||||
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)"
|
||||
tal:condition="python: obj.allows('Modify portal content')">
|
||||
tal:condition="obj/mayEdit">
|
||||
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||
</a></td>
|
||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||
<td>
|
||||
<img tal:condition="python: obj.allows('Delete objects') and obj.mayDelete()"
|
||||
<img tal:condition="obj/mayDelete"
|
||||
title="Delete" style="cursor:pointer"
|
||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||
|
|
|
@ -143,7 +143,8 @@
|
|||
</td>
|
||||
<td align="right" class="userStripText" tal:define="userInfo tool/getUserLine">
|
||||
<span tal:content="python: userInfo[0]"></span>
|
||||
<a tal:attributes="href python: userInfo[1]">
|
||||
<a tal:condition="python: userInfo[1]"
|
||||
tal:attributes="href python: userInfo[1]">
|
||||
<img tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</tal:moveRef>
|
||||
</td>
|
||||
<tal:comment replace="nothing">Edit the element</tal:comment>
|
||||
<td tal:condition="python: obj.allows('Modify portal content') and not appyType['noForm']">
|
||||
<td tal:condition="python: not appyType['noForm'] and obj.mayEdit()">
|
||||
<a tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], repeat['obj'].number()+startNumber, totalNumber);"
|
||||
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
|
||||
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||
|
@ -48,7 +48,7 @@
|
|||
</td>
|
||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||
<td>
|
||||
<img tal:condition="python: not appyType['isBack'] and obj.allows('Delete objects') and obj.mayDelete()"
|
||||
<img tal:condition="python: not appyType['isBack'] and obj.mayDelete()"
|
||||
title="Delete" style="cursor:pointer"
|
||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue