[gen] Get rid of some Zope-specific security aspects. This is a preparatory work to extend the Appy authentication system to perform ldap authentication as well.
This commit is contained in:
parent
04852360fa
commit
5223af2a62
|
@ -246,14 +246,14 @@ class Field:
|
||||||
elif rp and isinstance(rp, basestring):
|
elif rp and isinstance(rp, basestring):
|
||||||
self.readPermission = rp
|
self.readPermission = rp
|
||||||
else:
|
else:
|
||||||
self.readPermission = 'View'
|
self.readPermission = 'read'
|
||||||
wp = self.specificWritePermission
|
wp = self.specificWritePermission
|
||||||
if wp and not isinstance(wp, basestring):
|
if wp and not isinstance(wp, basestring):
|
||||||
self.writePermission = '%s: Write %s %s' % (appName, prefix, name)
|
self.writePermission = '%s: Write %s %s' % (appName, prefix, name)
|
||||||
elif wp and isinstance(wp, basestring):
|
elif wp and isinstance(wp, basestring):
|
||||||
self.writePermission = wp
|
self.writePermission = wp
|
||||||
else:
|
else:
|
||||||
self.writePermission = 'Modify portal content'
|
self.writePermission = 'write'
|
||||||
if (self.type == 'Ref') and not self.isBack:
|
if (self.type == 'Ref') and not self.isBack:
|
||||||
# We must initialise the corresponding back reference
|
# We must initialise the corresponding back reference
|
||||||
self.back.klass = klass
|
self.back.klass = klass
|
||||||
|
|
|
@ -140,7 +140,7 @@ class Ref(Field):
|
||||||
folder=zobj.getCreateFolder();
|
folder=zobj.getCreateFolder();
|
||||||
tiedClassName=ztool.getPortalType(field.klass);
|
tiedClassName=ztool.getPortalType(field.klass);
|
||||||
canWrite=not field.isBack and zobj.allows(field.writePermission);
|
canWrite=not field.isBack and zobj.allows(field.writePermission);
|
||||||
showPlusIcon=zobj.mayAddReference(field.name);
|
showPlusIcon=field.mayAdd(zobj);
|
||||||
atMostOneRef=(field.multiplicity[1] == 1) and \
|
atMostOneRef=(field.multiplicity[1] == 1) and \
|
||||||
(len(zobjects)<=1);
|
(len(zobjects)<=1);
|
||||||
addConfirmMsg=field.addConfirm and \
|
addConfirmMsg=field.addConfirm and \
|
||||||
|
@ -592,12 +592,8 @@ class Ref(Field):
|
||||||
# May the user edit this Ref field?
|
# May the user edit this Ref field?
|
||||||
if not obj.allows(self.writePermission):
|
if not obj.allows(self.writePermission):
|
||||||
return gutils.No('no_write_perm')
|
return gutils.No('no_write_perm')
|
||||||
# Have the user the correct add permission?
|
# May the user create instances of the referred class?
|
||||||
tool = obj.getTool()
|
if not obj.getTool().userMayCreate(self.klass):
|
||||||
addPermission = '%s: Add %s' % (tool.getAppName(),
|
|
||||||
tool.getPortalType(self.klass))
|
|
||||||
folder = obj.getCreateFolder()
|
|
||||||
if not tool.getUser().has_permission(addPermission, folder):
|
|
||||||
return gutils.No('no_add_perm')
|
return gutils.No('no_add_perm')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ class UiSearch:
|
||||||
pxView = Px('''
|
pxView = Px('''
|
||||||
<div class="portletSearch">
|
<div class="portletSearch">
|
||||||
<a href=":'%s?className=%s&search=%s' % \
|
<a href=":'%s?className=%s&search=%s' % \
|
||||||
(queryUrl, rootClass, search.name)"
|
(queryUrl, className, search.name)"
|
||||||
class=":search.name == currentSearch and 'portletCurrent' or ''"
|
class=":search.name == currentSearch and 'portletCurrent' or ''"
|
||||||
title=":search.translatedDescr">:search.translated</a>
|
title=":search.translatedDescr">:search.translated</a>
|
||||||
</div>''')
|
</div>''')
|
||||||
|
|
|
@ -59,12 +59,6 @@ class Import:
|
||||||
self.sort = sort
|
self.sort = sort
|
||||||
|
|
||||||
# Workflow-specific types and default workflows --------------------------------
|
# Workflow-specific types and default workflows --------------------------------
|
||||||
appyToZopePermissions = {
|
|
||||||
'read': ('View', 'Access contents information'),
|
|
||||||
'write': 'Modify portal content',
|
|
||||||
'delete': 'Delete objects',
|
|
||||||
}
|
|
||||||
|
|
||||||
class Role:
|
class Role:
|
||||||
'''Represents a role.'''
|
'''Represents a role.'''
|
||||||
zopeRoles = ('Manager', 'Owner', 'Anonymous', 'Authenticated')
|
zopeRoles = ('Manager', 'Owner', 'Anonymous', 'Authenticated')
|
||||||
|
@ -108,7 +102,7 @@ class State:
|
||||||
name of a role, this method returns self.usedRoles[role] if it
|
name of a role, this method returns self.usedRoles[role] if it
|
||||||
exists, or creates a Role instance, puts it in self.usedRoles and
|
exists, or creates a Role instance, puts it in self.usedRoles and
|
||||||
returns it else. If it is a Role instance, the method stores it in
|
returns it else. If it is a Role instance, the method stores it in
|
||||||
self.usedRoles if it not in it yet and returns it.'''
|
self.usedRoles if it is not in it yet and returns it.'''
|
||||||
if isinstance(role, basestring):
|
if isinstance(role, basestring):
|
||||||
if role in self.usedRoles:
|
if role in self.usedRoles:
|
||||||
return self.usedRoles[role]
|
return self.usedRoles[role]
|
||||||
|
@ -135,61 +129,6 @@ class State:
|
||||||
|
|
||||||
def getUsedRoles(self): return self.usedRoles.values()
|
def getUsedRoles(self): return self.usedRoles.values()
|
||||||
|
|
||||||
def getPermissions(self):
|
|
||||||
'''If you get the permissions mapping through self.permissions, dict
|
|
||||||
values may be of different types (a list of roles, a single role or
|
|
||||||
None). Iy you call this method, you will always get a list which
|
|
||||||
may be empty.'''
|
|
||||||
res = {}
|
|
||||||
for permission, roleValue in self.permissions.iteritems():
|
|
||||||
if roleValue == None:
|
|
||||||
res[permission] = []
|
|
||||||
elif isinstance(roleValue, basestring):
|
|
||||||
res[permission] = [roleValue]
|
|
||||||
else:
|
|
||||||
res[permission] = roleValue
|
|
||||||
return res
|
|
||||||
|
|
||||||
def updatePermission(self, obj, zopePermission, roleNames):
|
|
||||||
'''Updates, on p_obj, list of p_roleNames which are granted a given
|
|
||||||
p_zopePermission. This method returns True if the list has been
|
|
||||||
effectively updated.'''
|
|
||||||
attr = Permission.getZopeAttrName(zopePermission)
|
|
||||||
if not hasattr(obj.aq_base, attr) or \
|
|
||||||
(getattr(obj.aq_base, attr) != roleNames):
|
|
||||||
setattr(obj, attr, roleNames)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def updatePermissions(self, wf, obj):
|
|
||||||
'''Zope requires permission-to-roles mappings to be stored as attributes
|
|
||||||
on the object itself. This method does this job, duplicating the info
|
|
||||||
from this state definition on p_obj. p_res is True if at least one
|
|
||||||
change has been effectively performed.'''
|
|
||||||
res = False
|
|
||||||
for permission, roles in self.getPermissions().iteritems():
|
|
||||||
roleNames = tuple([role.name for role in roles])
|
|
||||||
# Compute Zope permission(s) related to this permission.
|
|
||||||
if appyToZopePermissions.has_key(permission):
|
|
||||||
# It is a standard permission (r, w, d)
|
|
||||||
zopePerm = appyToZopePermissions[permission]
|
|
||||||
elif isinstance(permission, basestring):
|
|
||||||
# It is a user-defined permission
|
|
||||||
zopePerm = permission
|
|
||||||
else:
|
|
||||||
# It is a Permission instance
|
|
||||||
appName = obj.getProductConfig().PROJECTNAME
|
|
||||||
zopePerm = permission.getName(wf, appName)
|
|
||||||
# zopePerm contains a single permission or a tuple of permissions
|
|
||||||
if isinstance(zopePerm, basestring):
|
|
||||||
changed = self.updatePermission(obj, zopePerm, roleNames)
|
|
||||||
res = res or changed
|
|
||||||
else:
|
|
||||||
for zPerm in zopePerm:
|
|
||||||
changed = self.updatePermission(obj, zPerm, roleNames)
|
|
||||||
res = res or changed
|
|
||||||
return res
|
|
||||||
|
|
||||||
class Transition:
|
class Transition:
|
||||||
def __init__(self, states, condition=True, action=None, notify=None,
|
def __init__(self, states, condition=True, action=None, notify=None,
|
||||||
show=True, confirm=False):
|
show=True, confirm=False):
|
||||||
|
@ -345,8 +284,6 @@ class Transition:
|
||||||
if not doHistory: comment = '_invisible_'
|
if not doHistory: comment = '_invisible_'
|
||||||
obj.addHistoryEvent(action, review_state=targetStateName,
|
obj.addHistoryEvent(action, review_state=targetStateName,
|
||||||
comments=comment)
|
comments=comment)
|
||||||
# Update permissions-to-roles attributes
|
|
||||||
targetState.updatePermissions(wf, obj)
|
|
||||||
# Reindex the object if required. Not only security-related indexes
|
# Reindex the object if required. Not only security-related indexes
|
||||||
# (Allowed, State) need to be updated here.
|
# (Allowed, State) need to be updated here.
|
||||||
if not obj.isTemporary(): obj.reindex()
|
if not obj.isTemporary(): obj.reindex()
|
||||||
|
@ -384,8 +321,7 @@ class Permission:
|
||||||
self.fieldDescriptor = fieldDescriptor
|
self.fieldDescriptor = fieldDescriptor
|
||||||
|
|
||||||
def getName(self, wf, appName):
|
def getName(self, wf, appName):
|
||||||
'''Returns the name of the Zope permission that corresponds to this
|
'''Returns the name of this permission.'''
|
||||||
permission.'''
|
|
||||||
className, fieldName = self.fieldDescriptor.rsplit('.', 1)
|
className, fieldName = self.fieldDescriptor.rsplit('.', 1)
|
||||||
if className.find('.') == -1:
|
if className.find('.') == -1:
|
||||||
# The related class resides in the same module as the workflow
|
# The related class resides in the same module as the workflow
|
||||||
|
@ -398,16 +334,6 @@ class Permission:
|
||||||
else: access = 'Write'
|
else: access = 'Write'
|
||||||
return '%s: %s %s %s' % (appName, access, fullClassName, fieldName)
|
return '%s: %s %s %s' % (appName, access, fullClassName, fieldName)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getZopeAttrName(zopePermission):
|
|
||||||
'''Gets the name of the attribute where Zope stores, on every object,
|
|
||||||
the tuple of roles who are granted a given p_zopePermission.'''
|
|
||||||
res = ''
|
|
||||||
for c in zopePermission:
|
|
||||||
if c in Permission.allowedChars: res += c
|
|
||||||
else: res += '_'
|
|
||||||
return '_%s_Permission' % res
|
|
||||||
|
|
||||||
class ReadPermission(Permission): pass
|
class ReadPermission(Permission): pass
|
||||||
class WritePermission(Permission): pass
|
class WritePermission(Permission): pass
|
||||||
|
|
||||||
|
@ -475,7 +401,7 @@ class Config:
|
||||||
languageSelector = False
|
languageSelector = False
|
||||||
# People having one of these roles will be able to create instances
|
# People having one of these roles will be able to create instances
|
||||||
# of classes defined in your application.
|
# of classes defined in your application.
|
||||||
defaultCreators = ['Manager', 'Owner']
|
defaultCreators = ['Manager']
|
||||||
# Number of translations for every page on a Translation object
|
# Number of translations for every page on a Translation object
|
||||||
translationsPerPage = 30
|
translationsPerPage = 30
|
||||||
# Language that will be used as a basis for translating to other
|
# Language that will be used as a basis for translating to other
|
||||||
|
|
|
@ -557,12 +557,6 @@ class ZopeGenerator(Generator):
|
||||||
if theImport not in imports:
|
if theImport not in imports:
|
||||||
imports.append(theImport)
|
imports.append(theImport)
|
||||||
repls['imports'] = '\n'.join(imports)
|
repls['imports'] = '\n'.join(imports)
|
||||||
# Compute list of add permissions
|
|
||||||
addPermissions = ''
|
|
||||||
for classDescr in classesAll:
|
|
||||||
addPermissions += ' "%s":"%s: Add %s",\n' % (classDescr.name,
|
|
||||||
self.applicationName, classDescr.name)
|
|
||||||
repls['addPermissions'] = addPermissions
|
|
||||||
# Compute root classes
|
# Compute root classes
|
||||||
repls['rootClasses'] = ','.join(["'%s'" % c.name \
|
repls['rootClasses'] = ','.join(["'%s'" % c.name \
|
||||||
for c in classesButTool if c.isRoot()])
|
for c in classesButTool if c.isRoot()])
|
||||||
|
|
|
@ -63,7 +63,6 @@ class ZopeInstaller:
|
||||||
self.productName = config.PROJECTNAME
|
self.productName = config.PROJECTNAME
|
||||||
self.languages = config.appConfig.languages
|
self.languages = config.appConfig.languages
|
||||||
self.logger = config.logger
|
self.logger = config.logger
|
||||||
self.addContentPermissions = config.ADD_CONTENT_PERMISSIONS
|
|
||||||
|
|
||||||
def installUi(self):
|
def installUi(self):
|
||||||
'''Installs the user interface.'''
|
'''Installs the user interface.'''
|
||||||
|
@ -157,14 +156,9 @@ class ZopeInstaller:
|
||||||
catalog.reindexIndex('SearchableText', self.app.REQUEST)
|
catalog.reindexIndex('SearchableText', self.app.REQUEST)
|
||||||
self.logger.info('Done.')
|
self.logger.info('Done.')
|
||||||
|
|
||||||
def getAddPermission(self, className):
|
|
||||||
'''What is the name of the permission allowing to create instances of
|
|
||||||
class whose name is p_className?'''
|
|
||||||
return self.productName + ': Add ' + className
|
|
||||||
|
|
||||||
def installBaseObjects(self):
|
def installBaseObjects(self):
|
||||||
'''Creates the tool and the root data folder if they do not exist.'''
|
'''Creates the tool and the base data folder if they do not exist.'''
|
||||||
# Create or update the base folder for storing data
|
# Create the tool.
|
||||||
zopeContent = self.app.objectIds()
|
zopeContent = self.app.objectIds()
|
||||||
from OFS.Folder import manage_addFolder
|
from OFS.Folder import manage_addFolder
|
||||||
|
|
||||||
|
@ -172,29 +166,8 @@ class ZopeInstaller:
|
||||||
toolName = '%sTool' % self.productName
|
toolName = '%sTool' % self.productName
|
||||||
gutils.createObject(self.app, 'config', toolName, self.productName,
|
gutils.createObject(self.app, 'config', toolName, self.productName,
|
||||||
wf=False, noSecurity=True)
|
wf=False, noSecurity=True)
|
||||||
|
# Create the base data folder.
|
||||||
if 'data' not in zopeContent:
|
if 'data' not in zopeContent: manage_addFolder(self.app, 'data')
|
||||||
manage_addFolder(self.app, 'data')
|
|
||||||
data = self.app.data
|
|
||||||
tool = self.app.config
|
|
||||||
# Manager has been granted Add permissions for all root classes.
|
|
||||||
# This may not be desired, so remove this.
|
|
||||||
for className in self.config.rootClasses:
|
|
||||||
permission = self.getAddPermission(className)
|
|
||||||
data.manage_permission(permission, (), acquire=0)
|
|
||||||
# All roles defined as creators should be able to create the
|
|
||||||
# corresponding root classes in this folder.
|
|
||||||
i = -1
|
|
||||||
for klass in self.config.appClasses:
|
|
||||||
i += 1
|
|
||||||
if not klass.__dict__.has_key('root') or \
|
|
||||||
not klass.__dict__['root']:
|
|
||||||
continue # It is not a root class
|
|
||||||
className = self.config.appClassNames[i]
|
|
||||||
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
|
||||||
creators = wrapperClass.getCreators(self.config)
|
|
||||||
permission = self.getAddPermission(className)
|
|
||||||
gutils.updateRolesForPermission(permission,tuple(creators),data)
|
|
||||||
|
|
||||||
# Remove some default objects created by Zope but not useful to Appy
|
# Remove some default objects created by Zope but not useful to Appy
|
||||||
for name in ('standard_html_footer', 'standard_html_header',\
|
for name in ('standard_html_footer', 'standard_html_header',\
|
||||||
|
@ -206,14 +179,13 @@ class ZopeInstaller:
|
||||||
inner objects (users, groups, translations, documents).'''
|
inner objects (users, groups, translations, documents).'''
|
||||||
tool = self.app.config
|
tool = self.app.config
|
||||||
tool.createOrUpdate(True, None)
|
tool.createOrUpdate(True, None)
|
||||||
tool.refreshSecurity()
|
|
||||||
appyTool = tool.appy()
|
appyTool = tool.appy()
|
||||||
appyTool.log('Appy version is "%s".' % appy.version.short)
|
appyTool.log('Appy version is "%s".' % appy.version.short)
|
||||||
|
|
||||||
# Create the default users if they do not exist.
|
# Create the default users if they do not exist.
|
||||||
for login, roles in self.defaultUsers.iteritems():
|
for login, roles in self.defaultUsers.iteritems():
|
||||||
if not appyTool.count('User', noSecurity=True, login=login):
|
if not appyTool.count('User', noSecurity=True, login=login):
|
||||||
appyTool.create('users', noSecurity=True, login=login,
|
appyTool.create('users', noSecurity=True, id=login, login=login,
|
||||||
password1=login, password2=login,
|
password1=login, password2=login,
|
||||||
email='%s@appyframework.org'%login, roles=roles)
|
email='%s@appyframework.org'%login, roles=roles)
|
||||||
appyTool.log('User "%s" created.' % login)
|
appyTool.log('User "%s" created.' % login)
|
||||||
|
@ -332,8 +304,7 @@ class ZopeInstaller:
|
||||||
wrapper = klass.wrapperClass
|
wrapper = klass.wrapperClass
|
||||||
exec 'from %s import manage_add%s as ctor' % (module, name)
|
exec 'from %s import manage_add%s as ctor' % (module, name)
|
||||||
self.zopeContext.registerClass(meta_type=name,
|
self.zopeContext.registerClass(meta_type=name,
|
||||||
constructors = (ctor,),
|
constructors = (ctor,), permission = None)
|
||||||
permission = self.addContentPermissions[name])
|
|
||||||
# Create workflow prototypical instances in __instance__ attributes
|
# Create workflow prototypical instances in __instance__ attributes
|
||||||
wf = wrapper.getWorkflow()
|
wf = wrapper.getWorkflow()
|
||||||
if not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
if not hasattr(wf, '__instance__'): wf.__instance__ = wf()
|
||||||
|
|
|
@ -199,7 +199,8 @@ class ToolMixin(BaseMixin):
|
||||||
|
|
||||||
def getRootClasses(self):
|
def getRootClasses(self):
|
||||||
'''Returns the list of root classes for this application.'''
|
'''Returns the list of root classes for this application.'''
|
||||||
return self.getProductConfig().rootClasses
|
cfg = self.getProductConfig()
|
||||||
|
return [self.getAppyClass(k) for k in cfg.rootClasses]
|
||||||
|
|
||||||
def _appy_getAllFields(self, className):
|
def _appy_getAllFields(self, className):
|
||||||
'''Returns the (translated) names of fields of p_className.'''
|
'''Returns the (translated) names of fields of p_className.'''
|
||||||
|
@ -265,8 +266,8 @@ class ToolMixin(BaseMixin):
|
||||||
p_className.'''
|
p_className.'''
|
||||||
appyClass = self.getAppyClass(className)
|
appyClass = self.getAppyClass(className)
|
||||||
importParams = self.getCreateMeans(appyClass)['import']
|
importParams = self.getCreateMeans(appyClass)['import']
|
||||||
onElement = importParams['onElement'].__get__('')
|
onElement = importParams.onElement.__get__('')
|
||||||
sortMethod = importParams['sort']
|
sortMethod = importParams.sort
|
||||||
if sortMethod: sortMethod = sortMethod.__get__('')
|
if sortMethod: sortMethod = sortMethod.__get__('')
|
||||||
elems = []
|
elems = []
|
||||||
importType = self.getAppyType('importPathFor%s' % className)
|
importType = self.getAppyType('importPathFor%s' % className)
|
||||||
|
@ -280,7 +281,7 @@ class ToolMixin(BaseMixin):
|
||||||
elems.append(elemInfo)
|
elems.append(elemInfo)
|
||||||
if sortMethod:
|
if sortMethod:
|
||||||
elems = sortMethod(elems)
|
elems = sortMethod(elems)
|
||||||
return [importParams['headers'], elems]
|
return [importParams.headers, elems]
|
||||||
|
|
||||||
def showPortlet(self, context, layoutType):
|
def showPortlet(self, context, layoutType):
|
||||||
'''When must the portlet be shown?'''
|
'''When must the portlet be shown?'''
|
||||||
|
@ -489,17 +490,13 @@ class ToolMixin(BaseMixin):
|
||||||
if wrapper: return zopeClass.wrapperClass
|
if wrapper: return zopeClass.wrapperClass
|
||||||
else: return zopeClass.wrapperClass.__bases__[-1]
|
else: return zopeClass.wrapperClass.__bases__[-1]
|
||||||
|
|
||||||
def getCreateMeans(self, contentTypeOrAppyClass):
|
def getCreateMeans(self, klass):
|
||||||
'''Gets the different ways objects of p_contentTypeOrAppyClass (which
|
'''Gets the different ways objects of p_klass can be created (via a web
|
||||||
can be a Zope content type or a Appy class) can be created
|
form, by importing external data, etc). Result is a dict whose keys
|
||||||
(via a web form, by importing external data, etc). Result is a
|
are strings (ie "form", "import"...) and whose values are additional
|
||||||
dict whose keys are strings (ie "form", "import"...) and whose
|
data about the particular mean.'''
|
||||||
values are additional data about the particular mean.'''
|
|
||||||
pythonClass = contentTypeOrAppyClass
|
|
||||||
if isinstance(contentTypeOrAppyClass, basestring):
|
|
||||||
pythonClass = self.getAppyClass(pythonClass)
|
|
||||||
res = {}
|
res = {}
|
||||||
if not pythonClass.__dict__.has_key('create'):
|
if not klass.__dict__.has_key('create'):
|
||||||
res['form'] = None
|
res['form'] = None
|
||||||
# No additional data for this means, which is the default one.
|
# No additional data for this means, which is the default one.
|
||||||
else:
|
else:
|
||||||
|
@ -511,23 +508,28 @@ class ToolMixin(BaseMixin):
|
||||||
if isinstance(mean, basestring):
|
if isinstance(mean, basestring):
|
||||||
res[mean] = None
|
res[mean] = None
|
||||||
else:
|
else:
|
||||||
res[mean.id] = mean.__dict__
|
res[mean.id] = mean
|
||||||
else:
|
else:
|
||||||
res[means.id] = means.__dict__
|
res[means.id] = means
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def userMaySearch(self, rootClass):
|
def userMaySearch(self, rootClass):
|
||||||
'''This method checks if the currently logged user can trigger searches
|
'''May the logged user search among instances of p_rootClass ?'''
|
||||||
on a given p_rootClass. This is done by calling method "maySearch"
|
# When editing a form, one should avoid annoying the user with this.
|
||||||
on the class. If no such method exists, we return True.'''
|
|
||||||
# When editign a form, one should avoid annoying the user with this.
|
|
||||||
url = self.REQUEST['ACTUAL_URL']
|
url = self.REQUEST['ACTUAL_URL']
|
||||||
if url.endswith('/edit') or url.endswith('/do'): return
|
if url.endswith('/edit') or url.endswith('/do'): return
|
||||||
pythonClass = self.getAppyClass(rootClass)
|
if 'maySearch' in rootClass.__dict__:
|
||||||
if 'maySearch' in pythonClass.__dict__:
|
return pythonClass.rootClass(self.appy())
|
||||||
return pythonClass.maySearch(self.appy())
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def userMayCreate(self, klass):
|
||||||
|
'''May the logged user create instances of p_klass ?'''
|
||||||
|
allowedRoles = getattr(klass, 'creators', None) or \
|
||||||
|
self.getProductConfig().appConfig.defaultCreators
|
||||||
|
for role in self.getUser().getRoles():
|
||||||
|
if role in allowedRoles:
|
||||||
|
return True
|
||||||
|
|
||||||
def onImportObjects(self):
|
def onImportObjects(self):
|
||||||
'''This method is called when the user wants to create objects from
|
'''This method is called when the user wants to create objects from
|
||||||
external data.'''
|
external data.'''
|
||||||
|
@ -737,23 +739,22 @@ class ToolMixin(BaseMixin):
|
||||||
obj = self.getObject(objectUid)
|
obj = self.getObject(objectUid)
|
||||||
return obj, fieldName
|
return obj, fieldName
|
||||||
|
|
||||||
def getGroupedSearches(self, className):
|
def getGroupedSearches(self, klass):
|
||||||
'''Returns an object with 2 attributes:
|
'''Returns an object with 2 attributes:
|
||||||
* "searches" stores the searches that are defined for p_className;
|
* "searches" stores the searches that are defined for p_klass;
|
||||||
* "default" stores the search defined as the default one.
|
* "default" stores the search defined as the default one.
|
||||||
Every item representing a search is a dict containing info about a
|
Every item representing a search is a dict containing info about a
|
||||||
search or about a group of searches.
|
search or about a group of searches.
|
||||||
'''
|
'''
|
||||||
appyClass = self.getAppyClass(className)
|
|
||||||
res = []
|
res = []
|
||||||
default = None # Also retrieve the default one here.
|
default = None # Also retrieve the default one here.
|
||||||
groups = {} # The already encountered groups
|
groups = {} # The already encountered groups
|
||||||
page = Page('main') # A dummy page required by class UiGroup
|
page = Page('main') # A dummy page required by class UiGroup
|
||||||
# Get the searches statically defined on the class
|
# Get the searches statically defined on the class
|
||||||
searches = ClassDescriptor.getSearches(appyClass, tool=self.appy())
|
searches = ClassDescriptor.getSearches(klass, tool=self.appy())
|
||||||
# Get the dynamically computed searches
|
# Get the dynamically computed searches
|
||||||
if hasattr(appyClass, 'getDynamicSearches'):
|
if hasattr(klass, 'getDynamicSearches'):
|
||||||
searches += appyClass.getDynamicSearches(self.appy())
|
searches += klass.getDynamicSearches(self.appy())
|
||||||
for search in searches:
|
for search in searches:
|
||||||
# Create the search descriptor
|
# Create the search descriptor
|
||||||
uiSearch = UiSearch(search, className, self)
|
uiSearch = UiSearch(search, className, self)
|
||||||
|
@ -792,9 +793,8 @@ class ToolMixin(BaseMixin):
|
||||||
if ui: res = UiSearch(res, className, self)
|
if ui: res = UiSearch(res, className, self)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def advancedSearchEnabledFor(self, className):
|
def advancedSearchEnabledFor(self, klass):
|
||||||
'''Is advanced search visible for p_klass ?'''
|
'''Is advanced search visible for p_klass ?'''
|
||||||
klass = self.getAppyClass(className)
|
|
||||||
# By default, advanced search is enabled.
|
# By default, advanced search is enabled.
|
||||||
if not hasattr(klass, 'searchAdvanced'): return True
|
if not hasattr(klass, 'searchAdvanced'): return True
|
||||||
# Evaluate attribute "show" on this Search instance representing the
|
# Evaluate attribute "show" on this Search instance representing the
|
||||||
|
@ -1103,29 +1103,26 @@ class ToolMixin(BaseMixin):
|
||||||
login = (rq.__class__.__name__ == 'Object') and 'system' or 'anon'
|
login = (rq.__class__.__name__ == 'Object') and 'system' or 'anon'
|
||||||
# Get the User object from a query in the catalog.
|
# Get the User object from a query in the catalog.
|
||||||
user = tool.search1('User', noSecurity=True, login=login)
|
user = tool.search1('User', noSecurity=True, login=login)
|
||||||
|
# It is possible that we find no user here: it happens before users
|
||||||
|
# "anon" and "system" are created, at first Zope startup.
|
||||||
|
if not user: return
|
||||||
rq.user = user
|
rq.user = user
|
||||||
# Precompute some values or this usser for performance reasons
|
# Precompute some values or this user for performance reasons.
|
||||||
rq.userRoles = user.getRoles()
|
rq.userRoles = user.getRoles()
|
||||||
|
rq.userLogins = user.getLogins()
|
||||||
rq.zopeUser = user.getZopeUser()
|
rq.zopeUser = user.getZopeUser()
|
||||||
return user
|
return user
|
||||||
#from AccessControl import getSecurityManager
|
|
||||||
#user = getSecurityManager().getUser()
|
|
||||||
#if not user:
|
|
||||||
# from AccessControl.User import nobody
|
|
||||||
# return nobody
|
|
||||||
#return user
|
|
||||||
|
|
||||||
def getUserLine(self):
|
def getUserLine(self):
|
||||||
'''Returns a info about the currently logged user as a 2-tuple: first
|
'''Returns a info about the currently logged user as a 2-tuple: first
|
||||||
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.'''
|
||||||
user = self.getUser()
|
user = self.getUser()
|
||||||
userRoles = self.appy().request.userRoles
|
|
||||||
info = [user.title]
|
info = [user.title]
|
||||||
rolesToShow = [r for r in userRoles if r != 'Authenticated']
|
showable = [r for r in user.getRoles() if r != 'Authenticated']
|
||||||
if rolesToShow:
|
if showable:
|
||||||
info.append(', '.join([self.translate('role_%s' % r) \
|
info.append(', '.join([self.translate('role_%s' % r) \
|
||||||
for r in rolesToShow]))
|
for r in showable]))
|
||||||
# Edit URL for the user.
|
# Edit URL for the user.
|
||||||
url = None
|
url = None
|
||||||
if user.o.mayEdit():
|
if user.o.mayEdit():
|
||||||
|
@ -1134,17 +1131,17 @@ class ToolMixin(BaseMixin):
|
||||||
|
|
||||||
def getUserName(self, login=None, normalized=False):
|
def getUserName(self, login=None, normalized=False):
|
||||||
'''Gets the user name corresponding to p_login (or the currently logged
|
'''Gets the user name corresponding to p_login (or the currently logged
|
||||||
login if None), or the p_login itself if the user does not exist
|
user if None), or the p_login itself if the user does not exist
|
||||||
anymore. If p_normalized is True, special chars in the first and last
|
anymore. If p_normalized is True, special chars in the first and last
|
||||||
names are normalized.'''
|
names are normalized.'''
|
||||||
tool = self.appy()
|
tool = self.appy()
|
||||||
if not login: login = tool.user.login
|
if not login: login = tool.user.login
|
||||||
# Manage the special case of an anonymous user.
|
# Manage the special case of an anonymous user.
|
||||||
if login == 'Anonymous User':
|
if login == 'anon':
|
||||||
name = self.translate('anonymous')
|
name = self.translate('anonymous')
|
||||||
if normalized: name = sutils.normalizeString(name)
|
if normalized: name = sutils.normalizeString(name)
|
||||||
return name
|
return name
|
||||||
# Manage the case of a "real" user.
|
# Manage the case of any other user.
|
||||||
user = tool.search1('User', noSecurity=True, login=login)
|
user = tool.search1('User', noSecurity=True, login=login)
|
||||||
if not user: return login
|
if not user: return login
|
||||||
firstName = user.firstName
|
firstName = user.firstName
|
||||||
|
|
|
@ -93,9 +93,6 @@ class BaseMixin:
|
||||||
# Manage potential link with an initiator object
|
# Manage potential link with an initiator object
|
||||||
if created and initiator: initiator.appy().link(initiatorField.name,obj)
|
if created and initiator: initiator.appy().link(initiatorField.name,obj)
|
||||||
|
|
||||||
# Manage "add" permissions and reindex the object
|
|
||||||
obj._appy_managePermissions()
|
|
||||||
|
|
||||||
# Call the custom "onEdit" if available
|
# Call the custom "onEdit" if available
|
||||||
msg = None # The message to display to the user. It can be set by onEdit
|
msg = None # The message to display to the user. It can be set by onEdit
|
||||||
if obj.wrapperClass:
|
if obj.wrapperClass:
|
||||||
|
@ -410,7 +407,7 @@ class BaseMixin:
|
||||||
return self.goto(tool.getSiteUrl(), msg)
|
return self.goto(tool.getSiteUrl(), msg)
|
||||||
# If the user can't access the object anymore, redirect him to the
|
# If the user can't access the object anymore, redirect him to the
|
||||||
# main site page.
|
# main site page.
|
||||||
if not obj.allows('View'):
|
if not obj.allows('read'):
|
||||||
return self.goto(tool.getSiteUrl(), msg)
|
return self.goto(tool.getSiteUrl(), msg)
|
||||||
if (buttonClicked == 'save') or saveConfirmed:
|
if (buttonClicked == 'save') or saveConfirmed:
|
||||||
obj.say(msg)
|
obj.say(msg)
|
||||||
|
@ -484,7 +481,7 @@ class BaseMixin:
|
||||||
corresponding Appy wrapper and returns, as XML, the its result.'''
|
corresponding Appy wrapper and returns, as XML, the its result.'''
|
||||||
self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml;charset=utf-8')
|
self.REQUEST.RESPONSE.setHeader('Content-Type','text/xml;charset=utf-8')
|
||||||
# Check if the user is allowed to consult this object
|
# Check if the user is allowed to consult this object
|
||||||
if not self.allows('View'):
|
if not self.allows('read'):
|
||||||
return XmlMarshaller().marshall('Unauthorized')
|
return XmlMarshaller().marshall('Unauthorized')
|
||||||
if not action:
|
if not action:
|
||||||
marshaller = XmlMarshaller(rootTag=self.getClass().__name__,
|
marshaller = XmlMarshaller(rootTag=self.getClass().__name__,
|
||||||
|
@ -670,11 +667,6 @@ class BaseMixin:
|
||||||
if not allValues: return ''
|
if not allValues: return ''
|
||||||
return getattr(allValues[rowIndex], name, '')
|
return getattr(allValues[rowIndex], name, '')
|
||||||
|
|
||||||
def mayAddReference(self, name):
|
|
||||||
'''May the user add references via Ref field named p_name in
|
|
||||||
p_folder?'''
|
|
||||||
return self.getAppyType(name).mayAdd(self)
|
|
||||||
|
|
||||||
def isDebug(self):
|
def isDebug(self):
|
||||||
'''Are we in debug mode ?'''
|
'''Are we in debug mode ?'''
|
||||||
for arg in sys.argv:
|
for arg in sys.argv:
|
||||||
|
@ -937,30 +929,6 @@ class BaseMixin:
|
||||||
stateName = stateName or self.State()
|
stateName = stateName or self.State()
|
||||||
return '%s_%s' % (self.getWorkflow(name=True), stateName)
|
return '%s_%s' % (self.getWorkflow(name=True), stateName)
|
||||||
|
|
||||||
def refreshSecurity(self):
|
|
||||||
'''Refresh security info on this object. Returns True if the info has
|
|
||||||
effectively been updated.'''
|
|
||||||
wf = self.getWorkflow()
|
|
||||||
try:
|
|
||||||
# Get the state definition of the object's current state.
|
|
||||||
state = getattr(wf, self.State())
|
|
||||||
except AttributeError:
|
|
||||||
# The workflow information for this object does not correspond to
|
|
||||||
# its current workflow attribution. Add a new fake event
|
|
||||||
# representing passage of this object to the initial state of his
|
|
||||||
# currently attributed workflow.
|
|
||||||
stateName = self.State(name=True, initial=True)
|
|
||||||
self.addHistoryEvent(None, review_state=stateName)
|
|
||||||
state = self.State(name=False, initial=True)
|
|
||||||
self.log('Wrong workflow info for a "%s"; is now in state "%s".' % \
|
|
||||||
(self.meta_type, stateName))
|
|
||||||
# Update permission attributes on the object if required
|
|
||||||
updated = state.updatePermissions(wf, self)
|
|
||||||
if updated:
|
|
||||||
# Reindex the object because security-related info is indexed.
|
|
||||||
self.reindex()
|
|
||||||
return updated
|
|
||||||
|
|
||||||
def applyUserIdChange(self, oldId, newId):
|
def applyUserIdChange(self, oldId, newId):
|
||||||
'''A user whose ID was p_oldId has now p_newId. If the old ID was
|
'''A user whose ID was p_oldId has now p_newId. If the old ID was
|
||||||
mentioned in self's local roles, update it to the new ID. This
|
mentioned in self's local roles, update it to the new ID. This
|
||||||
|
@ -1105,14 +1073,14 @@ class BaseMixin:
|
||||||
|
|
||||||
def mayDelete(self):
|
def mayDelete(self):
|
||||||
'''May the currently logged user delete this object?'''
|
'''May the currently logged user delete this object?'''
|
||||||
res = self.allows('Delete objects')
|
res = self.allows('delete')
|
||||||
if not res: return
|
if not res: return
|
||||||
# An additional, user-defined condition, may refine the base permission.
|
# 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, permission='Modify portal content'):
|
def mayEdit(self, permission='write'):
|
||||||
'''May the currently logged user edit this object? p_perm can be a
|
'''May the currently logged user edit this object? p_perm can be a
|
||||||
field-specific permission.'''
|
field-specific permission.'''
|
||||||
res = self.allows(permission)
|
res = self.allows(permission)
|
||||||
|
@ -1195,6 +1163,12 @@ class BaseMixin:
|
||||||
self.reindex()
|
self.reindex()
|
||||||
return self.goto(self.getUrl(rq['HTTP_REFERER']))
|
return self.goto(self.getUrl(rq['HTTP_REFERER']))
|
||||||
|
|
||||||
|
def getRolesFor(self, permission):
|
||||||
|
'''Gets, according to the workflow, the roles that are currently granted
|
||||||
|
p_permission on this object.'''
|
||||||
|
state = self.State(name=False)
|
||||||
|
return [role.name for role in state.permissions[permission]]
|
||||||
|
|
||||||
def appy(self):
|
def appy(self):
|
||||||
'''Returns a wrapper object allowing to manipulate p_self the Appy
|
'''Returns a wrapper object allowing to manipulate p_self the Appy
|
||||||
way.'''
|
way.'''
|
||||||
|
@ -1284,18 +1258,17 @@ class BaseMixin:
|
||||||
'''Returns the list of roles and users that are allowed to view this
|
'''Returns the list of roles and users that are allowed to view this
|
||||||
object. This index value will be used within catalog queries for
|
object. This index value will be used within catalog queries for
|
||||||
filtering objects the user is allowed to see.'''
|
filtering objects the user is allowed to see.'''
|
||||||
res = set()
|
# Get, from the workflow, roles having permission 'read'.
|
||||||
# Get, from the workflow, roles having permission 'View'.
|
res = self.getRolesFor('read')
|
||||||
for role in self.getProductConfig().rolesForPermissionOn('View', self):
|
# Add users or groups having, locally, this role on this object.
|
||||||
res.add(role)
|
localRoles = getattr(self.aq_base, '__ac_local_roles__', None)
|
||||||
# Add users having, locally, this role on this object.
|
if not localRoles: return res
|
||||||
localRoles = getattr(self, '__ac_local_roles__', None)
|
|
||||||
if not localRoles: return list(res)
|
|
||||||
for id, roles in localRoles.iteritems():
|
for id, roles in localRoles.iteritems():
|
||||||
for role in roles:
|
for role in roles:
|
||||||
if role in res:
|
if role in res:
|
||||||
res.add('user:%s' % id)
|
usr = 'user:%s' % id
|
||||||
return list(res)
|
if usr not in res: res.append(usr)
|
||||||
|
return res
|
||||||
|
|
||||||
def showState(self):
|
def showState(self):
|
||||||
'''Must I show self's current state ?'''
|
'''Must I show self's current state ?'''
|
||||||
|
@ -1326,39 +1299,6 @@ class BaseMixin:
|
||||||
res.append((elem, self.translate(self.getWorkflowLabel(elem))))
|
res.append((elem, self.translate(self.getWorkflowLabel(elem))))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _appy_managePermissions(self):
|
|
||||||
'''When an object is created or updated, we must update "add"
|
|
||||||
permissions accordingly: if the object is a folder, we must set on
|
|
||||||
it permissions that will allow to create, inside it, objects through
|
|
||||||
Ref fields; if it is not a folder, we must update permissions on its
|
|
||||||
parent folder instead.'''
|
|
||||||
# Determine on which folder we need to set "add" permissions
|
|
||||||
folder = self.getCreateFolder()
|
|
||||||
# On this folder, set "add" permissions for every content type that will
|
|
||||||
# be created through reference fields
|
|
||||||
allCreators = {} # One key for every add permission
|
|
||||||
addPermissions = self.getProductConfig().ADD_CONTENT_PERMISSIONS
|
|
||||||
for appyType in self.getAllAppyTypes():
|
|
||||||
if appyType.type != 'Ref': continue
|
|
||||||
if appyType.isBack or appyType.link: continue
|
|
||||||
# Indeed, no possibility to create objects with such Refs
|
|
||||||
tool = self.getTool()
|
|
||||||
refType = tool.getPortalType(appyType.klass)
|
|
||||||
if refType not in addPermissions: continue
|
|
||||||
# Get roles that may add this content type
|
|
||||||
appyWrapper = tool.getAppyClass(refType, wrapper=True)
|
|
||||||
creators = appyWrapper.getCreators(self.getProductConfig())
|
|
||||||
# Add those creators to the list of creators for this meta_type
|
|
||||||
addPermission = addPermissions[refType]
|
|
||||||
if addPermission in allCreators:
|
|
||||||
allCreators[addPermission] = allCreators[\
|
|
||||||
addPermission].union(creators)
|
|
||||||
else:
|
|
||||||
allCreators[addPermission] = set(creators)
|
|
||||||
# Update the permissions
|
|
||||||
for permission, creators in allCreators.iteritems():
|
|
||||||
updateRolesForPermission(permission, tuple(creators), folder)
|
|
||||||
|
|
||||||
getUrlDefaults = {'page':True, 'nav':True}
|
getUrlDefaults = {'page':True, 'nav':True}
|
||||||
def getUrl(self, base=None, mode='view', **kwargs):
|
def getUrl(self, base=None, mode='view', **kwargs):
|
||||||
'''Returns an URL for this object.
|
'''Returns an URL for this object.
|
||||||
|
|
|
@ -13,7 +13,6 @@ from ZPublisher.HTTPRequest import BaseRequest
|
||||||
from OFS.Image import File
|
from OFS.Image import File
|
||||||
from ZPublisher.HTTPRequest import FileUpload
|
from ZPublisher.HTTPRequest import FileUpload
|
||||||
from AccessControl import getSecurityManager
|
from AccessControl import getSecurityManager
|
||||||
from AccessControl.PermissionRole import rolesForPermissionOn
|
|
||||||
from DateTime import DateTime
|
from DateTime import DateTime
|
||||||
from Products.ExternalMethod.ExternalMethod import ExternalMethod
|
from Products.ExternalMethod.ExternalMethod import ExternalMethod
|
||||||
from Products.Transience.Transience import TransientObjectContainer
|
from Products.Transience.Transience import TransientObjectContainer
|
||||||
|
@ -24,8 +23,6 @@ logger = logging.getLogger('<!applicationName!>')
|
||||||
# Some global variables --------------------------------------------------------
|
# Some global variables --------------------------------------------------------
|
||||||
PROJECTNAME = '<!applicationName!>'
|
PROJECTNAME = '<!applicationName!>'
|
||||||
diskFolder = os.path.dirname(<!applicationName!>.__file__)
|
diskFolder = os.path.dirname(<!applicationName!>.__file__)
|
||||||
ADD_CONTENT_PERMISSIONS = {
|
|
||||||
<!addPermissions!>}
|
|
||||||
|
|
||||||
# Applications classes, in various formats
|
# Applications classes, in various formats
|
||||||
rootClasses = [<!rootClasses!>]
|
rootClasses = [<!rootClasses!>]
|
||||||
|
@ -34,7 +31,7 @@ appClassNames = [<!appClassNames!>]
|
||||||
allClassNames = [<!allClassNames!>]
|
allClassNames = [<!allClassNames!>]
|
||||||
|
|
||||||
# In the following dict, we store, for every Appy class, the ordered list of
|
# In the following dict, we store, for every Appy class, the ordered list of
|
||||||
# appy types (included inherited ones).
|
# fields.
|
||||||
attributes = {<!attributes!>}
|
attributes = {<!attributes!>}
|
||||||
|
|
||||||
# Application roles
|
# Application roles
|
||||||
|
|
27
gen/utils.py
27
gen/utils.py
|
@ -17,17 +17,11 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False):
|
||||||
user = tool.getUser()
|
user = tool.getUser()
|
||||||
if not noSecurity:
|
if not noSecurity:
|
||||||
# Check that the user can create objects of className.
|
# Check that the user can create objects of className.
|
||||||
userRoles = user.getRoles()
|
klass = ZopeClass.wrapperClass.__bases__[-1]
|
||||||
allowedRoles=ZopeClass.wrapperClass.getCreators(tool.getProductConfig())
|
if not tool.userMayCreate(klass):
|
||||||
allowed = False
|
|
||||||
for role in userRoles:
|
|
||||||
if role in allowedRoles:
|
|
||||||
allowed = True
|
|
||||||
break
|
|
||||||
if not allowed:
|
|
||||||
from AccessControl import Unauthorized
|
from AccessControl import Unauthorized
|
||||||
raise Unauthorized("User can't create instances of %s" % \
|
raise Unauthorized("User can't create instances of %s" % \
|
||||||
ZopeClass.__name__)
|
klass.__name__)
|
||||||
obj = ZopeClass(id)
|
obj = ZopeClass(id)
|
||||||
folder._objects = folder._objects + ({'id':id, 'meta_type':className},)
|
folder._objects = folder._objects + ({'id':id, 'meta_type':className},)
|
||||||
folder._setOb(id, obj)
|
folder._setOb(id, obj)
|
||||||
|
@ -137,21 +131,6 @@ def getClassName(klass, appName=None):
|
||||||
res = klass.__module__.replace('.', '_') + '_' + klass.__name__
|
res = klass.__module__.replace('.', '_') + '_' + klass.__name__
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def updateRolesForPermission(permission, roles, obj):
|
|
||||||
'''Adds roles from list p_roles to the list of roles that are granted
|
|
||||||
p_permission on p_obj.'''
|
|
||||||
from AccessControl.Permission import Permission
|
|
||||||
# Find existing roles that were granted p_permission on p_obj
|
|
||||||
existingRoles = ()
|
|
||||||
for p in obj.ac_inherited_permissions(1):
|
|
||||||
name, value = p[:2]
|
|
||||||
if name == permission:
|
|
||||||
perm = Permission(name, value, obj)
|
|
||||||
existingRoles = perm.getRoles()
|
|
||||||
allRoles = set(existingRoles).union(roles)
|
|
||||||
obj.manage_permission(permission, tuple(allRoles), acquire=0)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
def callMethod(obj, method, klass=None, cache=True):
|
def callMethod(obj, method, klass=None, cache=True):
|
||||||
'''This function is used to call a p_method on some Appy p_obj. m_method
|
'''This function is used to call a p_method on some Appy p_obj. m_method
|
||||||
|
|
|
@ -30,55 +30,18 @@ class GroupWrapper(AbstractWrapper):
|
||||||
'''Inter-field validation.'''
|
'''Inter-field validation.'''
|
||||||
return self._callCustom('validate', new, errors)
|
return self._callCustom('validate', new, errors)
|
||||||
|
|
||||||
def confirm(self, new):
|
|
||||||
'''Use this method for remembering the previous list of users for this
|
|
||||||
group.'''
|
|
||||||
obj = self.o
|
|
||||||
if hasattr(obj.aq_base, '_oldUsers'): del obj.aq_base._oldUsers
|
|
||||||
obj._oldUsers = self.users
|
|
||||||
|
|
||||||
def addUser(self, user):
|
def addUser(self, user):
|
||||||
'''Adds a p_user to this group.'''
|
'''Adds a p_user to this group.'''
|
||||||
# Update the Ref field.
|
# Update the Ref field.
|
||||||
self.link('users', user)
|
self.link('users', user)
|
||||||
# Update the group-related info on the Zope user.
|
|
||||||
zopeUser = user.getZopeUser()
|
|
||||||
zopeUser.groups[self.login] = self.roles
|
|
||||||
|
|
||||||
def removeUser(self, user):
|
def removeUser(self, user):
|
||||||
'''Removes a p_user from this group.'''
|
'''Removes a p_user from this group.'''
|
||||||
self.unlink('users', user)
|
self.unlink('users', user)
|
||||||
# Update the group-related info on the Zope user.
|
|
||||||
zopeUser = user.getZopeUser()
|
|
||||||
del zopeUser.groups[self.login]
|
|
||||||
|
|
||||||
def onEdit(self, created):
|
def onEdit(self, created):
|
||||||
# Create or update, on every Zope user of this group, group-related
|
# If the group was created by anon, anon can't stay its Owner.
|
||||||
# information.
|
if 'anon' in self.o.__ac_local_roles__:
|
||||||
# 1. Remove reference to this group for users that were removed from it
|
del self.o.__ac_local_roles__['anon']
|
||||||
newUsers = self.users
|
|
||||||
# The list of previously existing users does not exist when editing a
|
|
||||||
# group from Python. For updating self.users, it is recommended to use
|
|
||||||
# methods m_addUser and m_removeUser above.
|
|
||||||
oldUsers = getattr(self.o.aq_base, '_oldUsers', ())
|
|
||||||
for user in oldUsers:
|
|
||||||
if user not in newUsers:
|
|
||||||
del user.getZopeUser().groups[self.login]
|
|
||||||
self.log('User "%s" removed from group "%s".' % \
|
|
||||||
(user.login, self.login))
|
|
||||||
# 2. Add reference to this group for users that were added to it
|
|
||||||
for user in newUsers:
|
|
||||||
zopeUser = user.getZopeUser()
|
|
||||||
# We refresh group-related info on the Zope user even if the user
|
|
||||||
# was already in the group.
|
|
||||||
zopeUser.groups[self.login] = self.roles
|
|
||||||
if user not in oldUsers:
|
|
||||||
self.log('User "%s" added to group "%s".' % \
|
|
||||||
(user.login, self.login))
|
|
||||||
if hasattr(self.o.aq_base, '_oldUsers'): del self.o._oldUsers
|
|
||||||
# If the group was created by an Anonymous, Anonymous can't stay Owner
|
|
||||||
# of the object.
|
|
||||||
if None in self.o.__ac_local_roles__:
|
|
||||||
del self.o.__ac_local_roles__[None]
|
|
||||||
return self._callCustom('onEdit', created)
|
return self._callCustom('onEdit', created)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -174,8 +174,8 @@ class ToolWrapper(AbstractWrapper):
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- One section for every searchable root class -->
|
<!-- One section for every searchable root class -->
|
||||||
<x for="rootClass in [rc for rc in rootClasses \
|
<x for="rootClass in rootClasses" if="ztool.userMaySearch(rootClass)"
|
||||||
if ztool.userMaySearch(rc)]">
|
var2="className=ztool.getPortalType(rootClass)">
|
||||||
|
|
||||||
<!-- A separator if required -->
|
<!-- A separator if required -->
|
||||||
<div class="portletSep" var="nb=loop.rootClass.nb"
|
<div class="portletSep" var="nb=loop.rootClass.nb"
|
||||||
|
@ -188,31 +188,30 @@ class ToolWrapper(AbstractWrapper):
|
||||||
<a var="queryParam=searchInfo.default and \
|
<a var="queryParam=searchInfo.default and \
|
||||||
searchInfo.default.name or ''"
|
searchInfo.default.name or ''"
|
||||||
href=":'%s?className=%s&search=%s' % \
|
href=":'%s?className=%s&search=%s' % \
|
||||||
(queryUrl,rootClass,queryParam)"
|
(queryUrl, className, queryParam)"
|
||||||
class=":(not currentSearch and (currentClass==rootClass) and \
|
class=":(not currentSearch and (currentClass==className) and \
|
||||||
(currentPage=='query')) and \
|
(currentPage=='query')) and \
|
||||||
'portletCurrent' or ''">::_(rootClass + '_plural')</a>
|
'portletCurrent' or ''">::_(className + '_plural')</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<x var="addPermission='%s: Add %s' % (appName, rootClass);
|
<x var="mayCreate=ztool.userMayCreate(rootClass);
|
||||||
userMayAdd=user.has_permission(addPermission, appFolder);
|
|
||||||
createMeans=ztool.getCreateMeans(rootClass)">
|
createMeans=ztool.getCreateMeans(rootClass)">
|
||||||
|
|
||||||
<!-- Create a new object from a web form -->
|
<!-- Create a new object from a web form. -->
|
||||||
<input type="button" class="button"
|
<input type="button" class="button"
|
||||||
if="userMayAdd and ('form' in createMeans)"
|
if="mayCreate and ('form' in createMeans)"
|
||||||
style=":url('buttonAdd', bg=True)" value=":_('query_create')"
|
style=":url('buttonAdd', bg=True)" value=":_('query_create')"
|
||||||
onclick=":'goto(%s)' % \
|
onclick=":'goto(%s)' % \
|
||||||
q('%s/do?action=Create&className=%s' % \
|
q('%s/do?action=Create&className=%s' % \
|
||||||
(toolUrl, rootClass))"/>
|
(toolUrl, className))"/>
|
||||||
|
|
||||||
<!-- Create object(s) by importing data -->
|
<!-- Create object(s) by importing data -->
|
||||||
<input type="button" class="button"
|
<input type="button" class="button"
|
||||||
if="userMayAdd and ('import' in createMeans)"
|
if="mayCreate and ('import' in createMeans)"
|
||||||
style=":url('buttonImport', bg=True)" value=":_('query_import')"
|
style=":url('buttonImport', bg=True)" value=":_('query_import')"
|
||||||
onclick=":'goto(%s)' % \
|
onclick=":'goto(%s)' % \
|
||||||
q('%s/import?className=%s' % (toolUrl, rootClass))"/>
|
q('%s/import?className=%s' % (toolUrl, className))"/>
|
||||||
</x>
|
</x>
|
||||||
|
|
||||||
<!-- Searches -->
|
<!-- Searches -->
|
||||||
|
@ -221,7 +220,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
<!-- Live search -->
|
<!-- Live search -->
|
||||||
<form action=":'%s/do' % toolUrl">
|
<form action=":'%s/do' % toolUrl">
|
||||||
<input type="hidden" name="action" value="SearchObjects"/>
|
<input type="hidden" name="action" value="SearchObjects"/>
|
||||||
<input type="hidden" name="className" value=":rootClass"/>
|
<input type="hidden" name="className" value=":className"/>
|
||||||
<table cellpadding="0" cellspacing="0">
|
<table cellpadding="0" cellspacing="0">
|
||||||
<tr valign="bottom">
|
<tr valign="bottom">
|
||||||
<td><input type="text" size="14" name="w_SearchableText"
|
<td><input type="text" size="14" name="w_SearchableText"
|
||||||
|
@ -234,13 +233,13 @@ class ToolWrapper(AbstractWrapper):
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Advanced search -->
|
<!-- Advanced search -->
|
||||||
<div var="highlighted=(currentClass == rootClass) and \
|
<div var="highlighted=(currentClass == className) and \
|
||||||
(currentPage == 'search')"
|
(currentPage == 'search')"
|
||||||
class=":highlighted and 'portletSearch portletCurrent' or \
|
class=":highlighted and 'portletSearch portletCurrent' or \
|
||||||
'portletSearch'"
|
'portletSearch'"
|
||||||
align=":dright">
|
align=":dright">
|
||||||
<a var="text=_('search_title')" style="font-size: 88%"
|
<a var="text=_('search_title')" style="font-size: 88%"
|
||||||
href=":'%s/search?className=%s' % (toolUrl, rootClass)"
|
href=":'%s/search?className=%s' % (toolUrl, className)"
|
||||||
title=":text"><x>:text</x>...</a>
|
title=":text"><x>:text</x>...</a>
|
||||||
</div>
|
</div>
|
||||||
</x>
|
</x>
|
||||||
|
@ -754,16 +753,6 @@ class ToolWrapper(AbstractWrapper):
|
||||||
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
|
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
|
||||||
sendMail(self, to, subject, body, attachments=attachments)
|
sendMail(self, to, subject, body, attachments=attachments)
|
||||||
|
|
||||||
def refreshSecurity(self):
|
|
||||||
'''Refreshes, on every object in the database, security-related,
|
|
||||||
workflow-managed information.'''
|
|
||||||
context = {'nb': 0}
|
|
||||||
for className in self.o.getProductConfig().allClassNames:
|
|
||||||
self.compute(className, context=context, noSecurity=True,
|
|
||||||
expression="ctx['nb'] += int(obj.o.refreshSecurity())")
|
|
||||||
msg = 'Security refresh: %d object(s) updated.' % context['nb']
|
|
||||||
self.log(msg)
|
|
||||||
|
|
||||||
def refreshCatalog(self, startObject=None):
|
def refreshCatalog(self, startObject=None):
|
||||||
'''Reindex all Appy objects. For some unknown reason, method
|
'''Reindex all Appy objects. For some unknown reason, method
|
||||||
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
||||||
|
|
|
@ -140,8 +140,18 @@ class UserWrapper(AbstractWrapper):
|
||||||
else:
|
else:
|
||||||
self.title = self.login
|
self.title = self.login
|
||||||
|
|
||||||
|
def ensureAdminIsManager(self):
|
||||||
|
'''User 'admin' must always have role 'Manager'.'''
|
||||||
|
if self.o.id == 'admin':
|
||||||
|
roles = self.roles
|
||||||
|
if 'Manager' not in roles:
|
||||||
|
if not roles: roles = ['Manager']
|
||||||
|
else: roles.append('Manager')
|
||||||
|
self.roles = roles
|
||||||
|
|
||||||
def onEdit(self, created):
|
def onEdit(self, created):
|
||||||
self.updateTitle()
|
self.updateTitle()
|
||||||
|
self.ensureAdminIsManager()
|
||||||
aclUsers = self.o.acl_users
|
aclUsers = self.o.acl_users
|
||||||
login = self.login
|
login = self.login
|
||||||
if created:
|
if created:
|
||||||
|
@ -173,19 +183,25 @@ class UserWrapper(AbstractWrapper):
|
||||||
self.o.manage_addLocalRoles(login, ('Owner',))
|
self.o.manage_addLocalRoles(login, ('Owner',))
|
||||||
# If the user was created by an Anonymous, Anonymous can't stay Owner
|
# If the user was created by an Anonymous, Anonymous can't stay Owner
|
||||||
# of the object.
|
# of the object.
|
||||||
if None in self.o.__ac_local_roles__:
|
if 'anon' in self.o.__ac_local_roles__:
|
||||||
del self.o.__ac_local_roles__[None]
|
del self.o.__ac_local_roles__['anon']
|
||||||
return self._callCustom('onEdit', created)
|
return self._callCustom('onEdit', created)
|
||||||
|
|
||||||
def mayEdit(self):
|
def mayEdit(self):
|
||||||
|
'''No one can edit users "system" and "anon".'''
|
||||||
|
if self.o.id in ('system', 'anon'): return
|
||||||
|
# Call custom "mayEdit" when present.
|
||||||
custom = self._getCustomMethod('mayEdit')
|
custom = self._getCustomMethod('mayEdit')
|
||||||
if custom: return self._callCustom('mayEdit')
|
if custom: return self._callCustom('mayEdit')
|
||||||
else: return True
|
return True
|
||||||
|
|
||||||
def mayDelete(self):
|
def mayDelete(self):
|
||||||
|
'''No one can delete users "system", "anon" and "admin".'''
|
||||||
|
if self.o.id in ('system', 'anon', 'admin'): return
|
||||||
|
# Call custom "mayDelete" when present.
|
||||||
custom = self._getCustomMethod('mayDelete')
|
custom = self._getCustomMethod('mayDelete')
|
||||||
if custom: return self._callCustom('mayDelete')
|
if custom: return self._callCustom('mayDelete')
|
||||||
else: return True
|
return True
|
||||||
|
|
||||||
def getZopeUser(self):
|
def getZopeUser(self):
|
||||||
'''Gets the Zope user corresponding to this user.'''
|
'''Gets the Zope user corresponding to this user.'''
|
||||||
|
@ -198,97 +214,87 @@ class UserWrapper(AbstractWrapper):
|
||||||
# Call a custom "onDelete" if any.
|
# Call a custom "onDelete" if any.
|
||||||
return self._callCustom('onDelete')
|
return self._callCustom('onDelete')
|
||||||
|
|
||||||
# Standard Zope user methods -----------------------------------------------
|
def getLogins(self):
|
||||||
|
'''Gets all the logins that can "match" this user: it own login and the
|
||||||
|
logins of all the groups he belongs to.'''
|
||||||
|
# Try first to get those logins from a cache on the request.
|
||||||
|
try:
|
||||||
|
return self.request.userLogins
|
||||||
|
except AttributeError:
|
||||||
|
res = [group.login for group in self.groups]
|
||||||
|
res.append(self.login)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getRoles(self):
|
||||||
|
'''This method returns all the global roles for this user, not simply
|
||||||
|
self.roles, but also "ungrantable roles" (like Anonymous or
|
||||||
|
Authenticated) and roles inherited from group membership.'''
|
||||||
|
# Try first to get those roles from a cache on the request.
|
||||||
|
try:
|
||||||
|
return self.request.userRoles
|
||||||
|
except AttributeError:
|
||||||
|
res = list(self.roles)
|
||||||
|
# Add ungrantable roles
|
||||||
|
if self.o.id == 'anon':
|
||||||
|
res.append('Anonymous')
|
||||||
|
else:
|
||||||
|
res.append('Authenticated')
|
||||||
|
# Add group global roles
|
||||||
|
for group in self.groups:
|
||||||
|
for role in group.roles:
|
||||||
|
if role not in res: res.append(role)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def getRolesFor(self, obj):
|
||||||
|
'''Gets the roles the user has in the context of p_obj: its global roles
|
||||||
|
+ its roles which are local to p_obj.'''
|
||||||
|
obj = obj.o
|
||||||
|
# Start with user global roles.
|
||||||
|
res = self.getRoles()
|
||||||
|
# Add local roles, granted to the user directly or to one of its groups.
|
||||||
|
localRoles = getattr(obj.aq_base, '__ac_local_roles__', None)
|
||||||
|
if not localRoles: return res
|
||||||
|
# Gets the logins of this user and all its groups.
|
||||||
|
logins = self.getLogins()
|
||||||
|
for login, roles in localRoles.iteritems():
|
||||||
|
# Ignore logins not corresponding to this user.
|
||||||
|
if login not in logins: continue
|
||||||
|
for role in roles:
|
||||||
|
if role not in res: res.append(role)
|
||||||
|
return res
|
||||||
|
|
||||||
def has_role(self, role, obj=None):
|
def has_role(self, role, obj=None):
|
||||||
zopeUser = self.request.zopeUser
|
'''Has the logged user some p_role? If p_obj is None, check if the user
|
||||||
if obj: return zopeUser.has_role(role, obj)
|
has p_role globally; else, check if he has this p_role in the context
|
||||||
return zopeUser.has_role(role)
|
of p_obj.'''
|
||||||
|
if obj:
|
||||||
|
roles = self.getRolesFor(obj)
|
||||||
|
else:
|
||||||
|
roles = self.getRoles()
|
||||||
|
return role in roles
|
||||||
|
|
||||||
def has_permission(self, permission, obj):
|
def has_permission(self, permission, obj):
|
||||||
return self.request.zopeUser.has_permission(permission, obj)
|
'''Has the logged user p_permission on p_obj?'''
|
||||||
|
obj = obj.o
|
||||||
def getRoles(self):
|
# What are the roles which are granted p_permission on p_obj?
|
||||||
'''This method collects all the roles for this user, not simply
|
allowedRoles = obj.getRolesFor(permission)
|
||||||
user.roles, but also roles inherited from group membership.'''
|
# Grant access if "Anonymous" is among roles.
|
||||||
return self.getZopeUser().getRoles()
|
if ('Anonymous' in allowedRoles): return True
|
||||||
|
# Grant access if "Authenticated" is among p_roles and the user is not
|
||||||
# ------------------------------------------------------------------------------
|
# anonymous.
|
||||||
try:
|
if ('Authenticated' in allowedRoles) and (self.o.id != 'anon'):
|
||||||
from AccessControl.PermissionRole import _what_not_even_god_should_do, \
|
return True
|
||||||
rolesForPermissionOn
|
# Grant access based on global user roles.
|
||||||
from Acquisition import aq_base
|
|
||||||
except ImportError:
|
|
||||||
pass # For those using Appy without Zope
|
|
||||||
|
|
||||||
class ZopeUserPatches:
|
|
||||||
'''This class is a fake one that defines Appy variants of some of Zope's
|
|
||||||
AccessControl.User methods. The idea is to implement the notion of group
|
|
||||||
of users.'''
|
|
||||||
|
|
||||||
def getRoles(self):
|
|
||||||
'''Returns the global roles that this user (or any of its groups)
|
|
||||||
possesses.'''
|
|
||||||
res = list(self.roles)
|
|
||||||
if 'Anonymous' not in res: res.append('Authenticated')
|
|
||||||
# Add group global roles
|
|
||||||
if not hasattr(aq_base(self), 'groups'): return res
|
|
||||||
for roles in self.groups.itervalues():
|
|
||||||
for role in roles:
|
|
||||||
if role not in res: res.append(role)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def getRolesInContext(self, object):
|
|
||||||
'''Return the list of global and local (to p_object) roles granted to
|
|
||||||
this user (or to any of its groups).'''
|
|
||||||
if isinstance(object, AbstractWrapper): object = object.o
|
|
||||||
object = getattr(object, 'aq_inner', object)
|
|
||||||
# Start with user global roles
|
|
||||||
res = self.getRoles()
|
|
||||||
# Add local roles
|
|
||||||
localRoles = getattr(object, '__ac_local_roles__', None)
|
|
||||||
if not localRoles: return res
|
|
||||||
userId = self.getId()
|
|
||||||
groups = getattr(self, 'groups', ())
|
|
||||||
for id, roles in localRoles.iteritems():
|
|
||||||
if (id != userId) and (id not in groups): continue
|
|
||||||
for role in roles:
|
|
||||||
if role not in res: res.append(role)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def allowed(self, object, object_roles=None):
|
|
||||||
'''Checks whether the user has access to p_object. The user (or one of
|
|
||||||
its groups) must have one of the roles in p_object_roles.'''
|
|
||||||
if object_roles is _what_not_even_god_should_do: return 0
|
|
||||||
# If "Anonymous" is among p_object_roles, grant access.
|
|
||||||
if (object_roles is None) or ('Anonymous' in object_roles): return 1
|
|
||||||
# If "Authenticated" is among p_object_roles, grant access if the user
|
|
||||||
# is not anonymous.
|
|
||||||
if 'Authenticated' in object_roles and \
|
|
||||||
(self.getUserName() != 'Anonymous User'):
|
|
||||||
if self._check_context(object): return 1
|
|
||||||
# Try first to grant access based on global user roles
|
|
||||||
for role in self.getRoles():
|
for role in self.getRoles():
|
||||||
if role not in object_roles: continue
|
if role in allowedRoles: return True
|
||||||
if self._check_context(object): return 1
|
# Grant access based on local roles
|
||||||
return
|
localRoles = getattr(obj.aq_base, '__ac_local_roles__', None)
|
||||||
# Try then to grant access based on local roles
|
|
||||||
innerObject = getattr(object, 'aq_inner', object)
|
|
||||||
localRoles = getattr(innerObject, '__ac_local_roles__', None)
|
|
||||||
if not localRoles: return
|
if not localRoles: return
|
||||||
userId = self.getId()
|
# Gets the logins of this user and all its groups.
|
||||||
groups = getattr(self, 'groups', ())
|
userLogins = self.getLogins()
|
||||||
for id, roles in localRoles.iteritems():
|
for login, roles in localRoles.iteritems():
|
||||||
if (id != userId) and (id not in groups): continue
|
# Ignore logins not corresponding to this user.
|
||||||
|
if login not in logins: continue
|
||||||
for role in roles:
|
for role in roles:
|
||||||
if role not in object_roles: continue
|
if role in allowedRoles: return True
|
||||||
if self._check_context(object): return 1
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
from AccessControl.User import SimpleUser
|
|
||||||
SimpleUser.getRoles = getRoles
|
|
||||||
SimpleUser.getRolesInContext = getRolesInContext
|
|
||||||
SimpleUser.allowed = allowed
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -533,7 +533,7 @@ class AbstractWrapper(object):
|
||||||
</table>''')
|
</table>''')
|
||||||
|
|
||||||
pxView = Px('''
|
pxView = Px('''
|
||||||
<x var="x=zobj.allows('View', raiseError=True);
|
<x var="x=zobj.allows('read', raiseError=True);
|
||||||
errors=req.get('errors', {});
|
errors=req.get('errors', {});
|
||||||
layout=zobj.getPageLayout(layoutType);
|
layout=zobj.getPageLayout(layoutType);
|
||||||
phaseObj=zobj.getAppyPhases(currentOnly=True, layoutType='view');
|
phaseObj=zobj.getAppyPhases(currentOnly=True, layoutType='view');
|
||||||
|
@ -549,7 +549,7 @@ class AbstractWrapper(object):
|
||||||
</x>''', template=pxTemplate, hook='content')
|
</x>''', template=pxTemplate, hook='content')
|
||||||
|
|
||||||
pxEdit = Px('''
|
pxEdit = Px('''
|
||||||
<x var="x=zobj.allows('Modify portal content', raiseError=True);
|
<x var="x=zobj.allows('write', raiseError=True);
|
||||||
errors=req.get('errors', None) or {};
|
errors=req.get('errors', None) or {};
|
||||||
layout=zobj.getPageLayout(layoutType);
|
layout=zobj.getPageLayout(layoutType);
|
||||||
cssJs={};
|
cssJs={};
|
||||||
|
@ -625,9 +625,9 @@ class AbstractWrapper(object):
|
||||||
def _getParentAttr(klass, attr):
|
def _getParentAttr(klass, attr):
|
||||||
'''Gets value of p_attr on p_klass base classes (if this attr exists).
|
'''Gets value of p_attr on p_klass base classes (if this attr exists).
|
||||||
Scan base classes in the reverse order as Python does. Used by
|
Scan base classes in the reverse order as Python does. Used by
|
||||||
classmethods m_getWorkflow and m_getCreators below. Scanning base
|
classmethod m_getWorkflow below. Scanning base classes in reverse
|
||||||
classes in reverse order allows user-defined elements to override
|
order allows user-defined elements to override default Appy
|
||||||
default Appy elements.'''
|
elements.'''
|
||||||
i = len(klass.__bases__) - 1
|
i = len(klass.__bases__) - 1
|
||||||
res = None
|
res = None
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
|
@ -643,15 +643,6 @@ class AbstractWrapper(object):
|
||||||
if not res: res = WorkflowAnonymous
|
if not res: res = WorkflowAnonymous
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def getCreators(klass, cfg):
|
|
||||||
'''Returns the roles that are allowed to create instances of p_klass.
|
|
||||||
p_cfg is the product config that holds the default value.'''
|
|
||||||
res = klass._getParentAttr('creators')
|
|
||||||
# Return default creators if no creators was found.
|
|
||||||
if not res: res = cfg.appConfig.defaultCreators
|
|
||||||
return res
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getIndexes(klass, includeDefaults=True):
|
def getIndexes(klass, includeDefaults=True):
|
||||||
'''Returns a dict whose keys are the names of the indexes that are
|
'''Returns a dict whose keys are the names of the indexes that are
|
||||||
|
@ -823,7 +814,6 @@ class AbstractWrapper(object):
|
||||||
if isField:
|
if isField:
|
||||||
# Link the object to this one
|
# Link the object to this one
|
||||||
appyType.linkObject(self.o, zopeObj)
|
appyType.linkObject(self.o, zopeObj)
|
||||||
zopeObj._appy_managePermissions()
|
|
||||||
# Call custom initialization
|
# Call custom initialization
|
||||||
if externalData: param = externalData
|
if externalData: param = externalData
|
||||||
else: param = True
|
else: param = True
|
||||||
|
|
Loading…
Reference in a new issue