[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'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, 'Anonymous', 'Authenticated'), w:(mgr,o),d:(mgr,o)},
|
active = State({r:(mgr, 'Anonymous', 'Authenticated'), w:(mgr,o),d:(mgr,o)},
|
||||||
initial=True)
|
initial=True)
|
||||||
WorkflowAnonymous.__instance__ = WorkflowAnonymous()
|
|
||||||
|
|
||||||
class WorkflowAuthenticated:
|
class WorkflowAuthenticated:
|
||||||
'''One-state workflow allowing authenticated users to consult and Manager
|
'''One-state workflow allowing authenticated users to consult and Manager
|
||||||
|
@ -2800,7 +2799,6 @@ class WorkflowAuthenticated:
|
||||||
o = 'Owner'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, 'Authenticated'), w:(mgr,o), d:(mgr,o)},
|
active = State({r:(mgr, 'Authenticated'), w:(mgr,o), d:(mgr,o)},
|
||||||
initial=True)
|
initial=True)
|
||||||
WorkflowAuthenticated.__instance__ = WorkflowAuthenticated()
|
|
||||||
|
|
||||||
class WorkflowOwner:
|
class WorkflowOwner:
|
||||||
'''One-state workflow allowing only manager and owner to consult and
|
'''One-state workflow allowing only manager and owner to consult and
|
||||||
|
@ -2808,7 +2806,6 @@ class WorkflowOwner:
|
||||||
mgr = 'Manager'
|
mgr = 'Manager'
|
||||||
o = 'Owner'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, o), w:(mgr, o), d:mgr}, initial=True)
|
active = State({r:(mgr, o), w:(mgr, o), d:mgr}, initial=True)
|
||||||
WorkflowOwner.__instance__ = WorkflowOwner()
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Selection:
|
class Selection:
|
||||||
|
|
|
@ -368,8 +368,8 @@ class ZopeInstaller:
|
||||||
constructors = (ctor,),
|
constructors = (ctor,),
|
||||||
permission = self.addContentPermissions[name])
|
permission = self.addContentPermissions[name])
|
||||||
# Create workflow prototypical instances in __instance__ attributes
|
# Create workflow prototypical instances in __instance__ attributes
|
||||||
wf = getattr(klass.wrapperClass, 'workflow', None)
|
wf = wrapper.getWorkflow()
|
||||||
if wf and not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
if not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
||||||
|
|
||||||
def installAppyTypes(self):
|
def installAppyTypes(self):
|
||||||
'''We complete here the initialisation process of every Appy type of
|
'''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
|
elem is the one-line user info as shown on every page; second line is
|
||||||
the URL to edit user info.'''
|
the URL to edit user info.'''
|
||||||
appyUser = self.appy().appyUser
|
appyUser = self.appy().appyUser
|
||||||
res = [appyUser.title]
|
info = [appyUser.title]
|
||||||
rolesToShow = [r for r in appyUser.roles \
|
rolesToShow = [r for r in appyUser.roles \
|
||||||
if r not in ('Authenticated', 'Member')]
|
if r not in ('Authenticated', 'Member')]
|
||||||
if rolesToShow:
|
if rolesToShow:
|
||||||
res.append(', '.join([self.translate('role_%s'%r) \
|
info.append(', '.join([self.translate('role_%s'%r) \
|
||||||
for r in rolesToShow]))
|
for r in rolesToShow]))
|
||||||
return (' | '.join(res), appyUser.o.getUrl(mode='edit', page='main',
|
# Edit URL for the appy user.
|
||||||
nav=''))
|
url = None
|
||||||
|
if appyUser.o.mayEdit():
|
||||||
|
url = appyUser.o.getUrl(mode='edit', page='main', nav='')
|
||||||
|
return (' | '.join(info), url)
|
||||||
|
|
||||||
def generateUid(self, className):
|
def generateUid(self, className):
|
||||||
'''Generates a UID for an instance of p_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
|
'''Returns the workflow applicable for p_self (or for any instance of
|
||||||
p_className if given), or its name, if p_name is True.'''
|
p_className if given), or its name, if p_name is True.'''
|
||||||
if not className:
|
if not className:
|
||||||
appyClass = self.wrapperClass.__bases__[-1]
|
wrapperClass = self.wrapperClass
|
||||||
else:
|
else:
|
||||||
appyClass = self.getTool().getAppyClass(className)
|
wrapperClass = self.getTool().getAppyClass(className, wrapper=True)
|
||||||
if hasattr(appyClass, 'workflow'): wf = appyClass.workflow
|
wf = wrapperClass.getWorkflow()
|
||||||
else:
|
|
||||||
wf = gen.WorkflowAnonymous
|
|
||||||
if not name: return wf
|
if not name: return wf
|
||||||
return WorkflowDescriptor.getWorkflowName(wf)
|
return WorkflowDescriptor.getWorkflowName(wf)
|
||||||
|
|
||||||
|
@ -957,13 +955,23 @@ class BaseMixin:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def mayDelete(self):
|
def mayDelete(self):
|
||||||
'''May the currently logged user delete this object? This condition
|
'''May the currently logged user delete this object?.'''
|
||||||
comes as an addition/refinement to the corresponding workflow
|
res = self.allows('Delete objects')
|
||||||
permission.'''
|
if not res: return
|
||||||
|
# An additional, user-defined condition, may refine the base permission.
|
||||||
appyObj = self.appy()
|
appyObj = self.appy()
|
||||||
if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete()
|
if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete()
|
||||||
return True
|
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):
|
def executeAppyAction(self, actionName, reindex=True):
|
||||||
'''Executes action with p_fieldName on this object.'''
|
'''Executes action with p_fieldName on this object.'''
|
||||||
appyType = self.getAppyType(actionName)
|
appyType = self.getAppyType(actionName)
|
||||||
|
|
|
@ -285,7 +285,7 @@
|
||||||
<img title="Edit" style="cursor:pointer"
|
<img title="Edit" style="cursor:pointer"
|
||||||
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
|
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
|
||||||
src string: $appUrl/ui/editBig.png"
|
src string: $appUrl/ui/editBig.png"
|
||||||
tal:condition="python: contextObj.allows('Modify portal content')"/>
|
tal:condition="contextObj/mayEdit"/>
|
||||||
</tal:edit>
|
</tal:edit>
|
||||||
|
|
||||||
<tal:refresh condition="contextObj/isDebug">
|
<tal:refresh condition="contextObj/isDebug">
|
||||||
|
|
|
@ -124,12 +124,12 @@
|
||||||
<td>
|
<td>
|
||||||
<a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);"
|
<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: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"/>
|
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||||
</a></td>
|
</a></td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td>
|
<td>
|
||||||
<img tal:condition="python: obj.allows('Delete objects') and obj.mayDelete()"
|
<img tal:condition="obj/mayDelete"
|
||||||
title="Delete" style="cursor:pointer"
|
title="Delete" style="cursor:pointer"
|
||||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
|
|
|
@ -143,7 +143,8 @@
|
||||||
</td>
|
</td>
|
||||||
<td align="right" class="userStripText" tal:define="userInfo tool/getUserLine">
|
<td align="right" class="userStripText" tal:define="userInfo tool/getUserLine">
|
||||||
<span tal:content="python: userInfo[0]"></span>
|
<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"/>
|
<img tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</tal:moveRef>
|
</tal:moveRef>
|
||||||
</td>
|
</td>
|
||||||
<tal:comment replace="nothing">Edit the element</tal:comment>
|
<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);"
|
<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)">
|
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
|
||||||
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
<img title="Edit" tal:attributes="src string: $appUrl/ui/edit.gif"/>
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
</td>
|
</td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td>
|
<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"
|
title="Delete" style="cursor:pointer"
|
||||||
tal:attributes="src string: $appUrl/ui/delete.png;
|
tal:attributes="src string: $appUrl/ui/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, mimetypes
|
import os, os.path, mimetypes
|
||||||
import appy.pod
|
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.gen.utils import createObject
|
||||||
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
||||||
normalizeString, sequenceTypes
|
normalizeString, sequenceTypes
|
||||||
|
@ -87,6 +87,21 @@ class AbstractWrapper(object):
|
||||||
return customUser.__dict__[methodName](self, *args, **kwargs)
|
return customUser.__dict__[methodName](self, *args, **kwargs)
|
||||||
|
|
||||||
def getField(self, name): return self.o.getAppyType(name)
|
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):
|
def link(self, fieldName, obj):
|
||||||
'''This method links p_obj (which can be a list of objects) to this one
|
'''This method links p_obj (which can be a list of objects) to this one
|
||||||
|
|
Loading…
Reference in a new issue