appy.gen: Ploneless version.
This commit is contained in:
parent
d0cbe7e573
commit
a321257e55
|
@ -2566,9 +2566,9 @@ class Transition:
|
||||||
comments=comment)
|
comments=comment)
|
||||||
# Update permissions-to-roles attributes
|
# Update permissions-to-roles attributes
|
||||||
targetState.updatePermissions(wf, obj)
|
targetState.updatePermissions(wf, obj)
|
||||||
# Refresh catalog-related security if required
|
# Reindex the object if required. Not only security-related indexes
|
||||||
if not obj.isTemporary():
|
# (Allowed, State) need to be updated here.
|
||||||
obj.reindex(indexes=('allowedRolesAndUsers', 'State'))
|
if not obj.isTemporary(): obj.reindex()
|
||||||
# Execute the related action if needed
|
# Execute the related action if needed
|
||||||
msg = ''
|
msg = ''
|
||||||
if doAction and self.action: msg = self.executeAction(obj, wf)
|
if doAction and self.action: msg = self.executeAction(obj, wf)
|
||||||
|
@ -2718,7 +2718,7 @@ class Config:
|
||||||
# If you want to replace the default front page with a page coming from
|
# If you want to replace the default front page with a page coming from
|
||||||
# your application, use the following parameter. Setting
|
# your application, use the following parameter. Setting
|
||||||
# frontPage = True will replace the Plone front page with a page
|
# frontPage = True will replace the Plone front page with a page
|
||||||
# whose content will come fron i18n label "front_page_text".
|
# whose content will come from i18n label "front_page_text".
|
||||||
self.frontPage = False
|
self.frontPage = False
|
||||||
# You can choose the Plone or Appy main template
|
# You can choose the Plone or Appy main template
|
||||||
self.frontPageTemplate = 'plone' # or "appy"
|
self.frontPageTemplate = 'plone' # or "appy"
|
||||||
|
|
|
@ -515,6 +515,33 @@ class UserClassDescriptor(ClassDescriptor):
|
||||||
def generateSchema(self):
|
def generateSchema(self):
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
ClassDescriptor.generateSchema(self, configClass=True)
|
||||||
|
|
||||||
|
class GroupClassDescriptor(ClassDescriptor):
|
||||||
|
'''Represents the class that corresponds to the Group for the generated
|
||||||
|
application.'''
|
||||||
|
def __init__(self, klass, generator):
|
||||||
|
ClassDescriptor.__init__(self,klass,klass._appy_attributes[:],generator)
|
||||||
|
self.modelClass = self.klass
|
||||||
|
self.predefined = True
|
||||||
|
self.customized = False
|
||||||
|
def getParents(self, allClasses=()):
|
||||||
|
res = ['Group']
|
||||||
|
if self.customized:
|
||||||
|
res.append('%s.%s' % (self.klass.__module__, self.klass.__name__))
|
||||||
|
return res
|
||||||
|
def update(self, klass, attributes):
|
||||||
|
'''This method is called by the generator when he finds a custom group
|
||||||
|
definition. We must then add the custom group elements in this
|
||||||
|
default Group descriptor.
|
||||||
|
|
||||||
|
NOTE: currently, it is not possible to define a custom Group
|
||||||
|
class.'''
|
||||||
|
self.orderedAttributes += attributes
|
||||||
|
self.klass = klass
|
||||||
|
self.customized = True
|
||||||
|
def isFolder(self, klass=None): return False
|
||||||
|
def generateSchema(self):
|
||||||
|
ClassDescriptor.generateSchema(self, configClass=True)
|
||||||
|
|
||||||
class TranslationClassDescriptor(ClassDescriptor):
|
class TranslationClassDescriptor(ClassDescriptor):
|
||||||
'''Represents the set of translation ids for a gen-application.'''
|
'''Represents the set of translation ids for a gen-application.'''
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,8 @@ from appy.gen.po import PoMessage, PoFile, PoParser
|
||||||
from appy.gen.generator import Generator as AbstractGenerator
|
from appy.gen.generator import Generator as AbstractGenerator
|
||||||
from appy.gen.utils import getClassName
|
from appy.gen.utils import getClassName
|
||||||
from appy.gen.descriptors import WorkflowDescriptor
|
from appy.gen.descriptors import WorkflowDescriptor
|
||||||
from descriptors import ClassDescriptor, ToolClassDescriptor, \
|
from descriptors import *
|
||||||
UserClassDescriptor, TranslationClassDescriptor
|
from model import ModelClass, User, Group, Tool, Translation
|
||||||
from model import ModelClass, User, Tool, Translation
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Generator(AbstractGenerator):
|
class Generator(AbstractGenerator):
|
||||||
|
@ -24,9 +23,10 @@ class Generator(AbstractGenerator):
|
||||||
AbstractGenerator.__init__(self, *args, **kwargs)
|
AbstractGenerator.__init__(self, *args, **kwargs)
|
||||||
# Set our own Descriptor classes
|
# Set our own Descriptor classes
|
||||||
self.descriptorClasses['class'] = ClassDescriptor
|
self.descriptorClasses['class'] = ClassDescriptor
|
||||||
# Create our own Tool, User and Translation instances
|
# Create our own Tool, User, Group and Translation instances
|
||||||
self.tool = ToolClassDescriptor(Tool, self)
|
self.tool = ToolClassDescriptor(Tool, self)
|
||||||
self.user = UserClassDescriptor(User, self)
|
self.user = UserClassDescriptor(User, self)
|
||||||
|
self.group = GroupClassDescriptor(Group, self)
|
||||||
self.translation = TranslationClassDescriptor(Translation, self)
|
self.translation = TranslationClassDescriptor(Translation, self)
|
||||||
# i18n labels to generate
|
# i18n labels to generate
|
||||||
self.labels = [] # i18n labels
|
self.labels = [] # i18n labels
|
||||||
|
@ -132,6 +132,7 @@ class Generator(AbstractGenerator):
|
||||||
msg('pdf', '', msg.FORMAT_PDF),
|
msg('pdf', '', msg.FORMAT_PDF),
|
||||||
msg('doc', '', msg.FORMAT_DOC),
|
msg('doc', '', msg.FORMAT_DOC),
|
||||||
msg('rtf', '', msg.FORMAT_RTF),
|
msg('rtf', '', msg.FORMAT_RTF),
|
||||||
|
msg('front_page_text', '', msg.FRONT_PAGE_TEXT),
|
||||||
]
|
]
|
||||||
# Create a label for every role added by this application
|
# Create a label for every role added by this application
|
||||||
for role in self.getAllUsedRoles():
|
for role in self.getAllUsedRoles():
|
||||||
|
@ -283,6 +284,27 @@ class Generator(AbstractGenerator):
|
||||||
if isBack: res += '.back'
|
if isBack: res += '.back'
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getClasses(self, include=None):
|
||||||
|
'''Returns the descriptors for all the classes in the generated
|
||||||
|
gen-application. If p_include is:
|
||||||
|
* "all" it includes the descriptors for the config-related
|
||||||
|
classes (tool, user, group, translation)
|
||||||
|
* "allButTool" it includes the same descriptors, the tool excepted
|
||||||
|
* "custom" it includes descriptors for the config-related classes
|
||||||
|
for which the user has created a sub-class.'''
|
||||||
|
if not include: return self.classes
|
||||||
|
res = self.classes[:]
|
||||||
|
configClasses = [self.tool, self.user, self.group, self.translation]
|
||||||
|
if include == 'all':
|
||||||
|
res += configClasses
|
||||||
|
elif include == 'allButTool':
|
||||||
|
res += configClasses[1:]
|
||||||
|
elif include == 'custom':
|
||||||
|
res += [c for c in configClasses if c.customized]
|
||||||
|
elif include == 'predefined':
|
||||||
|
res = configClasses
|
||||||
|
return res
|
||||||
|
|
||||||
def generateConfigureZcml(self):
|
def generateConfigureZcml(self):
|
||||||
'''Generates file configure.zcml.'''
|
'''Generates file configure.zcml.'''
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
|
@ -375,27 +397,6 @@ class Generator(AbstractGenerator):
|
||||||
repls['totalNumberOfTests'] = self.totalNumberOfTests
|
repls['totalNumberOfTests'] = self.totalNumberOfTests
|
||||||
self.copyFile('__init__.py', repls)
|
self.copyFile('__init__.py', repls)
|
||||||
|
|
||||||
def getClasses(self, include=None):
|
|
||||||
'''Returns the descriptors for all the classes in the generated
|
|
||||||
gen-application. If p_include is:
|
|
||||||
* "all" it includes the descriptors for the config-related
|
|
||||||
classes (tool, user, translation)
|
|
||||||
* "allButTool" it includes the same descriptors, the tool excepted
|
|
||||||
* "custom" it includes descriptors for the config-related classes
|
|
||||||
for which the user has created a sub-class.'''
|
|
||||||
if not include: return self.classes
|
|
||||||
res = self.classes[:]
|
|
||||||
configClasses = [self.tool, self.user, self.translation]
|
|
||||||
if include == 'all':
|
|
||||||
res += configClasses
|
|
||||||
elif include == 'allButTool':
|
|
||||||
res += configClasses[1:]
|
|
||||||
elif include == 'custom':
|
|
||||||
res += [c for c in configClasses if c.customized]
|
|
||||||
elif include == 'predefined':
|
|
||||||
res = configClasses
|
|
||||||
return res
|
|
||||||
|
|
||||||
def getClassesInOrder(self, allClasses):
|
def getClassesInOrder(self, allClasses):
|
||||||
'''When generating wrappers, classes mut be dumped in order (else, it
|
'''When generating wrappers, classes mut be dumped in order (else, it
|
||||||
generates forward references in the Python file, that does not
|
generates forward references in the Python file, that does not
|
||||||
|
@ -478,13 +479,14 @@ class Generator(AbstractGenerator):
|
||||||
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
||||||
self.labels.append(msg)
|
self.labels.append(msg)
|
||||||
|
|
||||||
# Tune the Ref field between Tool and User
|
# Tune the Ref field between Tool->User and Group->User
|
||||||
Tool.users.klass = User
|
Tool.users.klass = User
|
||||||
if self.user.customized:
|
if self.user.customized:
|
||||||
Tool.users.klass = self.user.klass
|
Tool.users.klass = self.user.klass
|
||||||
|
Group.users.klass = self.user.klass
|
||||||
|
|
||||||
# Generate the Tool-related classes (User, Translation)
|
# Generate the Tool-related classes (User, Group, Translation)
|
||||||
for klass in (self.user, self.translation):
|
for klass in (self.user, self.group, self.translation):
|
||||||
klassType = klass.name[len(self.applicationName):]
|
klassType = klass.name[len(self.applicationName):]
|
||||||
klass.generateSchema()
|
klass.generateSchema()
|
||||||
self.labels += [ Msg(klass.name, '', klassType),
|
self.labels += [ Msg(klass.name, '', klassType),
|
||||||
|
@ -492,8 +494,7 @@ class Generator(AbstractGenerator):
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
repls.update({'methods': klass.methods, 'genClassName': klass.name,
|
repls.update({'methods': klass.methods, 'genClassName': klass.name,
|
||||||
'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
|
'baseMixin':'BaseMixin', 'parents': 'BaseMixin, SimpleItem',
|
||||||
'classDoc': 'User class for %s' % self.applicationName,
|
'classDoc': 'Standard Appy class', 'icon':'object.gif'})
|
||||||
'icon':'object.gif'})
|
|
||||||
self.copyFile('Class.py', repls, destName='%s.py' % klass.name)
|
self.copyFile('Class.py', repls, destName='%s.py' % klass.name)
|
||||||
|
|
||||||
# Before generating the Tool class, finalize it with query result
|
# Before generating the Tool class, finalize it with query result
|
||||||
|
|
|
@ -85,28 +85,10 @@ class PloneInstaller:
|
||||||
site.invokeFactory(self.appyFolderType, self.productName,
|
site.invokeFactory(self.appyFolderType, self.productName,
|
||||||
title=self.productName)
|
title=self.productName)
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
||||||
# Manager has been granted Add permissions for all root classes.
|
|
||||||
# This may not be desired, so remove this.
|
|
||||||
appFolder = getattr(site, self.productName)
|
|
||||||
for className in self.config.rootClasses:
|
|
||||||
permission = self.getAddPermission(className)
|
|
||||||
appFolder.manage_permission(permission, (), acquire=0)
|
|
||||||
else:
|
else:
|
||||||
appFolder = getattr(site, self.productName)
|
appFolder = getattr(site, self.productName)
|
||||||
# All roles defined as creators should be able to create the
|
|
||||||
# corresponding root content types in this folder.
|
|
||||||
i = -1
|
|
||||||
allCreators = set()
|
|
||||||
for klass in self.appClasses:
|
|
||||||
i += 1
|
|
||||||
if not klass.__dict__.has_key('root') or not klass.__dict__['root']:
|
|
||||||
continue # It is not a root class
|
|
||||||
creators = getattr(klass, 'creators', None)
|
|
||||||
if not creators: creators = self.defaultAddRoles
|
|
||||||
allCreators = allCreators.union(creators)
|
|
||||||
className = self.appClassNames[i]
|
|
||||||
permission = self.getAddPermission(className)
|
|
||||||
updateRolesForPermission(permission, tuple(creators), appFolder)
|
|
||||||
# Beyond content-type-specific "add" permissions, creators must also
|
# Beyond content-type-specific "add" permissions, creators must also
|
||||||
# have the main permission "Add portal content".
|
# have the main permission "Add portal content".
|
||||||
permission = 'Add portal content'
|
permission = 'Add portal content'
|
||||||
|
@ -273,7 +255,8 @@ class ZopeInstaller:
|
||||||
indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex',
|
indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex',
|
||||||
'Title': 'TextIndex', 'SortableTitle': 'FieldIndex',
|
'Title': 'TextIndex', 'SortableTitle': 'FieldIndex',
|
||||||
'SearchableText': 'FieldIndex', 'Creator': 'FieldIndex',
|
'SearchableText': 'FieldIndex', 'Creator': 'FieldIndex',
|
||||||
'Created': 'DateIndex', 'ClassName': 'FieldIndex'}
|
'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
||||||
|
'Allowed': 'KeywordIndex'}
|
||||||
tool = self.app.config
|
tool = self.app.config
|
||||||
for className in self.config.attributes.iterkeys():
|
for className in self.config.attributes.iterkeys():
|
||||||
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
wrapperClass = tool.getAppyClass(className, wrapper=True)
|
||||||
|
@ -284,22 +267,49 @@ class ZopeInstaller:
|
||||||
indexInfo[indexName] = appyType.getIndexType()
|
indexInfo[indexName] = appyType.getIndexType()
|
||||||
self.installIndexes(indexInfo)
|
self.installIndexes(indexInfo)
|
||||||
|
|
||||||
|
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 root data folder if they do not exist.'''
|
||||||
# Create or update the base folder for storing data
|
# Create or update the base folder for storing data
|
||||||
zopeContent = self.app.objectIds()
|
zopeContent = self.app.objectIds()
|
||||||
if 'data' not in zopeContent: self.app.manage_addFolder('data')
|
|
||||||
|
if 'data' not in zopeContent:
|
||||||
|
self.app.manage_addFolder('data')
|
||||||
|
data = self.app.data
|
||||||
|
# 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
|
||||||
|
creators = getattr(klass, 'creators', None)
|
||||||
|
if not creators: creators = self.config.defaultAddRoles
|
||||||
|
className = self.config.appClassNames[i]
|
||||||
|
permission = self.getAddPermission(className)
|
||||||
|
updateRolesForPermission(permission, tuple(creators), data)
|
||||||
|
|
||||||
if 'config' not in zopeContent:
|
if 'config' not in zopeContent:
|
||||||
toolName = '%sTool' % self.productName
|
toolName = '%sTool' % self.productName
|
||||||
createObject(self.app, 'config', toolName,self.productName,wf=False)
|
createObject(self.app, 'config', toolName,self.productName,wf=False)
|
||||||
# Remove some default objects created by Zope but not useful
|
# 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',\
|
||||||
'standard_template.pt'):
|
'standard_template.pt'):
|
||||||
if name in zopeContent: self.app.manage_delObjects([name])
|
if name in zopeContent: self.app.manage_delObjects([name])
|
||||||
|
|
||||||
def installTool(self):
|
def installTool(self):
|
||||||
'''Updates the tool (now that the catalog is created) and updates its
|
'''Updates the tool (now that the catalog is created) and updates its
|
||||||
inner objects (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()
|
tool.refreshSecurity()
|
||||||
|
@ -307,10 +317,24 @@ class ZopeInstaller:
|
||||||
|
|
||||||
# Create the admin user if no user exists.
|
# Create the admin user if no user exists.
|
||||||
if not self.app.acl_users.getUsers():
|
if not self.app.acl_users.getUsers():
|
||||||
appyTool.create('users', name='min', firstName='ad',
|
self.app.acl_users._doAddUser('admin', 'admin', ['Manager'], ())
|
||||||
login='admin', password1='admin',
|
|
||||||
password2='admin', roles=['Manager'])
|
|
||||||
appyTool.log('Admin user "admin" created.')
|
appyTool.log('Admin user "admin" created.')
|
||||||
|
|
||||||
|
# Create group "admins" if it does not exist
|
||||||
|
if not appyTool.count('Group', login='admins'):
|
||||||
|
appyTool.create('groups', login='admins', title='Administrators',
|
||||||
|
roles=['Manager'])
|
||||||
|
appyTool.log('Group "admins" created.')
|
||||||
|
|
||||||
|
# Create a group for every global role defined in the application
|
||||||
|
for role in self.config.applicationGlobalRoles:
|
||||||
|
relatedGroup = '%s_group' % role
|
||||||
|
if appyTool.count('Group', login=relatedGroup): continue
|
||||||
|
appyTool.create('groups', login=relatedGroup, title=relatedGroup,
|
||||||
|
roles=[role])
|
||||||
|
appyTool.log('Group "%s", related to global role "%s", was ' \
|
||||||
|
'created.' % (relatedGroup, role))
|
||||||
|
|
||||||
# Create POD templates within the tool if required
|
# Create POD templates within the tool if required
|
||||||
for contentType in self.config.attributes.iterkeys():
|
for contentType in self.config.attributes.iterkeys():
|
||||||
appyClass = tool.getAppyClass(contentType)
|
appyClass = tool.getAppyClass(contentType)
|
||||||
|
@ -426,9 +450,16 @@ class ZopeInstaller:
|
||||||
continue # Back refs are initialised within fw refs
|
continue # Back refs are initialised within fw refs
|
||||||
appyType.init(name, baseClass, appName)
|
appyType.init(name, baseClass, appName)
|
||||||
|
|
||||||
|
def installRoles(self):
|
||||||
|
'''Installs the application-specific roles if not already done.'''
|
||||||
|
roles = list(self.app.__ac_roles__)
|
||||||
|
for role in self.config.applicationRoles:
|
||||||
|
if role not in roles: roles.append(role)
|
||||||
|
self.app.__ac_roles__ = tuple(roles)
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
self.logger.info('is being installed...')
|
self.logger.info('is being installed...')
|
||||||
# Create the "admin" user if no user is present in the database
|
self.installRoles()
|
||||||
self.installAppyTypes()
|
self.installAppyTypes()
|
||||||
self.installZopeClasses()
|
self.installZopeClasses()
|
||||||
self.enableUserTracking()
|
self.enableUserTracking()
|
||||||
|
|
|
@ -30,6 +30,7 @@ class ToolMixin(BaseMixin):
|
||||||
if res.find('Extensions_appyWrappers') != -1:
|
if res.find('Extensions_appyWrappers') != -1:
|
||||||
elems = res.split('_')
|
elems = res.split('_')
|
||||||
res = '%s%s' % (elems[1], elems[4])
|
res = '%s%s' % (elems[1], elems[4])
|
||||||
|
if res in ('User', 'Group', 'Translation'): res = appName + res
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getCatalog(self):
|
def getCatalog(self):
|
||||||
|
@ -212,6 +213,17 @@ class ToolMixin(BaseMixin):
|
||||||
if not appy: return res
|
if not appy: return res
|
||||||
return res.appy()
|
return res.appy()
|
||||||
|
|
||||||
|
def getAllowedValue(self):
|
||||||
|
'''Gets, for the currently logged user, the value for index
|
||||||
|
"Allowed".'''
|
||||||
|
user = self.getUser()
|
||||||
|
res = ['user:%s' % user.getId(), 'Anonymous'] + user.getRoles()
|
||||||
|
try:
|
||||||
|
res += ['user:%s' % g for g in user.groups.keys()]
|
||||||
|
except AttributeError, ae:
|
||||||
|
pass # The Zope admin does not have this attribute.
|
||||||
|
return res
|
||||||
|
|
||||||
def executeQuery(self, className, searchName=None, startNumber=0,
|
def executeQuery(self, className, searchName=None, startNumber=0,
|
||||||
search=None, remember=False, brainsOnly=False,
|
search=None, remember=False, brainsOnly=False,
|
||||||
maxResults=None, noSecurity=False, sortBy=None,
|
maxResults=None, noSecurity=False, sortBy=None,
|
||||||
|
@ -303,10 +315,9 @@ class ToolMixin(BaseMixin):
|
||||||
if refObject:
|
if refObject:
|
||||||
refField = refObject.getAppyType(refField)
|
refField = refObject.getAppyType(refField)
|
||||||
params['UID'] = getattr(refObject, refField.name).data
|
params['UID'] = getattr(refObject, refField.name).data
|
||||||
# Determine what method to call on the portal catalog
|
# Use index "Allowed" if noSecurity is False
|
||||||
if noSecurity: catalogMethod = 'unrestrictedSearchResults'
|
if not noSecurity: params['Allowed'] = self.getAllowedValue()
|
||||||
else: catalogMethod = 'searchResults'
|
brains = self.getPath("/catalog")(**params)
|
||||||
exec 'brains = self.getPath("/catalog").%s(**params)' % catalogMethod
|
|
||||||
if brainsOnly:
|
if brainsOnly:
|
||||||
# Return brains only.
|
# Return brains only.
|
||||||
if not maxResults: return brains
|
if not maxResults: return brains
|
||||||
|
|
|
@ -87,6 +87,8 @@ class BaseMixin:
|
||||||
if field.type != 'Ref': continue
|
if field.type != 'Ref': continue
|
||||||
for obj in field.getValue(self):
|
for obj in field.getValue(self):
|
||||||
field.back.unlinkObject(obj, self, back=True)
|
field.back.unlinkObject(obj, self, back=True)
|
||||||
|
# Uncatalog the object
|
||||||
|
self.reindex(unindex=True)
|
||||||
# Delete the object
|
# Delete the object
|
||||||
self.getParentNode().manage_delObjects([self.id])
|
self.getParentNode().manage_delObjects([self.id])
|
||||||
|
|
||||||
|
@ -306,22 +308,12 @@ class BaseMixin:
|
||||||
url = self.absolute_url_path()
|
url = self.absolute_url_path()
|
||||||
catalog = self.getPhysicalRoot().catalog
|
catalog = self.getPhysicalRoot().catalog
|
||||||
if unindex:
|
if unindex:
|
||||||
method = catalog.uncatalog_object
|
catalog.uncatalog_object(url)
|
||||||
else:
|
else:
|
||||||
method = catalog.catalog_object
|
|
||||||
if indexes:
|
if indexes:
|
||||||
return method(self, url)
|
catalog.catalog_object(self, url, idxs=indexes)
|
||||||
else:
|
else:
|
||||||
return method(self, url, idxs=indexes)
|
catalog.catalog_object(self, url)
|
||||||
|
|
||||||
def unindex(self, indexes=None):
|
|
||||||
'''Undatalog this object.'''
|
|
||||||
url = self.absolute_url_path()
|
|
||||||
catalog = self.getPhysicalRoot().catalog
|
|
||||||
if indexes:
|
|
||||||
return catalog.catalog_object(self, url)
|
|
||||||
else:
|
|
||||||
return catalog.catalog_object(self, url, idxs=indexes)
|
|
||||||
|
|
||||||
def say(self, msg, type='info'):
|
def say(self, msg, type='info'):
|
||||||
'''Prints a p_msg in the user interface. p_logLevel may be "info",
|
'''Prints a p_msg in the user interface. p_logLevel may be "info",
|
||||||
|
@ -1170,6 +1162,23 @@ class BaseMixin:
|
||||||
'''Returns the name of the (Zope) class for self.'''
|
'''Returns the name of the (Zope) class for self.'''
|
||||||
return self.portal_type
|
return self.portal_type
|
||||||
|
|
||||||
|
def Allowed(self):
|
||||||
|
'''Returns the list of roles and users that are allowed to view this
|
||||||
|
object. This index value will be used within catalog queries for
|
||||||
|
filtering objects the user is allowed to see.'''
|
||||||
|
res = set()
|
||||||
|
# Get, from the workflow, roles having permission 'View'.
|
||||||
|
for role in self.getProductConfig().rolesForPermissionOn('View', self):
|
||||||
|
res.add(role)
|
||||||
|
# Add users having, locally, this role on this object.
|
||||||
|
localRoles = getattr(self, '__ac_local_roles__', None)
|
||||||
|
if not localRoles: return list(res)
|
||||||
|
for id, roles in localRoles.iteritems():
|
||||||
|
for role in roles:
|
||||||
|
if role in res:
|
||||||
|
res.add('user:%s' % id)
|
||||||
|
return list(res)
|
||||||
|
|
||||||
def _appy_showState(self, workflow, stateShow):
|
def _appy_showState(self, workflow, stateShow):
|
||||||
'''Must I show a state whose "show value" is p_stateShow?'''
|
'''Must I show a state whose "show value" is p_stateShow?'''
|
||||||
if callable(stateShow):
|
if callable(stateShow):
|
||||||
|
@ -1280,6 +1289,24 @@ class BaseMixin:
|
||||||
return nobody
|
return nobody
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
def getTool(self):
|
||||||
|
'''Returns the application tool.'''
|
||||||
|
return self.getPhysicalRoot().config
|
||||||
|
|
||||||
|
def getProductConfig(self):
|
||||||
|
'''Returns a reference to the config module.'''
|
||||||
|
return self.__class__.config
|
||||||
|
|
||||||
|
def index_html(self):
|
||||||
|
"""Redirects to /ui. Transfers the status message if any."""
|
||||||
|
rq = self.REQUEST
|
||||||
|
msg = rq.get('portal_status_message', '')
|
||||||
|
if msg:
|
||||||
|
url = self.getUrl(portal_status_message=msg)
|
||||||
|
else:
|
||||||
|
url = self.getUrl()
|
||||||
|
return rq.RESPONSE.redirect(url)
|
||||||
|
|
||||||
def userIsAnon(self):
|
def userIsAnon(self):
|
||||||
'''Is the currently logged user anonymous ?'''
|
'''Is the currently logged user anonymous ?'''
|
||||||
return self.getUser().getUserName() == 'Anonymous User'
|
return self.getUser().getUserName() == 'Anonymous User'
|
||||||
|
@ -1406,22 +1433,7 @@ class BaseMixin:
|
||||||
|
|
||||||
def allows(self, permission):
|
def allows(self, permission):
|
||||||
'''Has the logged user p_permission on p_self ?'''
|
'''Has the logged user p_permission on p_self ?'''
|
||||||
# Get first the roles that have this permission on p_self.
|
return self.getUser().has_permission(permission, self)
|
||||||
zopeAttr = Permission.getZopeAttrName(permission)
|
|
||||||
if not hasattr(self.aq_base, zopeAttr): return
|
|
||||||
allowedRoles = getattr(self.aq_base, zopeAttr)
|
|
||||||
# Has the user one of those roles?
|
|
||||||
user = self.getUser()
|
|
||||||
# XXX no groups at present
|
|
||||||
#ids = [user.getId()] + user.getGroups()
|
|
||||||
ids = [user.getId()]
|
|
||||||
userGlobalRoles = user.getRoles()
|
|
||||||
for role in allowedRoles:
|
|
||||||
# Has the user this role ? Check in the local roles first.
|
|
||||||
for id, roles in self.__ac_local_roles__.iteritems():
|
|
||||||
if (role in roles) and (id in ids): return True
|
|
||||||
# Check then in the global roles.
|
|
||||||
if role in userGlobalRoles: return True
|
|
||||||
|
|
||||||
def getEditorInit(self, name):
|
def getEditorInit(self, name):
|
||||||
'''Gets the Javascrit init code for displaying a rich editor for
|
'''Gets the Javascrit init code for displaying a rich editor for
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import types
|
import types
|
||||||
from appy.gen import *
|
from appy.gen import *
|
||||||
|
Grp=Group # Avoid name clash between appy.gen.Group and class Group below
|
||||||
|
|
||||||
# Prototypical instances of every type -----------------------------------------
|
# Prototypical instances of every type -----------------------------------------
|
||||||
class Protos:
|
class Protos:
|
||||||
|
@ -83,8 +84,8 @@ class ModelClass:
|
||||||
value = '%s.%s' % (moduleName, value.__name__)
|
value = '%s.%s' % (moduleName, value.__name__)
|
||||||
elif isinstance(value, Selection):
|
elif isinstance(value, Selection):
|
||||||
value = 'Selection("%s")' % value.methodName
|
value = 'Selection("%s")' % value.methodName
|
||||||
elif isinstance(value, Group):
|
elif isinstance(value, Grp):
|
||||||
value = 'Group("%s")' % value.name
|
value = 'Grp("%s")' % value.name
|
||||||
elif isinstance(value, Page):
|
elif isinstance(value, Page):
|
||||||
value = 'pages["%s"]' % value.name
|
value = 'pages["%s"]' % value.name
|
||||||
elif callable(value):
|
elif callable(value):
|
||||||
|
@ -149,6 +150,23 @@ class User(ModelClass):
|
||||||
gm['multiplicity'] = (0, None)
|
gm['multiplicity'] = (0, None)
|
||||||
roles = String(validator=Selection('getGrantableRoles'), indexed=True, **gm)
|
roles = String(validator=Selection('getGrantableRoles'), indexed=True, **gm)
|
||||||
|
|
||||||
|
# The Group class --------------------------------------------------------------
|
||||||
|
class Group(ModelClass):
|
||||||
|
# In a ModelClass we need to declare attributes in the following list.
|
||||||
|
_appy_attributes = ['title', 'login', 'roles', 'users']
|
||||||
|
# All methods defined below are fake. Real versions are in the wrapper.
|
||||||
|
m = {'group': 'main', 'width': 25, 'indexed': True}
|
||||||
|
title = String(multiplicity=(1,1), **m)
|
||||||
|
def showLogin(self): pass
|
||||||
|
def validateLogin(self): pass
|
||||||
|
login = String(show=showLogin, validator=validateLogin,
|
||||||
|
multiplicity=(1,1), **m)
|
||||||
|
roles = String(validator=Selection('getGrantableRoles'),
|
||||||
|
multiplicity=(0,None), **m)
|
||||||
|
users = Ref(User, multiplicity=(0,None), add=False, link=True,
|
||||||
|
back=Ref(attribute='groups', show=True),
|
||||||
|
showHeaders=True, shownInfo=('title', 'login'))
|
||||||
|
|
||||||
# The Translation class --------------------------------------------------------
|
# The Translation class --------------------------------------------------------
|
||||||
class Translation(ModelClass):
|
class Translation(ModelClass):
|
||||||
_appy_attributes = ['po', 'title']
|
_appy_attributes = ['po', 'title']
|
||||||
|
@ -166,7 +184,7 @@ toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns',
|
||||||
'enableAdvancedSearch', 'numberOfSearchColumns',
|
'enableAdvancedSearch', 'numberOfSearchColumns',
|
||||||
'searchFields', 'optionalFields', 'showWorkflow',
|
'searchFields', 'optionalFields', 'showWorkflow',
|
||||||
'showWorkflowCommentField', 'showAllStatesInPhase')
|
'showWorkflowCommentField', 'showAllStatesInPhase')
|
||||||
defaultToolFields = ('users', 'translations', 'enableNotifications',
|
defaultToolFields = ('users', 'groups', 'translations', 'enableNotifications',
|
||||||
'unoEnabledPython', 'openOfficePort',
|
'unoEnabledPython', 'openOfficePort',
|
||||||
'numberOfResultsPerPage', 'listBoxesMaximumWidth',
|
'numberOfResultsPerPage', 'listBoxesMaximumWidth',
|
||||||
'appyVersion', 'refreshSecurity')
|
'appyVersion', 'refreshSecurity')
|
||||||
|
@ -187,13 +205,20 @@ class Tool(ModelClass):
|
||||||
refreshSecurity = Action(action=refreshSecurity, confirm=True)
|
refreshSecurity = Action(action=refreshSecurity, confirm=True)
|
||||||
# Ref(User) will maybe be transformed into Ref(CustomUserClass).
|
# Ref(User) will maybe be transformed into Ref(CustomUserClass).
|
||||||
users = Ref(User, multiplicity=(0,None), add=True, link=False,
|
users = Ref(User, multiplicity=(0,None), add=True, link=False,
|
||||||
back=Ref(attribute='toTool', show=False), page='users',
|
back=Ref(attribute='toTool', show=False),
|
||||||
queryable=True, queryFields=('login',), showHeaders=True,
|
page=Page('users', show='view'),
|
||||||
shownInfo=('login', 'title', 'roles'))
|
queryable=True, queryFields=('title', 'login'),
|
||||||
|
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
||||||
|
groups = Ref(Group, multiplicity=(0,None), add=True, link=False,
|
||||||
|
back=Ref(attribute='toTool2', show=False),
|
||||||
|
page=Page('groups', show='view'),
|
||||||
|
queryable=True, queryFields=('title', 'login'),
|
||||||
|
showHeaders=True, shownInfo=('title', 'login', 'roles'))
|
||||||
translations = Ref(Translation, multiplicity=(0,None),add=False,link=False,
|
translations = Ref(Translation, multiplicity=(0,None),add=False,link=False,
|
||||||
back=Ref(attribute='trToTool', show=False), show='view',
|
back=Ref(attribute='trToTool', show=False), show='view',
|
||||||
page=Page('translations', show='view'))
|
page=Page('translations', show='view'))
|
||||||
enableNotifications = Boolean(default=True, page='notifications')
|
enableNotifications = Boolean(default=True,
|
||||||
|
page=Page('notifications', show=False))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _appy_clean(klass):
|
def _appy_clean(klass):
|
||||||
|
|
|
@ -23,21 +23,11 @@ class <!genClassName!>(<!parents!>):
|
||||||
global_allow = 1
|
global_allow = 1
|
||||||
icon = "ui/<!icon!>"
|
icon = "ui/<!icon!>"
|
||||||
wrapperClass = Wrapper
|
wrapperClass = Wrapper
|
||||||
for elem in dir(<!baseMixin!>):
|
config = cfg
|
||||||
if not elem.startswith('__'): security.declarePublic(elem)
|
|
||||||
def getTool(self): return self.getPhysicalRoot().config
|
|
||||||
def getProductConfig(self): return cfg
|
|
||||||
def index_html(self):
|
|
||||||
"""Redirects to /ui. Transfers the status message if any."""
|
|
||||||
rq = self.REQUEST
|
|
||||||
msg = rq.get('portal_status_message', '')
|
|
||||||
if msg:
|
|
||||||
url = self.getUrl(portal_status_message=msg)
|
|
||||||
else:
|
|
||||||
url = self.getUrl()
|
|
||||||
return rq.RESPONSE.redirect(url)
|
|
||||||
def do(self):
|
def do(self):
|
||||||
'''BaseMixin.do can't be traversed by Zope if this class is the tool.
|
'''BaseMixin.do can't be traversed by Zope if this class is the tool.
|
||||||
So here, we redefine this method.'''
|
So here, we redefine this method.'''
|
||||||
return BaseMixin.do(self)
|
return BaseMixin.do(self)
|
||||||
|
for elem in dir(<!baseMixin!>):
|
||||||
|
if not elem.startswith('__'): security.declarePublic(elem)
|
||||||
<!methods!>
|
<!methods!>
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy.gen import *
|
from appy.gen import *
|
||||||
|
Grp = Group # Avoid name clashes with the Group class below and appy.gen.Group
|
||||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||||
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool
|
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool
|
||||||
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser
|
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser
|
||||||
|
from appy.gen.plone25.wrappers.GroupWrapper import GroupWrapper as WGroup
|
||||||
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT
|
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT
|
||||||
from Globals import InitializeClass
|
from Globals import InitializeClass
|
||||||
from AccessControl import ClassSecurityInfo
|
from AccessControl import ClassSecurityInfo
|
||||||
|
@ -10,6 +12,7 @@ tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
||||||
<!imports!>
|
<!imports!>
|
||||||
|
|
||||||
<!User!>
|
<!User!>
|
||||||
|
<!Group!>
|
||||||
<!Translation!>
|
<!Translation!>
|
||||||
<!Tool!>
|
<!Tool!>
|
||||||
<!wrappers!>
|
<!wrappers!>
|
||||||
|
|
|
@ -14,6 +14,7 @@ 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
|
||||||
|
|
74
gen/plone25/wrappers/GroupWrapper.py
Normal file
74
gen/plone25/wrappers/GroupWrapper.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class GroupWrapper(AbstractWrapper):
|
||||||
|
|
||||||
|
def showLogin(self):
|
||||||
|
'''When must we show the login field?'''
|
||||||
|
if self.o.isTemporary(): return 'edit'
|
||||||
|
return 'view'
|
||||||
|
|
||||||
|
def validateLogin(self, login):
|
||||||
|
'''Is this p_login valid?'''
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getGrantableRoles(self):
|
||||||
|
'''Returns the list of roles that the admin can grant to a user.'''
|
||||||
|
res = []
|
||||||
|
for role in self.o.getProductConfig().grantableRoles:
|
||||||
|
res.append( (role, self.translate('role_%s' % role)) )
|
||||||
|
return res
|
||||||
|
|
||||||
|
def validate(self, new, errors):
|
||||||
|
'''Inter-field validation.'''
|
||||||
|
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):
|
||||||
|
'''Adds a p_user to this group.'''
|
||||||
|
# Update the Ref field.
|
||||||
|
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):
|
||||||
|
'''Removes a p_user from this group.'''
|
||||||
|
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):
|
||||||
|
# Create or update, on every Zope user of this group, group-related
|
||||||
|
# information.
|
||||||
|
# 1. Remove reference to this group for users that were removed from it
|
||||||
|
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
|
||||||
|
return self._callCustom('onEdit', created)
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -12,24 +12,18 @@ class UserWrapper(AbstractWrapper):
|
||||||
def validateLogin(self, login):
|
def validateLogin(self, login):
|
||||||
'''Is this p_login valid?'''
|
'''Is this p_login valid?'''
|
||||||
# The login can't be the id of the whole site or "admin"
|
# The login can't be the id of the whole site or "admin"
|
||||||
if (login == self.o.portal_url.getPortalObject().getId()) or \
|
if login == 'admin':
|
||||||
(login == 'admin'):
|
return self.translate('This username is reserved.')
|
||||||
return self.translate(u'This username is reserved. Please choose ' \
|
# Check that no user or group already uses this login.
|
||||||
'a different name.', domain='plone')
|
if self.count('User', login=login) or self.count('Group', login=login):
|
||||||
# Check that the login does not already exist and check some
|
return self.translate('This login is already in use.')
|
||||||
# Plone-specific rules.
|
|
||||||
pr = self.o.portal_registration
|
|
||||||
if not pr.isMemberIdAllowed(login):
|
|
||||||
return self.translate(u'The login name you selected is already ' \
|
|
||||||
'in use or is not valid. Please choose another.', domain='plone')
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def validatePassword(self, password):
|
def validatePassword(self, password):
|
||||||
'''Is this p_password valid?'''
|
'''Is this p_password valid?'''
|
||||||
# Password must be at least 5 chars length
|
# Password must be at least 5 chars length
|
||||||
if len(password) < 5:
|
if len(password) < 5:
|
||||||
return self.translate(u'Passwords must contain at least 5 letters.',
|
return self.translate('Passwords must contain at least 5 letters.')
|
||||||
domain='plone')
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def showPassword(self):
|
def showPassword(self):
|
||||||
|
@ -49,7 +43,7 @@ class UserWrapper(AbstractWrapper):
|
||||||
page = self.request.get('page', 'main')
|
page = self.request.get('page', 'main')
|
||||||
if page == 'main':
|
if page == 'main':
|
||||||
if hasattr(new, 'password1') and (new.password1 != new.password2):
|
if hasattr(new, 'password1') and (new.password1 != new.password2):
|
||||||
msg = self.translate(u'Passwords do not match.', domain='plone')
|
msg = self.translate('Passwords do not match.')
|
||||||
errors.password1 = msg
|
errors.password1 = msg
|
||||||
errors.password2 = msg
|
errors.password2 = msg
|
||||||
return self._callCustom('validate', new, errors)
|
return self._callCustom('validate', new, errors)
|
||||||
|
@ -61,41 +55,104 @@ class UserWrapper(AbstractWrapper):
|
||||||
if created:
|
if created:
|
||||||
# Create the corresponding Zope user
|
# Create the corresponding Zope user
|
||||||
aclUsers._doAddUser(login, self.password1, self.roles, ())
|
aclUsers._doAddUser(login, self.password1, self.roles, ())
|
||||||
|
zopeUser = aclUsers.getUser(login)
|
||||||
# Remove our own password copies
|
# Remove our own password copies
|
||||||
self.password1 = self.password2 = ''
|
self.password1 = self.password2 = ''
|
||||||
# Perform updates on the corresponding Plone user
|
from persistent.mapping import PersistentMapping
|
||||||
|
# The following dict will store, for every group, global roles
|
||||||
|
# granted to it.
|
||||||
|
zopeUser.groups = PersistentMapping()
|
||||||
|
else:
|
||||||
|
# Updates roles at the Zope level.
|
||||||
zopeUser = aclUsers.getUserById(login)
|
zopeUser = aclUsers.getUserById(login)
|
||||||
# This object must be owned by its Plone user
|
zopeUser.roles = self.roles
|
||||||
|
# "self" must be owned by its Zope user
|
||||||
if 'Owner' not in self.o.get_local_roles_for_userid(login):
|
if 'Owner' not in self.o.get_local_roles_for_userid(login):
|
||||||
self.o.manage_addLocalRoles(login, ('Owner',))
|
self.o.manage_addLocalRoles(login, ('Owner',))
|
||||||
# Change group membership according to self.roles. Indeed, instead of
|
|
||||||
# granting roles directly to the user, we will add the user to a
|
|
||||||
# Appy-created group having this role.
|
|
||||||
userRoles = self.roles
|
|
||||||
#userGroups = zopeUser.getGroups()
|
|
||||||
# for role in self.o.getProductConfig().grantableRoles:
|
|
||||||
# # Retrieve the group corresponding to this role
|
|
||||||
# groupName = '%s_group' % role
|
|
||||||
# if role == 'Manager': groupName = 'Administrators'
|
|
||||||
# elif role == 'Reviewer': groupName = 'Reviewers'
|
|
||||||
# group = self.o.portal_groups.getGroupById(groupName)
|
|
||||||
# # Add or remove the user from this group according to its role(s).
|
|
||||||
# if role in userRoles:
|
|
||||||
# # Add the user if not already present in the group
|
|
||||||
# if groupName not in userGroups:
|
|
||||||
# group.addMember(self.login)
|
|
||||||
# else:
|
|
||||||
# # Remove the user if it was in the corresponding group
|
|
||||||
# if groupName in userGroups:
|
|
||||||
# group.removeMember(self.login)
|
|
||||||
return self._callCustom('onEdit', created)
|
return self._callCustom('onEdit', created)
|
||||||
|
|
||||||
|
def getZopeUser(self):
|
||||||
|
'''Gets the Zope user corresponding to this user.'''
|
||||||
|
return self.o.acl_users.getUser(self.login)
|
||||||
|
|
||||||
def onDelete(self):
|
def onDelete(self):
|
||||||
'''Before deleting myself, I must delete the corresponding Plone
|
'''Before deleting myself, I must delete the corresponding Zope user.'''
|
||||||
user.'''
|
self.o.acl_users._doDelUsers([self.login])
|
||||||
# Delete the corresponding Plone user
|
self.log('User "%s" deleted.' % self.login)
|
||||||
self.o.acl_users._doDelUser(self.login)
|
|
||||||
self.log('Plone user "%s" deleted.' % self.login)
|
|
||||||
# Call a custom "onDelete" if any.
|
# Call a custom "onDelete" if any.
|
||||||
return self._callCustom('onDelete')
|
return self._callCustom('onDelete')
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
try:
|
||||||
|
from AccessControl.PermissionRole import _what_not_even_god_should_do, \
|
||||||
|
rolesForPermissionOn
|
||||||
|
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)
|
||||||
|
# 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).'''
|
||||||
|
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: res.add(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():
|
||||||
|
if role not in object_roles: continue
|
||||||
|
if self._check_context(object): return 1
|
||||||
|
return
|
||||||
|
# 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
|
||||||
|
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 object_roles: continue
|
||||||
|
if self._check_context(object): return 1
|
||||||
|
return
|
||||||
|
|
||||||
|
from AccessControl.User import SimpleUser
|
||||||
|
SimpleUser.getRoles = getRoles
|
||||||
|
SimpleUser.getRolesInContext = getRolesInContext
|
||||||
|
SimpleUser.allowed = allowed
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -54,6 +54,8 @@ class AbstractWrapper(object):
|
||||||
return o.workflow_history[key]
|
return o.workflow_history[key]
|
||||||
elif name == 'user':
|
elif name == 'user':
|
||||||
return self.o.getUser()
|
return self.o.getUser()
|
||||||
|
elif name == 'appyUser':
|
||||||
|
return self.search('User', login=self.o.getUser().getId())[0]
|
||||||
elif name == 'fields': return self.o.getAllAppyTypes()
|
elif name == 'fields': return self.o.getAllAppyTypes()
|
||||||
# Now, let's try to return a real attribute.
|
# Now, let's try to return a real attribute.
|
||||||
res = object.__getattribute__(self, name)
|
res = object.__getattribute__(self, name)
|
||||||
|
|
Loading…
Reference in a new issue