appy.gen: use new index 'getState' for indexing object states; reduced size of generated file config.py; optimized debug mode: class reload is not done automatically: a 'refresh' icon is available on view and edit views.

This commit is contained in:
Gaetan Delannay 2011-09-14 21:01:58 +02:00
parent 9258b76bdf
commit b6dcc42038
10 changed files with 106 additions and 121 deletions

View file

@ -326,7 +326,6 @@ class Search:
elif fieldName == 'description': elif fieldName == 'description':
if usage == 'search': return 'Description' if usage == 'search': return 'Description'
else: return None else: return None
elif fieldName == 'state': return 'review_state'
else: else:
return 'get%s%s'% (fieldName[0].upper(),fieldName[1:]) return 'get%s%s'% (fieldName[0].upper(),fieldName[1:])
@staticmethod @staticmethod
@ -487,24 +486,27 @@ class Type:
called for storing the name of the Appy field (p_name) and other called for storing the name of the Appy field (p_name) and other
attributes that are based on the name of the Appy p_klass, and the attributes that are based on the name of the Appy p_klass, and the
application name (p_appName).''' application name (p_appName).'''
if hasattr(self, 'name'): return # Already initialized
self.name = name self.name = name
# Determine prefix for this class
if not klass: prefix = appName
else: prefix = getClassName(klass, appName)
# Recompute the ID (and derived attributes) that may have changed if # Recompute the ID (and derived attributes) that may have changed if
# we are in debug mode (because we recreate new Type instances). # we are in debug mode (because we recreate new Type instances).
self.id = id(self) self.id = id(self)
if self.slaves: self.master_css = 'appyMaster master_%s' % self.id if self.slaves: self.master_css = 'appyMaster master_%s' % self.id
# Determine ids of i18n labels for this field # Determine ids of i18n labels for this field
labelName = name labelName = name
prefix = None trPrefix = None
if self.label: if self.label:
if isinstance(self.label, basestring): prefix = self.label if isinstance(self.label, basestring): trPrefix = self.label
else: # It is a tuple (prefix, name) else: # It is a tuple (trPrefix, name)
if self.label[1]: labelName = self.label[1] if self.label[1]: labelName = self.label[1]
if self.label[0]: prefix = self.label[0] if self.label[0]: trPrefix = self.label[0]
if not prefix: if not trPrefix:
if not klass: prefix = appName trPrefix = prefix
else: prefix = getClassName(klass, appName)
# Determine name to use for i18n # Determine name to use for i18n
self.labelId = '%s_%s' % (prefix, labelName) self.labelId = '%s_%s' % (trPrefix, labelName)
self.descrId = self.labelId + '_descr' self.descrId = self.labelId + '_descr'
self.helpId = self.labelId + '_help' self.helpId = self.labelId + '_help'
# Determine read and write permissions for this field # Determine read and write permissions for this field
@ -522,9 +524,10 @@ class Type:
self.writePermission = wp self.writePermission = wp
else: else:
self.writePermission = 'Modify portal content' self.writePermission = 'Modify portal content'
if isinstance(self, Ref):
self.backd = self.back.__dict__
if isinstance(self, Ref) and not self.isBack: if isinstance(self, Ref) and not self.isBack:
# We must initialise the corresponding back reference
self.back.klass = klass
self.back.init(self.back.attribute, self.klass, appName)
self.back.relationship = '%s_%s_rel' % (prefix, name) self.back.relationship = '%s_%s_rel' % (prefix, name)
def reload(self, klass, obj): def reload(self, klass, obj):
@ -534,6 +537,7 @@ class Type:
module has been performed.''' module has been performed.'''
res = getattr(klass, self.name, None) res = getattr(klass, self.name, None)
if not res: return self if not res: return self
if isinstance(self, Ref) and self.isBack: return self
res.init(self.name, klass, obj.getProductConfig().PROJECTNAME) res.init(self.name, klass, obj.getProductConfig().PROJECTNAME)
return res return res
@ -1693,6 +1697,7 @@ class Ref(Type):
back.isBack = True back.isBack = True
back.back = self back.back = self
back.backd = self.__dict__ back.backd = self.__dict__
setattr(klass, back.attribute, back)
# When displaying a tabular list of referenced objects, must we show # When displaying a tabular list of referenced objects, must we show
# the table headers? # the table headers?
self.showHeaders = showHeaders self.showHeaders = showHeaders
@ -2487,7 +2492,7 @@ class Transition:
targetState.updatePermissions(wf, obj) targetState.updatePermissions(wf, obj)
# Refresh catalog-related security if required # Refresh catalog-related security if required
if not obj.isTemporary(): if not obj.isTemporary():
obj.reindexObject(idxs=('allowedRolesAndUsers','review_state')) obj.reindexObject(idxs=('allowedRolesAndUsers', 'getState'))
# 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)
@ -2654,12 +2659,4 @@ class Config:
# Language that will be used as a basis for translating to other # Language that will be used as a basis for translating to other
# languages. # languages.
self.sourceLanguage = 'en' self.sourceLanguage = 'en'
# ------------------------------------------------------------------------------
# Special field "type" is mandatory for every class. If one class does not
# define it, we will add a copy of the instance defined below.
title = String(multiplicity=(1,1), show='edit')
title.init('title', None, 'appy')
state = String()
state.init('state', None, 'appy')
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -370,40 +370,21 @@ class Generator(AbstractGenerator):
# Compute the list of ordered attributes (forward and backward, # Compute the list of ordered attributes (forward and backward,
# inherited included) for every Appy class. # inherited included) for every Appy class.
attributes = [] attributes = []
attributesDict = []
for classDescr in classesAll: for classDescr in classesAll:
titleFound = False titleFound = False
attrs = [] names = []
attrNames = []
for name, appyType, klass in classDescr.getOrderedAppyAttributes(): for name, appyType, klass in classDescr.getOrderedAppyAttributes():
attrs.append(self.getAppyTypePath(name, appyType, klass)) names.append(name)
attrNames.append(name)
if name == 'title': titleFound = True if name == 'title': titleFound = True
# Add the "title" mandatory field if not found # Add the "title" mandatory field if not found
if not titleFound: if not titleFound: names.insert(0, 'title')
attrs.insert(0, 'copy.deepcopy(appy.gen.title)')
attrNames.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, rel in self.referers[classDescr.name]:
try: names.append(field.appyType.back.attribute)
getattr(field.classDescr.klass, field.fieldName) qNames = ['"%s"' % name for name in names]
klass = field.classDescr.klass attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
except AttributeError:
klass = field.classDescr.modelClass
attrs.append(self.getAppyTypePath(field.fieldName,
field.appyType, klass, isBack=True))
attrNames.append(field.appyType.back.attribute)
attributes.append('"%s":[%s]' % (classDescr.name,','.join(attrs)))
aDict = ''
i = -1
for attr in attrs:
i += 1
aDict += '"%s":attributes["%s"][%d],' % \
(attrNames[i], classDescr.name, i)
attributesDict.append('"%s":{%s}' % (classDescr.name, aDict))
repls['attributes'] = ',\n '.join(attributes) repls['attributes'] = ',\n '.join(attributes)
repls['attributesDict'] = ',\n '.join(attributesDict)
# Compute list of used roles for registering them if needed # Compute list of used roles for registering them if needed
specificRoles = self.getAllUsedRoles(plone=False) specificRoles = self.getAllUsedRoles(plone=False)
repls['roles'] = ','.join(['"%s"' % r.name for r in specificRoles]) repls['roles'] = ','.join(['"%s"' % r.name for r in specificRoles])

View file

@ -6,7 +6,7 @@ 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
from appy.gen import Type, Ref 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
from appy.gen.plone25.utils import updateRolesForPermission from appy.gen.plone25.utils import updateRolesForPermission
@ -231,10 +231,11 @@ class PloneInstaller:
'''Creates or updates the POD templates in the tool according to pod '''Creates or updates the POD templates in the tool according to pod
declarations in the application classes.''' declarations in the application classes.'''
# Creates the templates for Pod fields if they do not exist. # Creates the templates for Pod fields if they do not exist.
for contentType, appyTypes in self.attributes.iteritems(): for contentType in self.attributes.iterkeys():
appyClass = self.tool.getAppyClass(contentType) appyClass = self.tool.getAppyClass(contentType)
if not appyClass: continue # May be an abstract class if not appyClass: continue # May be an abstract class
for appyType in appyTypes: wrapperClass = self.tool.getAppyClass(contentType, wrapper=True)
for appyType in wrapperClass.__fields__:
if appyType.type != 'Pod': continue if appyType.type != 'Pod': continue
# Find the attribute that stores the template, and store on # Find the attribute that stores the template, and store on
# it the default one specified in the appyType if no # it the default one specified in the appyType if no
@ -290,6 +291,7 @@ class PloneInstaller:
nvProps.manage_changeProperties(**{'idsNotToList': current}) nvProps.manage_changeProperties(**{'idsNotToList': current})
self.tool = getattr(self.ploneSite, self.toolInstanceName) self.tool = getattr(self.ploneSite, self.toolInstanceName)
self.tool.refreshSecurity()
self.appyTool = self.tool.appy() self.appyTool = self.tool.appy()
if self.reinstall: if self.reinstall:
self.tool.createOrUpdate(False, None) self.tool.createOrUpdate(False, None)
@ -413,9 +415,12 @@ class PloneInstaller:
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.'''
indexInfo = {} # Create a special index for object state, that does not correspond to
for className, appyTypes in self.attributes.iteritems(): # a field.
for appyType in appyTypes: indexInfo = {'getState': 'FieldIndex'}
for className in self.attributes.iterkeys():
wrapperClass = self.tool.getAppyClass(className, wrapper=True)
for appyType in wrapperClass.__fields__:
if not appyType.indexed or (appyType.name == 'title'): continue if not appyType.indexed or (appyType.name == 'title'): continue
n = appyType.name n = appyType.name
indexName = 'get%s%s' % (n[0].upper(), n[1:]) indexName = 'get%s%s' % (n[0].upper(), n[1:])
@ -553,17 +558,25 @@ class ZopeInstaller:
def completeAppyTypes(self): def completeAppyTypes(self):
'''We complete here the initialisation process of every Appy type of '''We complete here the initialisation process of every Appy type of
every gen-class of the application.''' every gen-class of the application.'''
appName = self.productName
for klass in self.classes: for klass in self.classes:
# Store on wrapper class the ordered list of Appy types
wrapperClass = klass.wrapperClass
if not hasattr(wrapperClass, 'title'):
# Special field "type" is mandatory for every class.
title = String(multiplicity=(1,1), show='edit', indexed=True)
title.init('title', None, 'appy')
setattr(wrapperClass, 'title', title)
names = self.config.attributes[wrapperClass.__name__[:-8]]
wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names]
# Post-initialise every Appy type
for baseClass in klass.wrapperClass.__bases__: for baseClass in klass.wrapperClass.__bases__:
if baseClass.__name__ == 'AbstractWrapper': continue
for name, appyType in baseClass.__dict__.iteritems(): for name, appyType in baseClass.__dict__.iteritems():
if isinstance(appyType, Type): if not isinstance(appyType, Type) or \
appyType.init(name, baseClass, self.productName) (isinstance(appyType, Ref) and appyType.isBack):
# Do not forget back references continue # Back refs are initialised within fw refs
if isinstance(appyType, Ref): appyType.init(name, baseClass, appName)
bAppyType = appyType.back
bAppyType.init(bAppyType.attribute, appyType.klass,
self.productName)
bAppyType.klass = baseClass
def installApplication(self): def installApplication(self):
'''Performs some application-wide installation steps.''' '''Performs some application-wide installation steps.'''

View file

@ -79,10 +79,9 @@ class ToolMixin(BaseMixin):
def _appy_getAllFields(self, contentType): def _appy_getAllFields(self, contentType):
'''Returns the (translated) names of fields of p_contentType.''' '''Returns the (translated) names of fields of p_contentType.'''
res = [] res = []
for appyType in self.getProductConfig().attributes[contentType]: for appyType in self.getAllAppyTypes(className=contentType):
if appyType.name != 'title': # Will be included by default. if appyType.name == 'title': continue # Will be included by default.
label = '%s_%s' % (contentType, appyType.name) res.append((appyType.name, self.translate(appyType.labelId)))
res.append((appyType.name, self.translate(label)))
# Add object state # Add object state
res.append(('workflowState', self.translate('workflow_state'))) res.append(('workflowState', self.translate('workflow_state')))
return res return res
@ -91,7 +90,7 @@ class ToolMixin(BaseMixin):
'''Returns the (translated) names of fields that may be searched on '''Returns the (translated) names of fields that may be searched on
objects of type p_contentType (=indexed fields).''' objects of type p_contentType (=indexed fields).'''
res = [] res = []
for appyType in self.getProductConfig().attributes[contentType]: for appyType in self.getAllAppyTypes(className=contentType):
if appyType.indexed: if appyType.indexed:
res.append((appyType.name, self.translate(appyType.labelId))) res.append((appyType.name, self.translate(appyType.labelId)))
return res return res
@ -343,26 +342,23 @@ class ToolMixin(BaseMixin):
def getPublishedObject(self): def getPublishedObject(self):
'''Gets the currently published object, if its meta_class is among '''Gets the currently published object, if its meta_class is among
application classes.''' application classes.'''
rq = self.REQUEST obj = self.REQUEST['PUBLISHED']
obj = rq['PUBLISHED']
parent = obj.getParentNode() parent = obj.getParentNode()
if parent.id == 'skyn': if parent.id == 'skyn': obj = parent.getParentNode()
obj = parent.getParentNode() if obj.meta_type in self.getProductConfig().attributes: return obj
if obj.meta_type in self.getProductConfig().attributes:
return obj
return None
def getAppyClass(self, contentType): def getAppyClass(self, contentType, wrapper=False):
'''Gets the Appy Python class that is related to p_contentType.''' '''Gets the Appy Python class that is related to p_contentType.'''
# Retrieve first the Archetypes class corresponding to p_ContentType # Retrieve first the Archetypes class corresponding to p_ContentType
portalType = self.portal_types.get(contentType) portalType = self.portal_types.get(contentType)
if not portalType: return None if not portalType: return
atClassName = portalType.getProperty('content_meta_type') atClassName = portalType.getProperty('content_meta_type')
appName = self.getProductConfig().PROJECTNAME appName = self.getProductConfig().PROJECTNAME
exec 'from Products.%s.%s import %s as atClass' % \ exec 'from Products.%s.%s import %s as atClass' % \
(appName, atClassName, atClassName) (appName, atClassName, atClassName)
# Get then the Appy Python class # Get then the Appy Python class
return atClass.wrapperClass.__bases__[-1] if wrapper: return atClass.wrapperClass
else: return atClass.wrapperClass.__bases__[-1]
def getCreateMeans(self, contentTypeOrAppyClass): def getCreateMeans(self, contentTypeOrAppyClass):
'''Gets the different ways objects of p_contentTypeOrAppyClass (which '''Gets the different ways objects of p_contentTypeOrAppyClass (which
@ -395,6 +391,8 @@ class ToolMixin(BaseMixin):
'''This method checks if the currently logged user can trigger searches '''This method checks if the currently logged user can trigger searches
on a given p_rootClass. This is done by calling method "maySearch" on a given p_rootClass. This is done by calling method "maySearch"
on the class. If no such method exists, we return True.''' on the class. If no such method exists, we return True.'''
# When editign a form, one should avoid annoying the user with this.
if self.REQUEST['ACTUAL_URL'].endswith('/edit'): return
pythonClass = self.getAppyClass(rootClass) pythonClass = self.getAppyClass(rootClass)
if 'maySearch' in pythonClass.__dict__: if 'maySearch' in pythonClass.__dict__:
return pythonClass.maySearch(self.appy()) return pythonClass.maySearch(self.appy())

View file

@ -51,10 +51,7 @@ class BaseMixin:
for appyType in self.getAppyTypes('edit', rq.get('page')): for appyType in self.getAppyTypes('edit', rq.get('page')):
value = getattr(values, appyType.name, None) value = getattr(values, appyType.name, None)
appyType.store(obj, value) appyType.store(obj, value)
if created: if created: obj.unmarkCreationFlag()
# Now we have a title for the object, so we derive a nice id
obj.unmarkCreationFlag()
obj._renameAfterCreation(check_auto_id=True)
if previousData: if previousData:
# Keep in history potential changes on historized fields # Keep in history potential changes on historized fields
self.historizeData(previousData) self.historizeData(previousData)
@ -499,7 +496,6 @@ class BaseMixin:
'''Are we in debug mode ?''' '''Are we in debug mode ?'''
for arg in sys.argv: for arg in sys.argv:
if arg == 'debug-mode=on': return True if arg == 'debug-mode=on': return True
return False
def getClass(self, reloaded=False): def getClass(self, reloaded=False):
'''Returns the Appy class that dictates self's behaviour.''' '''Returns the Appy class that dictates self's behaviour.'''
@ -524,30 +520,34 @@ class BaseMixin:
def getAppyType(self, name, asDict=False, className=None): def getAppyType(self, name, asDict=False, className=None):
'''Returns the Appy type named p_name. If no p_className is defined, the '''Returns the Appy type named p_name. If no p_className is defined, the
field is supposed to belong to self's class.''' field is supposed to belong to self's class.'''
className = className or self.__class__.__name__ if not className:
attrs = self.getProductConfig().attributesDict[className] klass = self.__class__.wrapperClass
appyType = attrs.get(name, None) else:
if appyType and asDict: return appyType.__dict__ klass = self.getTool().getAppyClass(className, wrapper=True)
return appyType res = getattr(klass, name, None)
if res and asDict: return res.__dict__
return res
def getAllAppyTypes(self, className=None): def getAllAppyTypes(self, className=None):
'''Returns the ordered list of all Appy types for self's class if '''Returns the ordered list of all Appy types for self's class if
p_className is not specified, or for p_className else.''' p_className is not specified, or for p_className else.'''
className = className or self.__class__.__name__ if not className:
return self.getProductConfig().attributes[className] klass = self.__class__.wrapperClass
else:
klass = self.getTool().getAppyClass(className, wrapper=True)
return klass.__fields__
def getGroupedAppyTypes(self, layoutType, pageName): def getGroupedAppyTypes(self, layoutType, pageName):
'''Returns the fields sorted by group. For every field, the appyType '''Returns the fields sorted by group. For every field, the appyType
(dict version) is given.''' (dict version) is given.'''
res = [] res = []
groups = {} # The already encountered groups groups = {} # The already encountered groups
# In debug mode, reload the module containing self's class. # If param "refresh" is there, we must reload the Python class
debug = self.isDebug() refresh = ('refresh' in self.REQUEST)
if debug: if refresh:
klass = self.getClass(reloaded=True) klass = self.getClass(reloaded=True)
for appyType in self.getAllAppyTypes(): for appyType in self.getAllAppyTypes():
if debug: if refresh: appyType = appyType.reload(klass, self)
appyType = appyType.reload(klass, self)
if appyType.page.name != pageName: continue if appyType.page.name != pageName: continue
if not appyType.isShowable(self, layoutType): continue if not appyType.isShowable(self, layoutType): continue
if not appyType.group: if not appyType.group:
@ -594,13 +594,14 @@ class BaseMixin:
the result.''' the result.'''
res = [] res = []
for name in fieldNames: for name in fieldNames:
appyType = self.getAppyType(name, asDict) if name == 'state':
if appyType: res.append(appyType)
elif name == 'state':
# We do not return a appyType if the attribute is not a *real* # We do not return a appyType if the attribute is not a *real*
# attribute, but the workfow state. # attribute, but the workfow state.
res.append({'name': name, 'labelId': 'workflow_state', res.append({'name': name, 'labelId': 'workflow_state',
'filterable': False}) 'filterable': False})
else:
appyType = self.getAppyType(name, asDict)
if appyType: res.append(appyType)
else: else:
self.appy().log('Field "%s", used as shownInfo in a Ref, ' \ self.appy().log('Field "%s", used as shownInfo in a Ref, ' \
'was not found.' % name, type='warning') 'was not found.' % name, type='warning')

View file

@ -183,10 +183,8 @@ class Tool(ModelClass):
listBoxesMaximumWidth = Integer(default=100) listBoxesMaximumWidth = Integer(default=100)
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)
# First arg of Ref field below is None because we don't know yet if it will # Ref(User) will maybe be transformed into Ref(CustomUserClass).
# link to the predefined User class or a custom class defined in the users = Ref(User, multiplicity=(0,None), add=True, link=False,
# application.
users = Ref(None, multiplicity=(0,None), add=True, link=False,
back=Ref(attribute='toTool'), page='users', queryable=True, back=Ref(attribute='toTool'), page='users', queryable=True,
queryFields=('login',), showHeaders=True, queryFields=('login',), showHeaders=True,
shownInfo=('login', 'title', 'roles')) shownInfo=('login', 'title', 'roles'))

View file

@ -670,7 +670,7 @@
isEdit python: layoutType == 'edit'; isEdit python: layoutType == 'edit';
pageInfo python: phaseInfo['pagesInfo'][page]"> pageInfo python: phaseInfo['pagesInfo'][page]">
<br/> <br/>
<tal:previousButton condition="python: previousPage and pageInfo['showPrevious']"> <tal:previous condition="python: previousPage and pageInfo['showPrevious']">
<tal:button condition="isEdit"> <tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious" <input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
title="label_previous" i18n:attributes="title" i18n:domain="plone" title="label_previous" i18n:attributes="title" i18n:domain="plone"
@ -683,28 +683,34 @@
title="label_previous" i18n:attributes="title" i18n:domain="plone"/> title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
</a> </a>
</tal:link> </tal:link>
</tal:previousButton> </tal:previous>
<tal:saveButton condition="python: isEdit and pageInfo['showSave']"> <tal:save condition="python: isEdit and pageInfo['showSave']">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonOk" <input type="image" class="imageInput" style="cursor:pointer" name="buttonOk"
title="label_save" i18n:attributes="title" i18n:domain="plone" title="label_save" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/save.png"/> tal:attributes="src string:$portal_url/skyn/save.png"/>
</tal:saveButton> </tal:save>
<tal:cancelButton condition="python: isEdit and pageInfo['showCancel']"> <tal:cancel condition="python: isEdit and pageInfo['showCancel']">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel" <input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel"
title="label_cancel" i18n:attributes="title" i18n:domain="plone" title="label_cancel" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/cancel.png"/> tal:attributes="src string:$portal_url/skyn/cancel.png"/>
</tal:cancelButton> </tal:cancel>
<tal:editLink condition="python: not isEdit and pageInfo['showOnEdit']"> <tal:edit condition="python: not isEdit and pageInfo['showOnEdit']">
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer" <img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page); tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
src string: $portal_url/skyn/editBig.png" src string: $portal_url/skyn/editBig.png"
tal:condition="python: member.has_permission('Modify portal content', contextObj)"/> tal:condition="python: member.has_permission('Modify portal content', contextObj)"/>
</tal:editLink> </tal:edit>
<tal:nextButton condition="python: nextPage and pageInfo['showNext']"> <tal:refresh condition="contextObj/isDebug">
<img title="Refresh" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode=layoutType, page=page, refresh='yes');
src string: $portal_url/skyn/refresh.png"/>
</tal:refresh>
<tal:next condition="python: nextPage and pageInfo['showNext']">
<tal:button condition="isEdit"> <tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonNext" <input type="image" class="imageInput" style="cursor:pointer" name="buttonNext"
title="label_next" i18n:attributes="title" i18n:domain="plone" title="label_next" i18n:attributes="title" i18n:domain="plone"
@ -717,7 +723,7 @@
title="label_next" i18n:attributes="title" i18n:domain="plone"/> title="label_next" i18n:attributes="title" i18n:domain="plone"/>
</a> </a>
</tal:link> </tal:link>
</tal:nextButton> </tal:next>
</div> </div>
<tal:comment replace="nothing"> <tal:comment replace="nothing">

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -59,9 +59,6 @@ catalogMap = {}
# In the following dict, we store, for every Appy class, the ordered list of # In the following dict, we store, for every Appy class, the ordered list of
# appy types (included inherited ones). # appy types (included inherited ones).
attributes = {<!attributes!>} attributes = {<!attributes!>}
# In the following dict, we store, for every Appy class, a dict of appy types
# keyed by their names.
attributesDict = {<!attributesDict!>}
# Application roles # Application roles
applicationRoles = [<!roles!>] applicationRoles = [<!roles!>]

View file

@ -4,7 +4,7 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import os, os.path, time, mimetypes, random import os, os.path, time, mimetypes, random
import appy.pod import appy.pod
from appy.gen import Type, Search, Ref from appy.gen import Type, Search, Ref, String
from appy.gen.utils import sequenceTypes from appy.gen.utils import sequenceTypes
from appy.shared.utils import getOsTempFolder, executeCommand, normalizeString from appy.shared.utils import getOsTempFolder, executeCommand, normalizeString
from appy.shared.xml_parser import XmlMarshaller from appy.shared.xml_parser import XmlMarshaller
@ -41,7 +41,6 @@ class AbstractWrapper(object):
elif name == 'typeName': return self.__class__.__bases__[-1].__name__ elif name == 'typeName': return self.__class__.__bases__[-1].__name__
elif name == 'id': return self.o.id elif name == 'id': return self.o.id
elif name == 'uid': return self.o.UID() elif name == 'uid': return self.o.UID()
elif name == 'title': return self.o.Title()
elif name == 'klass': return self.__class__.__bases__[-1] elif name == 'klass': return self.__class__.__bases__[-1]
elif name == 'url': return self.o.absolute_url() elif name == 'url': return self.o.absolute_url()
elif name == 'state': return self.o.getState() elif name == 'state': return self.o.getState()
@ -57,12 +56,7 @@ class AbstractWrapper(object):
return self.o.portal_membership.getAuthenticatedMember() return self.o.portal_membership.getAuthenticatedMember()
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.
try:
res = object.__getattribute__(self, name) res = object.__getattribute__(self, name)
except AttributeError, ae:
# Maybe a back reference?
res = self.o.getAppyType(name)
if not res: raise ae
# If we got an Appy type, return the value of this type for this object # If we got an Appy type, return the value of this type for this object
if isinstance(res, Type): if isinstance(res, Type):
o = self.o o = self.o