Eradicated Flavour and PodTemplate classes (for the latter, use Pod fields instead); Added a code analyser; Groups can now be slaves in master/slaves relationships; Refs have more params (show a confirmation popup before adding an object, add an object without creation form); Code for Refs has been refactored to comply with the new way to organize Types; Added a WebDAV client library.
This commit is contained in:
parent
9f4db88bdf
commit
990e16c6e7
47 changed files with 1006 additions and 1297 deletions
|
@ -1,86 +0,0 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class FlavourWrapper(AbstractWrapper):
|
||||
def onEdit(self, created):
|
||||
if created:
|
||||
nbOfFlavours = len(self.tool.flavours)
|
||||
if nbOfFlavours != 1:
|
||||
self.number = nbOfFlavours
|
||||
self.o.registerPortalTypes()
|
||||
# Call the custom flavour "onEdit" method if it exists
|
||||
if len(self.__class__.__bases__) > 1:
|
||||
# There is a custom flavour
|
||||
if customFlavour.__dict__.has_key('onEdit'):
|
||||
customFlavour.__dict__['onEdit'](self, created)
|
||||
|
||||
def getAttributeName(self, attributeType, klass, attrName=None):
|
||||
'''Some names of Flavour attributes are not easy to guess. For example,
|
||||
the attribute that stores, for a given flavour, the POD templates
|
||||
for class A that is in package x.y is "flavour.podTemplatesForx_y_A".
|
||||
Other example: the attribute that stores the editable default value
|
||||
of field "f1" of class x.y.A is "flavour.defaultValueForx_y_A_f1".
|
||||
This method generates the attribute name based on p_attributeType,
|
||||
a p_klass from the application, and a p_attrName (given only if
|
||||
needed, for example if p_attributeType is "defaultValue").
|
||||
p_attributeType may be:
|
||||
|
||||
"defaultValue"
|
||||
Stores the editable default value for a given p_attrName of a
|
||||
given p_klass.
|
||||
|
||||
"podTemplates"
|
||||
Stores the POD templates that are defined for a given p_klass.
|
||||
|
||||
"podMaxShownTemplates"
|
||||
Stores the maximum number of POD templates shown at once. If the
|
||||
number of available templates is higher, templates are shown in a
|
||||
drop-down list.
|
||||
|
||||
"podTemplate"
|
||||
Stores the pod template for p_attrName.
|
||||
|
||||
"formats"
|
||||
Stores the output format(s) of a given pod template for
|
||||
p_attrName.
|
||||
|
||||
"resultColumns"
|
||||
Stores the list of columns that must be shown when displaying
|
||||
instances of the a given root p_klass.
|
||||
|
||||
"enableAdvancedSearch"
|
||||
Determines if the advanced search screen must be enabled for
|
||||
p_klass.
|
||||
|
||||
"numberOfSearchColumns"
|
||||
Determines in how many columns the search screen for p_klass
|
||||
is rendered.
|
||||
|
||||
"searchFields"
|
||||
Determines, among all indexed fields for p_klass, which one will
|
||||
really be used in the search screen.
|
||||
|
||||
"optionalFields"
|
||||
Stores the list of optional attributes that are in use in the
|
||||
current flavour for the given p_klass.
|
||||
|
||||
"showWorkflow"
|
||||
Stores the boolean field indicating if we must show workflow-
|
||||
related information for p_klass or not.
|
||||
|
||||
"showWorkflowCommentField"
|
||||
Stores the boolean field indicating if we must show the field
|
||||
allowing to enter a comment every time a transition is triggered.
|
||||
|
||||
"showAllStatesInPhase"
|
||||
Stores the boolean field indicating if we must show all states
|
||||
linked to the current phase or not. If this field is False, we
|
||||
simply show the current state, be it linked to the current phase
|
||||
or not.
|
||||
'''
|
||||
fullClassName = self.o.getPortalType(klass)
|
||||
res = '%sFor%s' % (attributeType, fullClassName)
|
||||
if attrName: res += '_%s' % attrName
|
||||
return res
|
||||
# ------------------------------------------------------------------------------
|
|
@ -1,6 +0,0 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class PodTemplateWrapper(AbstractWrapper): pass
|
||||
# ------------------------------------------------------------------------------
|
|
@ -44,4 +44,65 @@ class ToolWrapper(AbstractWrapper):
|
|||
def getDiskFolder(self):
|
||||
'''Returns the disk folder where the Appy application is stored.'''
|
||||
return self.o.getProductConfig().diskFolder
|
||||
|
||||
def getAttributeName(self, attributeType, klass, attrName=None):
|
||||
'''Some names of Tool attributes are not easy to guess. For example,
|
||||
the attribute that stores the names of the columns to display in
|
||||
query results for class A that is in package x.y is
|
||||
"tool.resultColumnsForx_y_A". Other example: the attribute that
|
||||
stores the editable default value of field "f1" of class x.y.A is
|
||||
"tool.defaultValueForx_y_A_f1". This method generates the attribute
|
||||
name based on p_attributeType, a p_klass from the application, and a
|
||||
p_attrName (given only if needed, for example if p_attributeType is
|
||||
"defaultValue"). p_attributeType may be:
|
||||
|
||||
"defaultValue"
|
||||
Stores the editable default value for a given p_attrName of a
|
||||
given p_klass.
|
||||
|
||||
"podTemplate"
|
||||
Stores the pod template for p_attrName.
|
||||
|
||||
"formats"
|
||||
Stores the output format(s) of a given pod template for
|
||||
p_attrName.
|
||||
|
||||
"resultColumns"
|
||||
Stores the list of columns that must be shown when displaying
|
||||
instances of the a given root p_klass.
|
||||
|
||||
"enableAdvancedSearch"
|
||||
Determines if the advanced search screen must be enabled for
|
||||
p_klass.
|
||||
|
||||
"numberOfSearchColumns"
|
||||
Determines in how many columns the search screen for p_klass
|
||||
is rendered.
|
||||
|
||||
"searchFields"
|
||||
Determines, among all indexed fields for p_klass, which one will
|
||||
really be used in the search screen.
|
||||
|
||||
"optionalFields"
|
||||
Stores the list of optional attributes that are in use in the
|
||||
tool for the given p_klass.
|
||||
|
||||
"showWorkflow"
|
||||
Stores the boolean field indicating if we must show workflow-
|
||||
related information for p_klass or not.
|
||||
|
||||
"showWorkflowCommentField"
|
||||
Stores the boolean field indicating if we must show the field
|
||||
allowing to enter a comment every time a transition is triggered.
|
||||
|
||||
"showAllStatesInPhase"
|
||||
Stores the boolean field indicating if we must show all states
|
||||
linked to the current phase or not. If this field is False, we
|
||||
simply show the current state, be it linked to the current phase
|
||||
or not.
|
||||
'''
|
||||
fullClassName = self.o.getPortalType(klass)
|
||||
res = '%sFor%s' % (attributeType, fullClassName)
|
||||
if attrName: res += '_%s' % attrName
|
||||
return res
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -3,6 +3,18 @@ from appy.gen.plone25.wrappers import AbstractWrapper
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
class UserWrapper(AbstractWrapper):
|
||||
|
||||
def _callCustom(self, methodName, *args, **kwargs):
|
||||
'''This wrapper implements some methods like "validate" and "onEdit".
|
||||
If the user has defined its own wrapper, its methods will not be
|
||||
called. So this method allows, from the methods here, to call the
|
||||
user versions.'''
|
||||
if len(self.__class__.__bases__) > 1:
|
||||
# There is a custom user class
|
||||
customUser = self.__class__.__bases__[-1]
|
||||
if customUser.__dict__.has_key(methodName):
|
||||
customUser.__dict__[methodName](self, *args, **kwargs)
|
||||
|
||||
def showLogin(self):
|
||||
'''When must we show the login field?'''
|
||||
if self.o.isTemporary(): return 'edit'
|
||||
|
@ -51,6 +63,7 @@ class UserWrapper(AbstractWrapper):
|
|||
msg = self.translate(u'Passwords do not match.', domain='plone')
|
||||
errors.password1 = msg
|
||||
errors.password2 = msg
|
||||
self._callCustom('validate', new, errors)
|
||||
|
||||
def onEdit(self, created):
|
||||
self.title = self.firstName + ' ' + self.name
|
||||
|
@ -86,11 +99,5 @@ class UserWrapper(AbstractWrapper):
|
|||
# Remove the user if it was in the corresponding group
|
||||
if groupName in userGroups:
|
||||
group.removeMember(self.login)
|
||||
# Call the custom user "onEdit" method if it exists
|
||||
# XXX This code does not work.
|
||||
if len(self.__class__.__bases__) > 1:
|
||||
customUser = self.__class__.__bases__[-1]
|
||||
# There is a custom user class
|
||||
if customUser.__dict__.has_key('onEdit'):
|
||||
customUser.__dict__['onEdit'](self, created)
|
||||
self._callCustom('onEdit', created)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -82,8 +82,6 @@ class AbstractWrapper:
|
|||
else: return 1
|
||||
def get_tool(self): return self.o.getTool().appy()
|
||||
tool = property(get_tool)
|
||||
def get_flavour(self): return self.o.getTool().getFlavour(self.o, appy=True)
|
||||
flavour = property(get_flavour)
|
||||
def get_request(self): return self.o.REQUEST
|
||||
request = property(get_request)
|
||||
def get_session(self): return self.o.REQUEST.SESSION
|
||||
|
@ -204,9 +202,9 @@ class AbstractWrapper:
|
|||
ploneObj.reindexObject()
|
||||
return appyObj
|
||||
|
||||
def translate(self, label, mapping={}, domain=None):
|
||||
def translate(self, label, mapping={}, domain=None, language=None):
|
||||
'''Check documentation of self.o.translate.'''
|
||||
return self.o.translate(label, mapping, domain)
|
||||
return self.o.translate(label, mapping, domain, language=language)
|
||||
|
||||
def do(self, transition, comment='', doAction=False, doNotify=False,
|
||||
doHistory=True):
|
||||
|
@ -276,27 +274,25 @@ class AbstractWrapper:
|
|||
p_maxResults. If p_noSecurity is specified, you get all objects,
|
||||
even if the logged user does not have the permission to view it.'''
|
||||
# Find the content type corresponding to p_klass
|
||||
flavour = self.flavour
|
||||
contentType = flavour.o.getPortalType(klass)
|
||||
contentType = self.tool.o.getPortalType(klass)
|
||||
# Create the Search object
|
||||
search = Search('customSearch', sortBy=sortBy, **fields)
|
||||
if not maxResults:
|
||||
maxResults = 'NO_LIMIT'
|
||||
# If I let maxResults=None, only a subset of the results will be
|
||||
# returned by method executeResult.
|
||||
res = self.tool.o.executeQuery(contentType,flavour.number,search=search,
|
||||
maxResults=maxResults, noSecurity=noSecurity)
|
||||
res = self.tool.o.executeQuery(contentType, search=search,
|
||||
maxResults=maxResults, noSecurity=noSecurity)
|
||||
return [o.appy() for o in res['objects']]
|
||||
|
||||
def count(self, klass, noSecurity=False, **fields):
|
||||
'''Identical to m_search above, but returns the number of objects that
|
||||
match the search instead of returning the objects themselves. Use
|
||||
this method instead of writing len(self.search(...)).'''
|
||||
flavour = self.flavour
|
||||
contentType = flavour.o.getPortalType(klass)
|
||||
contentType = self.tool.o.getPortalType(klass)
|
||||
search = Search('customSearch', **fields)
|
||||
res = self.tool.o.executeQuery(contentType,flavour.number,search=search,
|
||||
brainsOnly=True, noSecurity=noSecurity)
|
||||
res = self.tool.o.executeQuery(contentType, search=search,
|
||||
brainsOnly=True, noSecurity=noSecurity)
|
||||
if res: return res._len # It is a LazyMap instance
|
||||
else: return 0
|
||||
|
||||
|
@ -325,14 +321,12 @@ class AbstractWrapper:
|
|||
|
||||
"for obj in self.search(MyClass,...)"
|
||||
'''
|
||||
flavour = self.flavour
|
||||
contentType = flavour.o.getPortalType(klass)
|
||||
contentType = self.tool.o.getPortalType(klass)
|
||||
search = Search('customSearch', sortBy=sortBy, **fields)
|
||||
# Initialize the context variable "ctx"
|
||||
ctx = context
|
||||
for brain in self.tool.o.executeQuery(contentType, flavour.number, \
|
||||
search=search, brainsOnly=True, maxResults=maxResults,
|
||||
noSecurity=noSecurity):
|
||||
for brain in self.tool.o.executeQuery(contentType, search=search, \
|
||||
brainsOnly=True, maxResults=maxResults, noSecurity=noSecurity):
|
||||
# Get the Appy object from the brain
|
||||
obj = brain.getObject().appy()
|
||||
exec expression
|
||||
|
@ -379,5 +373,5 @@ class AbstractWrapper:
|
|||
|
||||
p_data must be a dictionary whose keys are field names (strings) and
|
||||
whose values are the previous field values.'''
|
||||
self.o.addDataChange(data, labels=False)
|
||||
self.o.addDataChange(data)
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue