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:
Gaetan Delannay 2010-10-14 14:43:56 +02:00
parent 9f4db88bdf
commit 990e16c6e7
47 changed files with 1006 additions and 1297 deletions

View file

@ -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
# ------------------------------------------------------------------------------

View file

@ -1,6 +0,0 @@
# ------------------------------------------------------------------------------
from appy.gen.plone25.wrappers import AbstractWrapper
# ------------------------------------------------------------------------------
class PodTemplateWrapper(AbstractWrapper): pass
# ------------------------------------------------------------------------------

View file

@ -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
# ------------------------------------------------------------------------------

View file

@ -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)
# ------------------------------------------------------------------------------

View file

@ -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)
# ------------------------------------------------------------------------------