appy.gen: reworked management of Ref fields, that do not use Archetypes and reference catalog anymore. appy.gen: added a mechanism for migrating from one Appy version to another, automatically, when reinstalling an Appy application.
This commit is contained in:
parent
eceb9175fd
commit
93619dafe1
|
@ -5,6 +5,7 @@ import sys, os.path
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
from appy.gen.generator import GeneratorError
|
from appy.gen.generator import GeneratorError
|
||||||
from appy.shared.utils import LinesCounter
|
from appy.shared.utils import LinesCounter
|
||||||
|
import appy.version
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
ERROR_CODE = 1
|
ERROR_CODE = 1
|
||||||
|
@ -103,6 +104,7 @@ class GeneratorScript:
|
||||||
(options, args) = optParser.parse_args()
|
(options, args) = optParser.parse_args()
|
||||||
try:
|
try:
|
||||||
self.manageArgs(optParser, options, args)
|
self.manageArgs(optParser, options, args)
|
||||||
|
print 'Appy version:', appy.version.verbose
|
||||||
print 'Generating %s product in %s...' % (args[1], args[2])
|
print 'Generating %s product in %s...' % (args[1], args[2])
|
||||||
self.generateProduct(options, *args)
|
self.generateProduct(options, *args)
|
||||||
# Give the user some statistics about its code
|
# Give the user some statistics about its code
|
||||||
|
|
|
@ -528,7 +528,6 @@ class Type:
|
||||||
# We must initialise the corresponding back reference
|
# We must initialise the corresponding back reference
|
||||||
self.back.klass = klass
|
self.back.klass = klass
|
||||||
self.back.init(self.back.attribute, self.klass, appName)
|
self.back.init(self.back.attribute, self.klass, appName)
|
||||||
self.back.relationship = '%s_%s_rel' % (prefix, name)
|
|
||||||
|
|
||||||
def reload(self, klass, obj):
|
def reload(self, klass, obj):
|
||||||
'''In debug mode, we want to reload layouts without restarting Zope.
|
'''In debug mode, we want to reload layouts without restarting Zope.
|
||||||
|
@ -1735,18 +1734,16 @@ class Ref(Type):
|
||||||
if (layoutType == 'edit') and self.add: return False
|
if (layoutType == 'edit') and self.add: return False
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
if layoutType == 'edit': return False
|
if layoutType == 'edit': return False
|
||||||
else:
|
else: return getattr(obj, self.name, None)
|
||||||
return obj.getBRefs(self.relationship)
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getValue(self, obj, type='objects', noListIfSingleObj=False,
|
def getValue(self, obj, type='objects', noListIfSingleObj=False,
|
||||||
startNumber=None, someObjects=False):
|
startNumber=None, someObjects=False):
|
||||||
'''Returns the objects linked to p_obj through Ref field "self".
|
'''Returns the objects linked to p_obj through this Ref field.
|
||||||
- If p_type is "objects", it returns the Appy wrappers;
|
- If p_type is "objects", it returns the Appy wrappers;
|
||||||
- If p_type is "zobjects", it returns the Zope objects;
|
- If p_type is "zobjects", it returns the Zope objects;
|
||||||
- If p_type is "uids", it returns UIDs of objects (= strings).
|
- If p_type is "uids", it returns UIDs of objects (= strings).
|
||||||
|
|
||||||
|
|
||||||
* If p_startNumber is None, it returns all referred objects.
|
* If p_startNumber is None, it returns all referred objects.
|
||||||
* If p_startNumber is a number, it returns self.maxPerPage objects,
|
* If p_startNumber is a number, it returns self.maxPerPage objects,
|
||||||
starting at p_startNumber.
|
starting at p_startNumber.
|
||||||
|
@ -1756,23 +1753,7 @@ class Ref(Type):
|
||||||
|
|
||||||
If p_someObjects is True, it returns an instance of SomeObjects
|
If p_someObjects is True, it returns an instance of SomeObjects
|
||||||
instead of returning a list of references.'''
|
instead of returning a list of references.'''
|
||||||
if self.isBack:
|
uids = getattr(obj, self.name, [])
|
||||||
getRefs = obj.reference_catalog.getBackReferences
|
|
||||||
uids = [r.sourceUID for r in getRefs(obj, self.relationship)]
|
|
||||||
else:
|
|
||||||
uids = obj._appy_getSortedField(self.name)
|
|
||||||
batchNeeded = startNumber != None
|
|
||||||
exec 'refUids = obj.getRaw%s%s()' % (self.name[0].upper(),
|
|
||||||
self.name[1:])
|
|
||||||
# There may be too much UIDs in sortedField because these fields
|
|
||||||
# are not updated when objects are deleted. So we do it now.
|
|
||||||
# TODO: do such cleaning on object deletion ?
|
|
||||||
toDelete = []
|
|
||||||
for uid in uids:
|
|
||||||
if uid not in refUids:
|
|
||||||
toDelete.append(uid)
|
|
||||||
for uid in toDelete:
|
|
||||||
uids.remove(uid)
|
|
||||||
if not uids:
|
if not uids:
|
||||||
# Maybe is there a default value?
|
# Maybe is there a default value?
|
||||||
defValue = Type.getValue(self, obj)
|
defValue = Type.getValue(self, obj)
|
||||||
|
@ -1783,8 +1764,8 @@ class Ref(Type):
|
||||||
uids = [o.o.UID() for o in defValue]
|
uids = [o.o.UID() for o in defValue]
|
||||||
else:
|
else:
|
||||||
uids = [defValue.o.UID()]
|
uids = [defValue.o.UID()]
|
||||||
# Prepare the result: an instance of SomeObjects, that, in this case,
|
# Prepare the result: an instance of SomeObjects, that will be unwrapped
|
||||||
# represent a subset of all referred objects
|
# if not required.
|
||||||
res = SomeObjects()
|
res = SomeObjects()
|
||||||
res.totalNumber = res.batchSize = len(uids)
|
res.totalNumber = res.batchSize = len(uids)
|
||||||
batchNeeded = startNumber != None
|
batchNeeded = startNumber != None
|
||||||
|
@ -1792,10 +1773,8 @@ class Ref(Type):
|
||||||
res.batchSize = self.maxPerPage
|
res.batchSize = self.maxPerPage
|
||||||
if startNumber != None:
|
if startNumber != None:
|
||||||
res.startNumber = startNumber
|
res.startNumber = startNumber
|
||||||
# Get the needed referred objects
|
# Get the objects given their uids
|
||||||
i = res.startNumber
|
i = res.startNumber
|
||||||
# Is it possible and more efficient to perform a single query in
|
|
||||||
# portal_catalog and get the result in the order of specified uids?
|
|
||||||
while i < (res.startNumber + res.batchSize):
|
while i < (res.startNumber + res.batchSize):
|
||||||
if i >= res.totalNumber: break
|
if i >= res.totalNumber: break
|
||||||
# Retrieve every reference in the correct format according to p_type
|
# Retrieve every reference in the correct format according to p_type
|
||||||
|
@ -1852,22 +1831,23 @@ class Ref(Type):
|
||||||
* a Appy object;
|
* a Appy object;
|
||||||
* a list of Appy or Zope objects.
|
* a list of Appy or Zope objects.
|
||||||
'''
|
'''
|
||||||
# Standardize the way p_value is expressed
|
# Standardize p_value into a list of uids
|
||||||
refs = value
|
uids = value
|
||||||
if not refs: refs = []
|
if not uids: uids = []
|
||||||
if type(refs) not in sequenceTypes: refs = [refs]
|
if type(uids) not in sequenceTypes: uids = [uids]
|
||||||
for i in range(len(refs)):
|
for i in range(len(uids)):
|
||||||
if isinstance(refs[i], basestring):
|
if not isinstance(uids[i], basestring):
|
||||||
# Get the Zope object from the UID
|
# Get the UID from the Zope or Appy object
|
||||||
refs[i] = obj.portal_catalog(UID=refs[i])[0].getObject()
|
uids[i] = uids[i].o.UID()
|
||||||
else:
|
# Update the list of referred uids.
|
||||||
refs[i] = refs[i].o # Now we are sure to have a Zope object.
|
refs = getattr(obj, self.name, None)
|
||||||
# Update the field storing on p_obj the ordered list of UIDs
|
if refs == None:
|
||||||
sortedRefs = obj._appy_getSortedField(self.name)
|
refs = obj.getProductConfig().PersistentList(uids)
|
||||||
del sortedRefs[:]
|
setattr(obj, self.name, refs)
|
||||||
for ref in refs: sortedRefs.append(ref.UID())
|
else:
|
||||||
# Update the Archetypes Ref field.
|
# Empty the list and fill it with uids
|
||||||
exec 'obj.set%s%s(refs)' % (self.name[0].upper(), self.name[1:])
|
del refs[:]
|
||||||
|
for uid in uids: refs.append(uid)
|
||||||
|
|
||||||
def clone(self, forTool=True):
|
def clone(self, forTool=True):
|
||||||
'''Produces a clone of myself.'''
|
'''Produces a clone of myself.'''
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import types, copy
|
import types, copy
|
||||||
from model import ModelClass, toolFieldPrefixes
|
from model import ModelClass, toolFieldPrefixes
|
||||||
from utils import stringify
|
|
||||||
import appy.gen
|
import appy.gen
|
||||||
import appy.gen.descriptors
|
import appy.gen.descriptors
|
||||||
from appy.gen.po import PoMessage
|
from appy.gen.po import PoMessage
|
||||||
|
@ -95,14 +94,8 @@ class FieldDescriptor:
|
||||||
|
|
||||||
def walkRef(self):
|
def walkRef(self):
|
||||||
'''How to generate a Ref?'''
|
'''How to generate a Ref?'''
|
||||||
relationship = '%s_%s_rel' % (self.classDescr.name, self.fieldName)
|
|
||||||
self.fieldType = 'ReferenceField'
|
|
||||||
self.widgetType = 'ReferenceWidget'
|
|
||||||
self.fieldParams['relationship'] = relationship
|
|
||||||
if self.appyType.isMultiValued():
|
|
||||||
self.fieldParams['multiValued'] = True
|
|
||||||
# Update the list of referers
|
# Update the list of referers
|
||||||
self.generator.addReferer(self, relationship)
|
self.generator.addReferer(self)
|
||||||
# Add the widget label for the back reference
|
# Add the widget label for the back reference
|
||||||
refClassName = getClassName(self.appyType.klass, self.applicationName)
|
refClassName = getClassName(self.appyType.klass, self.applicationName)
|
||||||
backLabel = "%s_%s" % (refClassName, self.appyType.back.attribute)
|
backLabel = "%s_%s" % (refClassName, self.appyType.back.attribute)
|
||||||
|
@ -185,35 +178,12 @@ class FieldDescriptor:
|
||||||
def generate(self):
|
def generate(self):
|
||||||
'''Generates the i18n labels for this type.'''
|
'''Generates the i18n labels for this type.'''
|
||||||
self.walkAppyType()
|
self.walkAppyType()
|
||||||
if self.appyType.type != 'Ref': return
|
|
||||||
res = ''
|
|
||||||
s = stringify
|
|
||||||
spaces = TABS
|
|
||||||
# Generate field name
|
|
||||||
res += ' '*spaces + self.fieldType + '(\n'
|
|
||||||
# Generate field parameters
|
|
||||||
spaces += TABS
|
|
||||||
for fParamName, fParamValue in self.fieldParams.iteritems():
|
|
||||||
res += ' '*spaces + fParamName + '=' + s(fParamValue) + ',\n'
|
|
||||||
# Generate widget
|
|
||||||
res += ' '*spaces + 'widget=%s(\n' % self.widgetType
|
|
||||||
spaces += TABS
|
|
||||||
for wParamName, wParamValue in self.widgetParams.iteritems():
|
|
||||||
res += ' '*spaces + wParamName + '=' + s(wParamValue) + ',\n'
|
|
||||||
# End of widget definition
|
|
||||||
spaces -= TABS
|
|
||||||
res += ' '*spaces + ')\n'
|
|
||||||
# End of field definition
|
|
||||||
spaces -= TABS
|
|
||||||
res += ' '*spaces + '),\n'
|
|
||||||
return res
|
|
||||||
|
|
||||||
class ClassDescriptor(appy.gen.descriptors.ClassDescriptor):
|
class ClassDescriptor(appy.gen.descriptors.ClassDescriptor):
|
||||||
'''Represents an Archetypes-compliant class.'''
|
'''Represents an Archetypes-compliant class.'''
|
||||||
def __init__(self, klass, orderedAttributes, generator):
|
def __init__(self, klass, orderedAttributes, generator):
|
||||||
appy.gen.descriptors.ClassDescriptor.__init__(self, klass,
|
appy.gen.descriptors.ClassDescriptor.__init__(self, klass,
|
||||||
orderedAttributes, generator)
|
orderedAttributes, generator)
|
||||||
self.schema = '' # The archetypes schema will be generated here
|
|
||||||
self.methods = '' # Needed method definitions will be generated here
|
self.methods = '' # Needed method definitions will be generated here
|
||||||
# We remember here encountered pages and groups defined in the Appy
|
# We remember here encountered pages and groups defined in the Appy
|
||||||
# type. Indeed, after having parsed all application classes, we will
|
# type. Indeed, after having parsed all application classes, we will
|
||||||
|
@ -247,11 +217,10 @@ class ClassDescriptor(appy.gen.descriptors.ClassDescriptor):
|
||||||
return (parentWrapper, parentClass)
|
return (parentWrapper, parentClass)
|
||||||
|
|
||||||
def generateSchema(self, configClass=False):
|
def generateSchema(self, configClass=False):
|
||||||
'''Generates the corresponding Archetypes schema in self.schema. If we
|
'''Generates i18n and other related stuff for this class. If this class
|
||||||
are generating a schema for a class that is in the configuration
|
is in the configuration (tool, user, etc) we must avoid having
|
||||||
(tool, user, etc) we must avoid having attributes that rely on
|
attributes that rely on the configuration (ie attributes that are
|
||||||
the configuration (ie attributes that are optional, with
|
optional, with editDefault=True, etc).'''
|
||||||
editDefault=True, etc).'''
|
|
||||||
for attrName in self.orderedAttributes:
|
for attrName in self.orderedAttributes:
|
||||||
try:
|
try:
|
||||||
attrValue = getattr(self.klass, attrName)
|
attrValue = getattr(self.klass, attrName)
|
||||||
|
@ -262,11 +231,7 @@ class ClassDescriptor(appy.gen.descriptors.ClassDescriptor):
|
||||||
attrValue = copy.copy(attrValue)
|
attrValue = copy.copy(attrValue)
|
||||||
attrValue.optional = False
|
attrValue.optional = False
|
||||||
attrValue.editDefault = False
|
attrValue.editDefault = False
|
||||||
field = FieldDescriptor(attrName, attrValue, self)
|
FieldDescriptor(attrName, attrValue, self).generate()
|
||||||
fieldDef = field.generate()
|
|
||||||
if fieldDef:
|
|
||||||
# Currently, we generate Archetypes fields for Refs only.
|
|
||||||
self.schema += '\n' + fieldDef
|
|
||||||
|
|
||||||
def isAbstract(self):
|
def isAbstract(self):
|
||||||
'''Is self.klass abstract?'''
|
'''Is self.klass abstract?'''
|
||||||
|
|
|
@ -286,14 +286,13 @@ class Generator(AbstractGenerator):
|
||||||
res = [r for r in res if eval('r.%s == %s' % (p, p))]
|
res = [r for r in res if eval('r.%s == %s' % (p, p))]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def addReferer(self, fieldDescr, relationship):
|
def addReferer(self, fieldDescr):
|
||||||
'''p_fieldDescr is a Ref type definition. We will create in config.py a
|
'''p_fieldDescr is a Ref type definition.'''
|
||||||
dict that lists all back references, by type.'''
|
|
||||||
k = fieldDescr.appyType.klass
|
k = fieldDescr.appyType.klass
|
||||||
refClassName = getClassName(k, self.applicationName)
|
refClassName = getClassName(k, self.applicationName)
|
||||||
if not self.referers.has_key(refClassName):
|
if not self.referers.has_key(refClassName):
|
||||||
self.referers[refClassName] = []
|
self.referers[refClassName] = []
|
||||||
self.referers[refClassName].append( (fieldDescr, relationship))
|
self.referers[refClassName].append(fieldDescr)
|
||||||
|
|
||||||
def getAppyTypePath(self, name, appyType, klass, isBack=False):
|
def getAppyTypePath(self, name, appyType, klass, isBack=False):
|
||||||
'''Gets the path to the p_appyType when a direct reference to an
|
'''Gets the path to the p_appyType when a direct reference to an
|
||||||
|
@ -363,7 +362,7 @@ class Generator(AbstractGenerator):
|
||||||
if not titleFound: names.insert(0, 'title')
|
if not titleFound: names.insert(0, 'title')
|
||||||
# Any backward attributes to append?
|
# Any backward attributes to append?
|
||||||
if classDescr.name in self.referers:
|
if classDescr.name in self.referers:
|
||||||
for field, rel in self.referers[classDescr.name]:
|
for field in self.referers[classDescr.name]:
|
||||||
names.append(field.appyType.back.attribute)
|
names.append(field.appyType.back.attribute)
|
||||||
qNames = ['"%s"' % name for name in names]
|
qNames = ['"%s"' % name for name in names]
|
||||||
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
||||||
|
@ -536,10 +535,10 @@ class Generator(AbstractGenerator):
|
||||||
self.labels += [ Msg(klass.name, '', klassType),
|
self.labels += [ Msg(klass.name, '', klassType),
|
||||||
Msg('%s_plural' % klass.name,'', klass.name+'s')]
|
Msg('%s_plural' % klass.name,'', klass.name+'s')]
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
repls.update({'fields': klass.schema, 'methods': klass.methods,
|
repls.update({'methods': klass.methods, 'genClassName': klass.name,
|
||||||
'genClassName': klass.name, 'imports': '','baseMixin':'BaseMixin',
|
'imports': '','baseMixin':'BaseMixin', 'baseSchema': 'BaseSchema',
|
||||||
'baseSchema': 'BaseSchema', 'global_allow': 1,
|
'global_allow': 1, 'parents': 'BaseMixin, BaseContent',
|
||||||
'parents': 'BaseMixin, BaseContent', 'static': '',
|
'static': '',
|
||||||
'classDoc': 'User class for %s' % self.applicationName,
|
'classDoc': 'User class for %s' % self.applicationName,
|
||||||
'implements': "(getattr(BaseContent,'__implements__',()),)",
|
'implements': "(getattr(BaseContent,'__implements__',()),)",
|
||||||
'register': "registerType(%s, '%s')" % (klass.name,
|
'register': "registerType(%s, '%s')" % (klass.name,
|
||||||
|
@ -567,7 +566,7 @@ class Generator(AbstractGenerator):
|
||||||
|
|
||||||
# Generate the Tool class
|
# Generate the Tool class
|
||||||
repls = self.repls.copy()
|
repls = self.repls.copy()
|
||||||
repls.update({'fields': self.tool.schema, 'methods': self.tool.methods,
|
repls.update({'methods': self.tool.methods,
|
||||||
'genClassName': self.tool.name, 'imports':'', 'baseMixin':'ToolMixin',
|
'genClassName': self.tool.name, 'imports':'', 'baseMixin':'ToolMixin',
|
||||||
'baseSchema': 'OrderedBaseFolderSchema', 'global_allow': 0,
|
'baseSchema': 'OrderedBaseFolderSchema', 'global_allow': 0,
|
||||||
'parents': 'ToolMixin, UniqueObject, OrderedBaseFolder',
|
'parents': 'ToolMixin, UniqueObject, OrderedBaseFolder',
|
||||||
|
@ -584,7 +583,7 @@ class Generator(AbstractGenerator):
|
||||||
|
|
||||||
def generateClass(self, classDescr):
|
def generateClass(self, classDescr):
|
||||||
'''Is called each time an Appy class is found in the application, for
|
'''Is called each time an Appy class is found in the application, for
|
||||||
generating the corresponding Archetype class and schema.'''
|
generating the corresponding Archetype class.'''
|
||||||
k = classDescr.klass
|
k = classDescr.klass
|
||||||
print 'Generating %s.%s (gen-class)...' % (k.__module__, k.__name__)
|
print 'Generating %s.%s (gen-class)...' % (k.__module__, k.__name__)
|
||||||
if not classDescr.isAbstract():
|
if not classDescr.isAbstract():
|
||||||
|
@ -625,9 +624,9 @@ class Generator(AbstractGenerator):
|
||||||
'className': classDescr.klass.__name__, 'global_allow': 1,
|
'className': classDescr.klass.__name__, 'global_allow': 1,
|
||||||
'genClassName': classDescr.name, 'baseMixin':'BaseMixin',
|
'genClassName': classDescr.name, 'baseMixin':'BaseMixin',
|
||||||
'classDoc': classDoc, 'applicationName': self.applicationName,
|
'classDoc': classDoc, 'applicationName': self.applicationName,
|
||||||
'fields': classDescr.schema, 'methods': classDescr.methods,
|
'methods': classDescr.methods, 'implements': implements,
|
||||||
'implements': implements, 'baseSchema': baseSchema, 'static': '',
|
'baseSchema': baseSchema, 'static': '', 'register': register,
|
||||||
'register': register, 'toolInstanceName': self.toolInstanceName})
|
'toolInstanceName': self.toolInstanceName})
|
||||||
fileName = '%s.py' % classDescr.name
|
fileName = '%s.py' % classDescr.name
|
||||||
# Create i18n labels (class name and plural form)
|
# Create i18n labels (class name and plural form)
|
||||||
poMsg = PoMessage(classDescr.name, '', classDescr.klass.__name__)
|
poMsg = PoMessage(classDescr.name, '', classDescr.klass.__name__)
|
||||||
|
@ -651,7 +650,7 @@ class Generator(AbstractGenerator):
|
||||||
poMsg.produceNiceDefault()
|
poMsg.produceNiceDefault()
|
||||||
if poMsg not in self.labels:
|
if poMsg not in self.labels:
|
||||||
self.labels.append(poMsg)
|
self.labels.append(poMsg)
|
||||||
# Generate the resulting Archetypes class and schema.
|
# Generate the resulting Archetypes class.
|
||||||
self.copyFile('Class.py', repls, destName=fileName)
|
self.copyFile('Class.py', repls, destName=fileName)
|
||||||
|
|
||||||
def generateWorkflow(self, wfDescr):
|
def generateWorkflow(self, wfDescr):
|
||||||
|
|
|
@ -6,12 +6,14 @@ import os, os.path, time
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from sets import Set
|
from sets import Set
|
||||||
import appy
|
import appy
|
||||||
|
import appy.version
|
||||||
from appy.gen import Type, Ref, String
|
from appy.gen import Type, Ref, String
|
||||||
from appy.gen.po import PoParser
|
from appy.gen.po import PoParser
|
||||||
from appy.gen.utils import produceNiceMessage
|
from appy.gen.utils import produceNiceMessage, updateRolesForPermission
|
||||||
from appy.gen.plone25.utils import updateRolesForPermission
|
|
||||||
from appy.shared.data import languages
|
from appy.shared.data import languages
|
||||||
|
from migrator import Migrator
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
class ZCTextIndexInfo:
|
class ZCTextIndexInfo:
|
||||||
'''Silly class used for storing information about a ZCTextIndex.'''
|
'''Silly class used for storing information about a ZCTextIndex.'''
|
||||||
lexicon_id = "plone_lexicon"
|
lexicon_id = "plone_lexicon"
|
||||||
|
@ -246,7 +248,6 @@ class PloneInstaller:
|
||||||
self.tool.createOrUpdate(False, None)
|
self.tool.createOrUpdate(False, None)
|
||||||
else:
|
else:
|
||||||
self.tool.createOrUpdate(True, None)
|
self.tool.createOrUpdate(True, None)
|
||||||
self.updatePodTemplates()
|
|
||||||
|
|
||||||
def installTranslations(self):
|
def installTranslations(self):
|
||||||
'''Creates or updates the translation objects within the tool.'''
|
'''Creates or updates the translation objects within the tool.'''
|
||||||
|
@ -304,20 +305,6 @@ class PloneInstaller:
|
||||||
site.portal_groups.setRolesForGroup(group, [role])
|
site.portal_groups.setRolesForGroup(group, [role])
|
||||||
site.__ac_roles__ = tuple(data)
|
site.__ac_roles__ = tuple(data)
|
||||||
|
|
||||||
def installStyleSheet(self):
|
|
||||||
'''Registers In Plone the stylesheet linked to this application.'''
|
|
||||||
cssName = self.productName + '.css'
|
|
||||||
cssTitle = self.productName + ' CSS styles'
|
|
||||||
cssInfo = {'id': cssName, 'title': cssTitle}
|
|
||||||
portalCss = self.ploneSite.portal_css
|
|
||||||
try:
|
|
||||||
portalCss.unregisterResource(cssInfo['id'])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
defaults = {'id': '', 'media': 'all', 'enabled': True}
|
|
||||||
defaults.update(cssInfo)
|
|
||||||
portalCss.registerStylesheet(**defaults)
|
|
||||||
|
|
||||||
def manageIndexes(self):
|
def manageIndexes(self):
|
||||||
'''For every indexed field, this method installs and updates the
|
'''For every indexed field, this method installs and updates the
|
||||||
corresponding index if it does not exist yet.'''
|
corresponding index if it does not exist yet.'''
|
||||||
|
@ -350,54 +337,34 @@ class PloneInstaller:
|
||||||
site = self.ploneSite
|
site = self.ploneSite
|
||||||
# Do not allow an anonymous user to register himself as new user
|
# Do not allow an anonymous user to register himself as new user
|
||||||
site.manage_permission('Add portal member', ('Manager',), acquire=0)
|
site.manage_permission('Add portal member', ('Manager',), acquire=0)
|
||||||
# Call custom installer if any
|
|
||||||
if hasattr(self.appyTool, 'install'):
|
|
||||||
self.tool.executeAppyAction('install', reindex=False)
|
|
||||||
# Replace Plone front-page with an application-specific page if needed
|
# Replace Plone front-page with an application-specific page if needed
|
||||||
if self.appFrontPage:
|
if self.appFrontPage:
|
||||||
frontPageName = self.productName + 'FrontPage'
|
frontPageName = self.productName + 'FrontPage'
|
||||||
site.manage_changeProperties(default_page=frontPageName)
|
site.manage_changeProperties(default_page=frontPageName)
|
||||||
|
# Store the used Appy version (used for detecting new versions)
|
||||||
|
self.appyTool.appyVersion = appy.version.short
|
||||||
|
self.info('Appy version is %s.' % self.appyTool.appyVersion)
|
||||||
|
# Call custom installer if any
|
||||||
|
if hasattr(self.appyTool, 'install'):
|
||||||
|
self.tool.executeAppyAction('install', reindex=False)
|
||||||
|
|
||||||
def info(self, msg): return self.appyTool.log(msg)
|
def info(self, msg): return self.appyTool.log(msg)
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
|
# Begin with a migration if required.
|
||||||
|
self.installTool()
|
||||||
|
if self.reinstall: Migrator(self).run()
|
||||||
self.installRootFolder()
|
self.installRootFolder()
|
||||||
self.installTypes()
|
self.installTypes()
|
||||||
self.installTool()
|
self.manageLanguages()
|
||||||
|
self.manageIndexes()
|
||||||
|
self.updatePodTemplates()
|
||||||
self.installTranslations()
|
self.installTranslations()
|
||||||
self.installRolesAndGroups()
|
self.installRolesAndGroups()
|
||||||
self.installStyleSheet()
|
|
||||||
self.manageIndexes()
|
|
||||||
self.manageLanguages()
|
|
||||||
self.finalizeInstallation()
|
self.finalizeInstallation()
|
||||||
self.appyTool.log("Installation done.")
|
self.appyTool.log("Installation done.")
|
||||||
|
|
||||||
def uninstallTool(self):
|
def uninstall(self): return 'Done.'
|
||||||
site = self.ploneSite
|
|
||||||
# Unmention tool in the search form
|
|
||||||
portalProperties = getattr(site, 'portal_properties', None)
|
|
||||||
if portalProperties is not None:
|
|
||||||
siteProperties = getattr(portalProperties, 'site_properties', None)
|
|
||||||
if siteProperties is not None and \
|
|
||||||
siteProperties.hasProperty('types_not_searched'):
|
|
||||||
current = list(siteProperties.getProperty('types_not_searched'))
|
|
||||||
if self.toolName in current:
|
|
||||||
current.remove(self.toolName)
|
|
||||||
siteProperties.manage_changeProperties(
|
|
||||||
**{'types_not_searched' : current})
|
|
||||||
|
|
||||||
# Unmention tool in the navigation
|
|
||||||
if portalProperties is not None:
|
|
||||||
nvProps = getattr(portalProperties, 'navtree_properties', None)
|
|
||||||
if nvProps is not None and nvProps.hasProperty('idsNotToList'):
|
|
||||||
current = list(nvProps.getProperty('idsNotToList'))
|
|
||||||
if self.toolInstanceName in current:
|
|
||||||
current.remove(self.toolInstanceName)
|
|
||||||
nvProps.manage_changeProperties(**{'idsNotToList': current})
|
|
||||||
|
|
||||||
def uninstall(self):
|
|
||||||
self.uninstallTool()
|
|
||||||
return 'Done.'
|
|
||||||
|
|
||||||
# Stuff for tracking user activity ---------------------------------------------
|
# Stuff for tracking user activity ---------------------------------------------
|
||||||
loggedUsers = {}
|
loggedUsers = {}
|
||||||
|
|
63
gen/plone25/migrator.py
Normal file
63
gen/plone25/migrator.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
import time
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class Migrator:
|
||||||
|
'''This class is responsible for performing migrations, when, on
|
||||||
|
installation, we've detected a new Appy version.'''
|
||||||
|
def __init__(self, installer):
|
||||||
|
self.installer = installer
|
||||||
|
|
||||||
|
def migrateTo_0_7_1(self):
|
||||||
|
'''Appy 0.7.1 has its own management of Ref fields and does not use
|
||||||
|
Archetypes references and the reference catalog anymore. So we must
|
||||||
|
update data structures that store Ref info on instances.'''
|
||||||
|
ins = self.installer
|
||||||
|
ins.info('Migrating to Appy 0.7.1...')
|
||||||
|
allClassNames = [ins.tool.__class__.__name__] + ins.config.allClassNames
|
||||||
|
for className in allClassNames:
|
||||||
|
i = -1
|
||||||
|
updated = 0
|
||||||
|
ins.info('Analysing class "%s"...' % className)
|
||||||
|
refFields = None
|
||||||
|
for obj in ins.tool.executeQuery(className,\
|
||||||
|
noSecurity=True)['objects']:
|
||||||
|
i += 1
|
||||||
|
if i == 0:
|
||||||
|
# Get the Ref fields for objects of this class
|
||||||
|
refFields = [f for f in obj.getAllAppyTypes() \
|
||||||
|
if (f.type == 'Ref') and not f.isBack]
|
||||||
|
if refFields:
|
||||||
|
refNames = ', '.join([rf.name for rf in refFields])
|
||||||
|
ins.info(' Ref fields found: %s' % refNames)
|
||||||
|
else:
|
||||||
|
ins.info(' No Ref field found.')
|
||||||
|
break
|
||||||
|
isUpdated = False
|
||||||
|
for field in refFields:
|
||||||
|
# Attr for storing UIDs of referred objects has moved
|
||||||
|
# from _appy_[fieldName] to [fieldName].
|
||||||
|
refs = getattr(obj, '_appy_%s' % field.name)
|
||||||
|
if refs:
|
||||||
|
isUpdated = True
|
||||||
|
setattr(obj, field.name, refs)
|
||||||
|
exec 'del obj._appy_%s' % field.name
|
||||||
|
# Set the back references
|
||||||
|
for refObject in field.getValue(obj):
|
||||||
|
refObject.link(field.back.name, obj, back=True)
|
||||||
|
if isUpdated: updated += 1
|
||||||
|
if updated:
|
||||||
|
ins.info(' %d/%d object(s) updated.' % (updated, i+1))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
i = self.installer
|
||||||
|
installedVersion = i.appyTool.appyVersion
|
||||||
|
startTime = time.time()
|
||||||
|
migrationRequired = False
|
||||||
|
if not installedVersion or (installedVersion <= '0.7.0'):
|
||||||
|
migrationRequired = True
|
||||||
|
self.migrateTo_0_7_1()
|
||||||
|
stopTime = time.time()
|
||||||
|
if migrationRequired:
|
||||||
|
i.info('Migration done in %d minute(s).'% ((stopTime-startTime)/60))
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -270,7 +270,7 @@ class ToolMixin(BaseMixin):
|
||||||
# corresponding filter widget on the screen.
|
# corresponding filter widget on the screen.
|
||||||
if refObject:
|
if refObject:
|
||||||
refField = refObject.getAppyType(refField)
|
refField = refObject.getAppyType(refField)
|
||||||
params['UID'] = refObject._appy_getSortedField(refField.name).data
|
params['UID'] = getattr(refObject, refField.name).data
|
||||||
# Determine what method to call on the portal catalog
|
# Determine what method to call on the portal catalog
|
||||||
if noSecurity: catalogMethod = 'unrestrictedSearchResults'
|
if noSecurity: catalogMethod = 'unrestrictedSearchResults'
|
||||||
else: catalogMethod = 'searchResults'
|
else: catalogMethod = 'searchResults'
|
||||||
|
@ -694,7 +694,7 @@ class ToolMixin(BaseMixin):
|
||||||
# In the case of a reference, we retrieve ALL surrounding objects.
|
# In the case of a reference, we retrieve ALL surrounding objects.
|
||||||
masterObj = self.getObject(d1)
|
masterObj = self.getObject(d1)
|
||||||
batchSize = masterObj.getAppyType(fieldName).maxPerPage
|
batchSize = masterObj.getAppyType(fieldName).maxPerPage
|
||||||
uids = getattr(masterObj, '_appy_%s' % fieldName)
|
uids = getattr(masterObj, fieldName)
|
||||||
# Display the reference widget at the page where the current object
|
# Display the reference widget at the page where the current object
|
||||||
# lies.
|
# lies.
|
||||||
startNumberKey = '%s%s_startNumber' % (masterObj.UID(), fieldName)
|
startNumberKey = '%s%s_startNumber' % (masterObj.UID(), fieldName)
|
||||||
|
|
|
@ -11,7 +11,6 @@ from appy.gen.utils import *
|
||||||
from appy.gen.layout import Table, defaultPageLayouts
|
from appy.gen.layout import Table, defaultPageLayouts
|
||||||
from appy.gen.descriptors import WorkflowDescriptor
|
from appy.gen.descriptors import WorkflowDescriptor
|
||||||
from appy.gen.plone25.descriptors import ClassDescriptor
|
from appy.gen.plone25.descriptors import ClassDescriptor
|
||||||
from appy.gen.plone25.utils import updateRolesForPermission,checkTransitionGuard
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class BaseMixin:
|
class BaseMixin:
|
||||||
|
@ -81,6 +80,11 @@ class BaseMixin:
|
||||||
# Call a custom "onDelete" if it exists
|
# Call a custom "onDelete" if it exists
|
||||||
appyObj = self.appy()
|
appyObj = self.appy()
|
||||||
if hasattr(appyObj, 'onDelete'): appyObj.onDelete()
|
if hasattr(appyObj, 'onDelete'): appyObj.onDelete()
|
||||||
|
# Any people referencing me must forget me now
|
||||||
|
for field in self.getAllAppyTypes():
|
||||||
|
if field.type != 'Ref': continue
|
||||||
|
for obj in field.getValue(self):
|
||||||
|
obj.unlink(field.back.name, self, back=True)
|
||||||
# Delete the object
|
# Delete the object
|
||||||
self.getParentNode().manage_delObjects([self.id])
|
self.getParentNode().manage_delObjects([self.id])
|
||||||
|
|
||||||
|
@ -430,7 +434,6 @@ class BaseMixin:
|
||||||
appyType = self.getAppyType(name)
|
appyType = self.getAppyType(name)
|
||||||
if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]):
|
if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]):
|
||||||
return appyType.getValue(self)
|
return appyType.getValue(self)
|
||||||
return None
|
|
||||||
|
|
||||||
def getFormattedFieldValue(self, name, value):
|
def getFormattedFieldValue(self, name, value):
|
||||||
'''Gets a nice, string representation of p_value which is a value from
|
'''Gets a nice, string representation of p_value which is a value from
|
||||||
|
@ -442,9 +445,9 @@ class BaseMixin:
|
||||||
If p_startNumber is None, this method returns all referred objects.
|
If p_startNumber is None, this method returns all referred objects.
|
||||||
If p_startNumber is a number, this method will return
|
If p_startNumber is a number, this method will return
|
||||||
appyType.maxPerPage objects, starting at p_startNumber.'''
|
appyType.maxPerPage objects, starting at p_startNumber.'''
|
||||||
appyType = self.getAppyType(name)
|
field = self.getAppyType(name)
|
||||||
return appyType.getValue(self, type='zobjects', someObjects=True,
|
return field.getValue(self, type='zobjects', someObjects=True,
|
||||||
startNumber=startNumber).__dict__
|
startNumber=startNumber).__dict__
|
||||||
|
|
||||||
def getSelectableAppyRefs(self, name):
|
def getSelectableAppyRefs(self, name):
|
||||||
'''p_name is the name of a Ref field. This method returns the list of
|
'''p_name is the name of a Ref field. This method returns the list of
|
||||||
|
@ -494,9 +497,9 @@ class BaseMixin:
|
||||||
|
|
||||||
def getAppyRefIndex(self, fieldName, obj):
|
def getAppyRefIndex(self, fieldName, obj):
|
||||||
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
'''Gets the position of p_obj within Ref field named p_fieldName.'''
|
||||||
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
refs = getattr(self, fieldName, None)
|
||||||
res = sortedObjectsUids.index(obj.UID())
|
if not refs: raise IndexError()
|
||||||
return res
|
return refs.index(obj.UID())
|
||||||
|
|
||||||
def isDebug(self):
|
def isDebug(self):
|
||||||
'''Are we in debug mode ?'''
|
'''Are we in debug mode ?'''
|
||||||
|
@ -782,14 +785,14 @@ class BaseMixin:
|
||||||
'''This method changes the position of object with uid p_objectUid in
|
'''This method changes the position of object with uid p_objectUid in
|
||||||
reference field p_fieldName to p_newIndex i p_isDelta is False, or
|
reference field p_fieldName to p_newIndex i p_isDelta is False, or
|
||||||
to actualIndex+p_newIndex if p_isDelta is True.'''
|
to actualIndex+p_newIndex if p_isDelta is True.'''
|
||||||
sortedObjectsUids = self._appy_getSortedField(fieldName)
|
refs = getattr(self, fieldName, None)
|
||||||
oldIndex = sortedObjectsUids.index(objectUid)
|
oldIndex = refs.index(objectUid)
|
||||||
sortedObjectsUids.remove(objectUid)
|
refs.remove(objectUid)
|
||||||
if isDelta:
|
if isDelta:
|
||||||
newIndex = oldIndex + newIndex
|
newIndex = oldIndex + newIndex
|
||||||
else:
|
else:
|
||||||
pass # To implement later on
|
pass # To implement later on
|
||||||
sortedObjectsUids.insert(newIndex, objectUid)
|
refs.insert(newIndex, objectUid)
|
||||||
|
|
||||||
def onChangeRefOrder(self):
|
def onChangeRefOrder(self):
|
||||||
'''This method is called when the user wants to change order of an
|
'''This method is called when the user wants to change order of an
|
||||||
|
@ -1074,7 +1077,7 @@ class BaseMixin:
|
||||||
for appyType in self.getAllAppyTypes():
|
for appyType in self.getAllAppyTypes():
|
||||||
if appyType.type != 'Ref': continue
|
if appyType.type != 'Ref': continue
|
||||||
if appyType.isBack or appyType.link: continue
|
if appyType.isBack or appyType.link: continue
|
||||||
# Indeed, no possibility to create objects with such Ref
|
# Indeed, no possibility to create objects with such Refs
|
||||||
refType = self.getTool().getPortalType(appyType.klass)
|
refType = self.getTool().getPortalType(appyType.klass)
|
||||||
if refType not in addPermissions: continue
|
if refType not in addPermissions: continue
|
||||||
# Get roles that may add this content type
|
# Get roles that may add this content type
|
||||||
|
@ -1111,16 +1114,6 @@ class BaseMixin:
|
||||||
res = self.portal_type
|
res = self.portal_type
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _appy_getSortedField(self, fieldName):
|
|
||||||
'''Gets, for reference field p_fieldName, the Appy persistent list
|
|
||||||
that contains the sorted list of referred object UIDs. If this list
|
|
||||||
does not exist, it is created.'''
|
|
||||||
sortedFieldName = '_appy_%s' % fieldName
|
|
||||||
if not hasattr(self.aq_base, sortedFieldName):
|
|
||||||
pList = self.getProductConfig().PersistentList
|
|
||||||
exec 'self.%s = pList()' % sortedFieldName
|
|
||||||
return getattr(self, sortedFieldName)
|
|
||||||
|
|
||||||
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 a Appy URL.
|
'''Returns a Appy URL.
|
||||||
|
|
|
@ -168,7 +168,7 @@ toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns',
|
||||||
defaultToolFields = ('users', 'translations', 'enableNotifications',
|
defaultToolFields = ('users', 'translations', 'enableNotifications',
|
||||||
'unoEnabledPython', 'openOfficePort',
|
'unoEnabledPython', 'openOfficePort',
|
||||||
'numberOfResultsPerPage', 'listBoxesMaximumWidth',
|
'numberOfResultsPerPage', 'listBoxesMaximumWidth',
|
||||||
'refreshSecurity')
|
'appyVersion', 'refreshSecurity')
|
||||||
|
|
||||||
class Tool(ModelClass):
|
class Tool(ModelClass):
|
||||||
# In a ModelClass we need to declare attributes in the following list.
|
# In a ModelClass we need to declare attributes in the following list.
|
||||||
|
@ -181,6 +181,7 @@ class Tool(ModelClass):
|
||||||
openOfficePort = Integer(default=2002, group="connectionToOpenOffice")
|
openOfficePort = Integer(default=2002, group="connectionToOpenOffice")
|
||||||
numberOfResultsPerPage = Integer(default=30)
|
numberOfResultsPerPage = Integer(default=30)
|
||||||
listBoxesMaximumWidth = Integer(default=100)
|
listBoxesMaximumWidth = Integer(default=100)
|
||||||
|
appyVersion = String(show=False, layouts='f')
|
||||||
def refreshSecurity(self): pass # Real method in the wrapper
|
def refreshSecurity(self): pass # Real method in the wrapper
|
||||||
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).
|
||||||
|
|
|
@ -8,11 +8,6 @@ from appy.gen.plone25.mixins import BaseMixin
|
||||||
from appy.gen.plone25.mixins.ToolMixin import ToolMixin
|
from appy.gen.plone25.mixins.ToolMixin import ToolMixin
|
||||||
from Extensions.appyWrappers import <!genClassName!>_Wrapper
|
from Extensions.appyWrappers import <!genClassName!>_Wrapper
|
||||||
<!imports!>
|
<!imports!>
|
||||||
|
|
||||||
schema = Schema((<!fields!>
|
|
||||||
),)
|
|
||||||
fullSchema = <!baseSchema!>.copy() + schema.copy()
|
|
||||||
|
|
||||||
class <!genClassName!>(<!parents!>):
|
class <!genClassName!>(<!parents!>):
|
||||||
'''<!classDoc!>'''
|
'''<!classDoc!>'''
|
||||||
security = ClassSecurityInfo()
|
security = ClassSecurityInfo()
|
||||||
|
@ -30,7 +25,7 @@ class <!genClassName!>(<!parents!>):
|
||||||
typeDescMsgId = '<!genClassName!>'
|
typeDescMsgId = '<!genClassName!>'
|
||||||
i18nDomain = '<!applicationName!>'
|
i18nDomain = '<!applicationName!>'
|
||||||
wrapperClass = <!genClassName!>_Wrapper
|
wrapperClass = <!genClassName!>_Wrapper
|
||||||
schema = fullSchema
|
schema = <!baseSchema!>.copy()
|
||||||
for elem in dir(<!baseMixin!>):
|
for elem in dir(<!baseMixin!>):
|
||||||
if not elem.startswith('__'): security.declarePublic(elem)
|
if not elem.startswith('__'): security.declarePublic(elem)
|
||||||
<!static!>
|
<!static!>
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
def stringify(value):
|
|
||||||
'''Transforms p_value such that it can be dumped as a string into a
|
|
||||||
generated file.'''
|
|
||||||
if isinstance(value, tuple) or isinstance(value, list):
|
|
||||||
res = '('
|
|
||||||
for v in value:
|
|
||||||
res += '%s,' % stringify(v)
|
|
||||||
res += ')'
|
|
||||||
elif value.__class__.__name__ == 'DateTime':
|
|
||||||
res = 'DateTime("%s")' % value.strftime('%Y/%m/%d %H:%M')
|
|
||||||
else:
|
|
||||||
res = str(value)
|
|
||||||
if isinstance(value, basestring):
|
|
||||||
if value.startswith('python:'):
|
|
||||||
res = value[7:]
|
|
||||||
else:
|
|
||||||
res = "'%s'" % value.replace("'", "\\'")
|
|
||||||
res = res.replace('\n', '\\n')
|
|
||||||
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 checkTransitionGuard(guard, sm, wf_def, ob):
|
|
||||||
'''This method is similar to DCWorkflow.Guard.check, but allows to retrieve
|
|
||||||
the truth value as a appy.gen.No instance, not simply "1" or "0".'''
|
|
||||||
from Products.DCWorkflow.Expression import StateChangeInfo,createExprContext
|
|
||||||
u_roles = None
|
|
||||||
if wf_def.manager_bypass:
|
|
||||||
# Possibly bypass.
|
|
||||||
u_roles = sm.getUser().getRolesInContext(ob)
|
|
||||||
if 'Manager' in u_roles:
|
|
||||||
return 1
|
|
||||||
if guard.permissions:
|
|
||||||
for p in guard.permissions:
|
|
||||||
if _checkPermission(p, ob):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
if guard.roles:
|
|
||||||
# Require at least one of the given roles.
|
|
||||||
if u_roles is None:
|
|
||||||
u_roles = sm.getUser().getRolesInContext(ob)
|
|
||||||
for role in guard.roles:
|
|
||||||
if role in u_roles:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
if guard.groups:
|
|
||||||
# Require at least one of the specified groups.
|
|
||||||
u = sm.getUser()
|
|
||||||
b = aq_base( u )
|
|
||||||
if hasattr( b, 'getGroupsInContext' ):
|
|
||||||
u_groups = u.getGroupsInContext( ob )
|
|
||||||
elif hasattr( b, 'getGroups' ):
|
|
||||||
u_groups = u.getGroups()
|
|
||||||
else:
|
|
||||||
u_groups = ()
|
|
||||||
for group in guard.groups:
|
|
||||||
if group in u_groups:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
expr = guard.expr
|
|
||||||
if expr is not None:
|
|
||||||
econtext = createExprContext(StateChangeInfo(ob, wf_def))
|
|
||||||
res = expr(econtext)
|
|
||||||
return res
|
|
||||||
return 1
|
|
||||||
# ------------------------------------------------------------------------------
|
|
|
@ -86,65 +86,67 @@ class AbstractWrapper(object):
|
||||||
|
|
||||||
def getField(self, name): return self.o.getAppyType(name)
|
def getField(self, name): return self.o.getAppyType(name)
|
||||||
|
|
||||||
def link(self, fieldName, obj):
|
def link(self, fieldName, obj, back=False):
|
||||||
'''This method links p_obj (which can be a list of objects) to this one
|
'''This method links p_obj (which can be a list of objects) to this one
|
||||||
through reference field p_fieldName.'''
|
through reference field p_fieldName. As this method is recursive and
|
||||||
postfix = 'et%s%s' % (fieldName[0].upper(), fieldName[1:])
|
can be called to update the corresponding back reference, param
|
||||||
# Update the Archetypes reference field
|
p_back is there, but, you, as Appy developer, should never set it
|
||||||
exec 'objs = self.o.g%s()' % postfix
|
to True.'''
|
||||||
if not objs:
|
# p_objs can be a list of objects
|
||||||
objs = []
|
|
||||||
elif type(objs) not in sequenceTypes:
|
|
||||||
objs = [objs]
|
|
||||||
# Add p_obj to the existing objects
|
|
||||||
if type(obj) in sequenceTypes:
|
if type(obj) in sequenceTypes:
|
||||||
for o in obj: objs.append(o.o)
|
for o in obj: self.link(fieldName, o, back=back)
|
||||||
else:
|
return
|
||||||
objs.append(obj.o)
|
# Gets the list of referred objects (=list of uids), or create it.
|
||||||
exec 'self.o.s%s(objs)' % postfix
|
selfO = self.o
|
||||||
# Update the ordered list of references
|
refs = getattr(selfO, fieldName, None)
|
||||||
sorted = self.o._appy_getSortedField(fieldName)
|
if refs == None:
|
||||||
if type(obj) in sequenceTypes:
|
refs = selfO.getProductConfig().PersistentList()
|
||||||
for o in obj: sorted.append(o.o.UID())
|
setattr(selfO, fieldName, refs)
|
||||||
else:
|
# Insert p_obj into it.
|
||||||
sorted.append(obj.o.UID())
|
uid = obj.o.UID()
|
||||||
|
if uid not in refs:
|
||||||
|
refs.append(uid)
|
||||||
|
# Update the back reference
|
||||||
|
if not back:
|
||||||
|
backName = selfO.getAppyType(fieldName).back.attribute
|
||||||
|
obj.appy().link(backName, self, back=True)
|
||||||
|
|
||||||
def unlink(self, fieldName, obj):
|
def unlink(self, fieldName, obj, back=False):
|
||||||
'''This method unlinks p_obj (which can be a list of objects) from this
|
'''This method unlinks p_obj (which can be a list of objects) from this
|
||||||
one through reference field p_fieldName.'''
|
one through reference field p_fieldName. As this method is recursive
|
||||||
postfix = 'et%s%s' % (fieldName[0].upper(), fieldName[1:])
|
and can be called to update the corresponding back reference, param
|
||||||
# Update the Archetypes reference field.
|
p_back is there, but, you, as Appy developer, should never set it
|
||||||
exec 'objs = self.o.g%s()' % postfix
|
to True.'''
|
||||||
if not objs: return
|
# p_objs can be a list of objects
|
||||||
if type(objs) not in sequenceTypes: objs = [objs]
|
|
||||||
# Remove p_obj from existing objects
|
|
||||||
if type(obj) in sequenceTypes:
|
if type(obj) in sequenceTypes:
|
||||||
for o in obj:
|
for o in obj: self.unlink(fieldName, o, back=back)
|
||||||
if o.o in objs: objs.remove(o.o)
|
return
|
||||||
else:
|
# Get the list of referred objects
|
||||||
if obj.o in objs: objs.remove(obj.o)
|
selfO = self.o
|
||||||
exec 'self.o.s%s(objs)' % postfix
|
refs = getattr(selfO, fieldName, None)
|
||||||
# Update the ordered list of references
|
if not refs: return
|
||||||
sorted = self.o._appy_getSortedField(fieldName)
|
# Unlink the object
|
||||||
if type(obj) in sequenceTypes:
|
uid = obj.o.UID()
|
||||||
for o in obj:
|
if uid in refs:
|
||||||
if o.o.UID() in sorted:
|
refs.remove(uid)
|
||||||
sorted.remove(o.o.UID())
|
# Update the back reference
|
||||||
else:
|
if not back:
|
||||||
if obj.o.UID() in sorted:
|
backName = selfO.getAppyType(fieldName).back.attribute
|
||||||
sorted.remove(obj.o.UID())
|
obj.appy().unlink(backName, self, back=True)
|
||||||
|
|
||||||
def sort(self, fieldName, sortKey='title', reverse=False):
|
def sort(self, fieldName, sortKey='title', reverse=False):
|
||||||
'''Sorts referred elements linked to p_self via p_fieldName according
|
'''Sorts referred elements linked to p_self via p_fieldName according
|
||||||
to a given p_sortKey which must be an attribute set on referred
|
to a given p_sortKey which must be an attribute set on referred
|
||||||
objects ("title", by default).'''
|
objects ("title", by default).'''
|
||||||
sortedUids = getattr(self.o, '_appy_%s' % fieldName)
|
selfO = self.o
|
||||||
c = self.o.portal_catalog
|
refs = getattr(selfO, fieldName, None)
|
||||||
sortedUids.sort(lambda x,y: \
|
if not refs: return
|
||||||
|
c = selfO.portal_catalog
|
||||||
|
refs.sort(lambda x,y: \
|
||||||
cmp(getattr(c(UID=x)[0].getObject().appy(), sortKey),
|
cmp(getattr(c(UID=x)[0].getObject().appy(), sortKey),
|
||||||
getattr(c(UID=y)[0].getObject().appy(), sortKey)))
|
getattr(c(UID=y)[0].getObject().appy(), sortKey)))
|
||||||
if reverse:
|
if reverse:
|
||||||
sortedUids.reverse()
|
refs.reverse()
|
||||||
|
|
||||||
def create(self, fieldNameOrClass, **kwargs):
|
def create(self, fieldNameOrClass, **kwargs):
|
||||||
'''If p_fieldNameOfClass is the name of a field, this method allows to
|
'''If p_fieldNameOfClass is the name of a field, this method allows to
|
||||||
|
|
15
gen/utils.py
15
gen/utils.py
|
@ -314,4 +314,19 @@ def getClassName(klass, appName=None):
|
||||||
else: # This is a standard class
|
else: # This is a standard class
|
||||||
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)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue