appy.gen: added, for every Type, param 'label' allowing to specify an existing i18n label (the one from another field for instance), thus avoiding to generate i18n labels for this Type; optimized generation of appyWrappers.py (more than twice less code).

This commit is contained in:
Gaetan Delannay 2011-09-06 21:46:57 +02:00
parent 813b47843c
commit 3c95ac083d
8 changed files with 162 additions and 103 deletions

View file

@ -361,7 +361,7 @@ class Type:
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, specificWritePermission, searchable, specificReadPermission, specificWritePermission,
width, height, maxChars, colspan, master, masterValue, focus, width, height, maxChars, colspan, master, masterValue, focus,
historized, sync, mapping): historized, sync, mapping, label):
# The validator restricts which values may be defined. It can be an # The validator restricts which values may be defined. It can be an
# interval (1,None), a list of string values ['choice1', 'choice2'], # interval (1,None), a list of string values ['choice1', 'choice2'],
# a regular expression, a custom function, a Selection instance, etc. # a regular expression, a custom function, a Selection instance, etc.
@ -465,10 +465,18 @@ class Type:
self.filterable = False self.filterable = False
# Can this field have values that can be edited and validated? # Can this field have values that can be edited and validated?
self.validable = True self.validable = True
# The base label for translations is normally generated automatically.
# It is made of 2 parts: the prefix, based on class name, and the name,
# which is the field name by default. You can change this by specifying
# a value for param "label". If this value is a string, it will be
# understood as a new prefix. If it is a tuple, it will represent the
# prefix and another name. If you want to specify a new name only, and
# not a prefix, write (None, newName).
self.label = label
def init(self, name, klass, appName): def init(self, name, klass, appName):
'''When the application server starts, this secondary constructor is '''When the application server starts, this secondary constructor is
called for storing the names 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).'''
self.name = name self.name = name
@ -477,9 +485,18 @@ class Type:
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
if not klass: prefix = appName labelName = name
else: prefix = getClassName(klass, appName) prefix = None
self.labelId = '%s_%s' % (prefix, name) if self.label:
if isinstance(self.label, basestring): prefix = self.label
else: # It is a tuple (prefix, name)
if self.label[1]: labelName = self.label[1]
if self.label[0]: prefix = self.label[0]
if not prefix:
if not klass: prefix = appName
else: prefix = getClassName(klass, appName)
# Determine name to use for i18n
self.labelId = '%s_%s' % (prefix, 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
@ -932,12 +949,13 @@ class Integer(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=6, height=None, specificWritePermission=False, width=6, height=None,
maxChars=13, colspan=1, master=None, masterValue=None, maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None): focus=False, historized=False, mapping=None, label=None):
Type.__init__(self, validator, multiplicity, index, default, optional, Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan, specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, True, mapping) master, masterValue, focus, historized, True, mapping,
label)
self.pythonType = long self.pythonType = long
def validateValue(self, obj, value): def validateValue(self, obj, value):
@ -961,8 +979,8 @@ class Float(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=6, height=None, specificWritePermission=False, width=6, height=None,
maxChars=13, colspan=1, master=None, masterValue=None, maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, precision=None, focus=False, historized=False, mapping=None, label=None,
sep=(',', '.')): precision=None, sep=(',', '.')):
# The precision is the number of decimal digits. This number is used # The precision is the number of decimal digits. This number is used
# for rendering the float, but the internal float representation is not # for rendering the float, but the internal float representation is not
# rounded. # rounded.
@ -981,7 +999,7 @@ class Float(Type):
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
False, specificReadPermission, specificWritePermission, False, specificReadPermission, specificWritePermission,
width, height, maxChars, colspan, master, masterValue, width, height, maxChars, colspan, master, masterValue,
focus, historized, True, mapping) focus, historized, True, mapping, label)
self.pythonType = float self.pythonType = float
def getFormattedValue(self, obj, value): def getFormattedValue(self, obj, value):
@ -1130,7 +1148,8 @@ class String(Type):
indexed=False, searchable=False, specificReadPermission=False, indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, transform='none'): focus=False, historized=False, mapping=None, label=None,
transform='none'):
self.format = format self.format = format
# The following field has a direct impact on the text entered by the # The following field has a direct impact on the text entered by the
# user. It applies a transformation on it, exactly as does the CSS # user. It applies a transformation on it, exactly as does the CSS
@ -1142,7 +1161,8 @@ class String(Type):
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan, specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, True, mapping) master, masterValue, focus, historized, True, mapping,
label)
self.isSelect = self.isSelection() self.isSelect = self.isSelection()
# Default width, height and maxChars vary according to String format # Default width, height and maxChars vary according to String format
if width == None: if width == None:
@ -1374,12 +1394,13 @@ class Boolean(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None): focus=False, historized=False, mapping=None, label=None):
Type.__init__(self, validator, multiplicity, index, default, optional, Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, True, mapping) master, masterValue, focus, historized, True, mapping,
label)
self.pythonType = bool self.pythonType = bool
def getDefaultLayouts(self): def getDefaultLayouts(self):
@ -1417,7 +1438,7 @@ class Date(Type):
indexed=False, searchable=False, specificReadPermission=False, indexed=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None): focus=False, historized=False, mapping=None, label=None):
self.format = format self.format = format
self.calendar = calendar self.calendar = calendar
self.startYear = startYear self.startYear = startYear
@ -1429,7 +1450,8 @@ class Date(Type):
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, True, mapping) master, masterValue, focus, historized, True, mapping,
label)
def getCss(self, layoutType): def getCss(self, layoutType):
if (layoutType == 'edit') and self.calendar: if (layoutType == 'edit') and self.calendar:
@ -1490,13 +1512,14 @@ class File(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, isImage=False): focus=False, historized=False, mapping=None, label=None,
isImage=False):
self.isImage = isImage self.isImage = isImage
Type.__init__(self, validator, multiplicity, index, default, optional, Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
False, specificReadPermission, specificWritePermission, False, specificReadPermission, specificWritePermission,
width, height, None, colspan, master, masterValue, focus, width, height, None, colspan, master, masterValue, focus,
historized, True, mapping) historized, True, mapping, label)
@staticmethod @staticmethod
def getFileObject(filePath, fileName=None, zope=False): def getFileObject(filePath, fileName=None, zope=False):
@ -1640,8 +1663,8 @@ class Ref(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=5, specificWritePermission=False, width=None, height=5,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, queryable=False, focus=False, historized=False, mapping=None, label=None,
queryFields=None, queryNbCols=1): queryable=False, queryFields=None, queryNbCols=1):
self.klass = klass self.klass = klass
self.attribute = attribute self.attribute = attribute
# May the user add new objects through this ref ? # May the user add new objects through this ref ?
@ -1656,6 +1679,7 @@ class Ref(Type):
self.link = link self.link = link
# May the user unlink existing objects? # May the user unlink existing objects?
self.unlink = unlink self.unlink = unlink
self.back = None
if back: if back:
# It is a forward reference # It is a forward reference
self.isBack = False self.isBack = False
@ -1691,7 +1715,7 @@ class Ref(Type):
editDefault, show, page, group, layouts, move, indexed, editDefault, show, page, group, layouts, move, indexed,
False, specificReadPermission, specificWritePermission, False, specificReadPermission, specificWritePermission,
width, height, None, colspan, master, masterValue, focus, width, height, None, colspan, master, masterValue, focus,
historized, sync, mapping) historized, sync, mapping, label)
self.validable = self.link self.validable = self.link
def getDefaultLayouts(self): return {'view': Table('l-f'), 'edit': 'lrv-f'} def getDefaultLayouts(self): return {'view': Table('l-f'), 'edit': 'lrv-f'}
@ -1858,7 +1882,7 @@ class Computed(Type):
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, method=None, plainText=True, maxChars=None, colspan=1, method=None, plainText=True,
master=None, masterValue=None, focus=False, historized=False, master=None, masterValue=None, focus=False, historized=False,
sync=True, mapping=None, context={}): sync=True, mapping=None, label=None, context={}):
# The Python method used for computing the field value # The Python method used for computing the field value
self.method = method self.method = method
# Does field computation produce plain text or XHTML? # Does field computation produce plain text or XHTML?
@ -1875,7 +1899,7 @@ class Computed(Type):
False, show, page, group, layouts, move, indexed, False, False, show, page, group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, sync, mapping) historized, sync, mapping, label)
self.validable = False self.validable = False
def callMacro(self, obj, macroPath): def callMacro(self, obj, macroPath):
@ -1925,7 +1949,7 @@ class Action(Type):
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, action=None, result='computation', maxChars=None, colspan=1, action=None, result='computation',
confirm=False, master=None, masterValue=None, focus=False, confirm=False, master=None, masterValue=None, focus=False,
historized=False, mapping=None): historized=False, mapping=None, label=None):
# Can be a single method or a list/tuple of methods # Can be a single method or a list/tuple of methods
self.action = action self.action = action
# For the 'result' param: # For the 'result' param:
@ -1946,7 +1970,7 @@ class Action(Type):
False, show, page, group, layouts, move, indexed, False, False, show, page, group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, False, mapping) historized, False, mapping, label)
self.validable = False self.validable = False
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'} def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
@ -1994,12 +2018,12 @@ class Info(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None): focus=False, historized=False, mapping=None, label=None):
Type.__init__(self, None, (0,1), index, default, optional, Type.__init__(self, None, (0,1), index, default, optional,
False, show, page, group, layouts, move, indexed, False, False, show, page, group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, False, mapping) historized, False, mapping, label)
self.validable = False self.validable = False
class Pod(Type): class Pod(Type):
@ -2015,9 +2039,9 @@ class Pod(Type):
searchable=False, specificReadPermission=False, searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, template=None, focus=False, historized=False, mapping=None, label=None,
context=None, action=None, askAction=False, stylesMapping={}, template=None, context=None, action=None, askAction=False,
freezeFormat='pdf'): stylesMapping={}, freezeFormat='pdf'):
# The following param stores the path to a POD template # The following param stores the path to a POD template
self.template = template self.template = template
# The context is a dict containing a specific pod context, or a method # The context is a dict containing a specific pod context, or a method
@ -2037,7 +2061,8 @@ class Pod(Type):
False, show, page, group, layouts, move, indexed, False, show, page, group, layouts, move, indexed,
searchable, specificReadPermission, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping) master, masterValue, focus, historized, False, mapping,
label)
self.validable = False self.validable = False
def isFrozen(self, obj): def isFrozen(self, obj):

View file

@ -147,17 +147,18 @@ class FieldDescriptor:
(self.fieldName not in ('title', 'description')): (self.fieldName not in ('title', 'description')):
self.classDescr.addIndexMethod(self) self.classDescr.addIndexMethod(self)
# i18n labels # i18n labels
i18nPrefix = "%s_%s" % (self.classDescr.name, self.fieldName)
# Create labels for generating them in i18n files.
messages = self.generator.labels messages = self.generator.labels
if self.appyType.hasLabel: if not self.appyType.label:
messages.append(self.produceMessage(i18nPrefix)) # Create labels for generating them in i18n files, only if required.
if self.appyType.hasDescr: i18nPrefix = "%s_%s" % (self.classDescr.name, self.fieldName)
descrId = i18nPrefix + '_descr' if self.appyType.hasLabel:
messages.append(self.produceMessage(descrId,isLabel=False)) messages.append(self.produceMessage(i18nPrefix))
if self.appyType.hasHelp: if self.appyType.hasDescr:
helpId = i18nPrefix + '_help' descrId = i18nPrefix + '_descr'
messages.append(self.produceMessage(helpId, isLabel=False)) messages.append(self.produceMessage(descrId,isLabel=False))
if self.appyType.hasHelp:
helpId = i18nPrefix + '_help'
messages.append(self.produceMessage(helpId, isLabel=False))
# Create i18n messages linked to pages and phases, only if there is more # Create i18n messages linked to pages and phases, only if there is more
# than one page/phase for the class. # than one page/phase for the class.
ppMsgs = [] ppMsgs = []
@ -561,8 +562,8 @@ class TranslationClassDescriptor(ClassDescriptor):
def addLabelField(self, messageId, page): def addLabelField(self, messageId, page):
'''Adds a Computed field that will display, in the source language, the '''Adds a Computed field that will display, in the source language, the
content of the text to translate.''' content of the text to translate.'''
field = Computed(method=self.modelClass.computeLabel, plainText=False, field = Computed(method=self.modelClass.label, plainText=False,
page=page, show=self.modelClass.showField, layouts='f') page=page, show=self.modelClass.show, layouts='f')
self.addField('%s_label' % messageId, field) self.addField('%s_label' % messageId, field)
def addMessageField(self, messageId, page, i18nFiles): def addMessageField(self, messageId, page, i18nFiles):
@ -570,7 +571,7 @@ class TranslationClassDescriptor(ClassDescriptor):
class, on a given p_page. We need i18n files p_i18nFiles for class, on a given p_page. We need i18n files p_i18nFiles for
fine-tuning the String type to generate for this field (one-line? fine-tuning the String type to generate for this field (one-line?
several lines?...)''' several lines?...)'''
params = {'page':page, 'layouts':'f', 'show':self.modelClass.showField} params = {'page':page, 'layouts':'f', 'show': self.modelClass.show}
appName = self.generator.applicationName appName = self.generator.applicationName
# Scan all messages corresponding to p_messageId from all translation # Scan all messages corresponding to p_messageId from all translation
# files. We will define field length from the longer found message # files. We will define field length from the longer found message

View file

@ -590,9 +590,8 @@ class Generator(AbstractGenerator):
'''Generates the Plone tool that corresponds to this application.''' '''Generates the Plone tool that corresponds to this application.'''
Msg = PoMessage Msg = PoMessage
# Create Tool-related i18n-related messages # Create Tool-related i18n-related messages
self.labels += [ msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
Msg(self.tool.name, '', Msg.CONFIG % self.applicationName), self.labels.append(msg)
Msg('%s_edit_descr' % self.tool.name, '', ' ')]
# Tune the Ref field between Tool and User # Tune the Ref field between Tool and User
Tool.users.klass = User Tool.users.klass = User
@ -604,7 +603,6 @@ class Generator(AbstractGenerator):
klassType = klass.name[len(self.applicationName):] klassType = klass.name[len(self.applicationName):]
klass.generateSchema() klass.generateSchema()
self.labels += [ Msg(klass.name, '', klassType), self.labels += [ Msg(klass.name, '', klassType),
Msg('%s_edit_descr' % klass.name, '', ' '),
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({'fields': klass.schema, 'methods': klass.methods,
@ -702,12 +700,10 @@ class Generator(AbstractGenerator):
'implements': implements, 'baseSchema': baseSchema, 'static': '', 'implements': implements, 'baseSchema': baseSchema, 'static': '',
'register': register, 'toolInstanceName': self.toolInstanceName}) 'register': register, 'toolInstanceName': self.toolInstanceName})
fileName = '%s.py' % classDescr.name fileName = '%s.py' % classDescr.name
# Create i18n labels (class name, description 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__)
poMsg.produceNiceDefault() poMsg.produceNiceDefault()
self.labels.append(poMsg) self.labels.append(poMsg)
poMsgDescr = PoMessage('%s_edit_descr' % classDescr.name, '', ' ')
self.labels.append(poMsgDescr)
poMsgPl = PoMessage('%s_plural' % classDescr.name, '', poMsgPl = PoMessage('%s_plural' % classDescr.name, '',
classDescr.klass.__name__+'s') classDescr.klass.__name__+'s')
poMsgPl.produceNiceDefault() poMsgPl.produceNiceDefault()

View file

@ -9,6 +9,33 @@
import types import types
from appy.gen import * from appy.gen import *
# Prototypical instances of every type -----------------------------------------
class Protos:
protos = {}
# List of attributes that can't be given to a Type constructor
notInit = ('id', 'type', 'pythonType', 'slaves', 'isSelect', 'hasLabel',
'hasDescr', 'hasHelp', 'master_css', 'required', 'filterable',
'validable', 'backd', 'isBack', 'sync', 'pageName')
@classmethod
def get(self, appyType):
'''Returns a prototype instance for p_appyType.'''
className = appyType.__class__.__name__
isString = (className == 'String')
if isString:
# For Strings, we create one prototype per format, because default
# values may change according to format.
className += str(appyType.format)
if className in self.protos: return self.protos[className]
# The prototype does not exist yet: create it
if isString:
proto = appyType.__class__(format=appyType.format)
# Now, we fake to be able to detect default values
proto.format = 0
else:
proto = appyType.__class__()
self.protos[className] = proto
return proto
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class ModelClass: class ModelClass:
'''This class is the abstract class of all predefined application classes '''This class is the abstract class of all predefined application classes
@ -17,60 +44,73 @@ class ModelClass:
in order to avoid name conflicts with user-defined parts of the in order to avoid name conflicts with user-defined parts of the
application model.''' application model.'''
_appy_attributes = [] # We need to keep track of attributes order. _appy_attributes = [] # We need to keep track of attributes order.
# When creating a new instance of a ModelClass, the following attributes
# must not be given in the constructor (they are computed attributes).
_appy_notinit = ('id', 'type', 'pythonType', 'slaves', 'isSelect',
'hasLabel', 'hasDescr', 'hasHelp', 'master_css',
'required', 'filterable', 'validable', 'backd', 'isBack',
'sync', 'pageName')
@classmethod @classmethod
def _appy_getTypeBody(klass, appyType): def _appy_getTypeBody(klass, appyType, wrapperName):
'''This method returns the code declaration for p_appyType.''' '''This method returns the code declaration for p_appyType.'''
typeArgs = '' typeArgs = ''
for attrName, attrValue in appyType.__dict__.iteritems(): proto = Protos.get(appyType)
if attrName in ModelClass._appy_notinit: continue for name, value in appyType.__dict__.iteritems():
if attrName == 'layouts': # Some attrs can't be given to the constructor
if klass.__name__ == 'Tool': continue if name in Protos.notInit: continue
# If the given value corresponds to the default value, don't give it
if value == getattr(proto, name): continue
if name == 'layouts':
# For Tool attributes we do not copy layout info. Indeed, most # For Tool attributes we do not copy layout info. Indeed, most
# fields added to the Tool are config-related attributes whose # fields added to the Tool are config-related attributes whose
# layouts must be standard. # layouts must be standard.
attrValue = appyType.getInputLayouts() if klass.__name__ == 'Tool': continue
elif isinstance(attrValue, basestring): layouts = appyType.getInputLayouts()
attrValue = '"%s"' % attrValue # For the Translation class that has potentially thousands of
elif isinstance(attrValue, Ref): # attributes, the most used layout is cached in a global var in
if not attrValue.isBack: continue # named "tfw" in appyWrappers.py.
attrValue = klass._appy_getTypeBody(attrValue) if (klass.__name__ == 'Translation') and \
elif type(attrValue) == type(ModelClass): (layouts == '{"edit":"f","cell":"f","view":"f",}'):
moduleName = attrValue.__module__ value = 'tfw'
if moduleName.startswith('appy.gen'):
attrValue = attrValue.__name__
else: else:
attrValue = '%s.%s' % (moduleName, attrValue.__name__) value = appyType.getInputLayouts()
elif isinstance(attrValue, Selection): elif isinstance(value, basestring):
attrValue = 'Selection("%s")' % attrValue.methodName value = '"%s"' % value
elif isinstance(attrValue, Group): elif isinstance(value, Ref):
attrValue = 'Group("%s")' % attrValue.name if not value.isBack: continue
elif isinstance(attrValue, Page): value = klass._appy_getTypeBody(value, wrapperName)
attrValue = 'pages["%s"]' % attrValue.name elif type(value) == type(ModelClass):
elif callable(attrValue): moduleName = value.__module__
attrValue = '%sWrapper.%s'% (klass.__name__, attrValue.__name__) if moduleName.startswith('appy.gen'):
typeArgs += '%s=%s,' % (attrName, attrValue) value = value.__name__
else:
value = '%s.%s' % (moduleName, value.__name__)
elif isinstance(value, Selection):
value = 'Selection("%s")' % value.methodName
elif isinstance(value, Group):
value = 'Group("%s")' % value.name
elif isinstance(value, Page):
value = 'pages["%s"]' % value.name
elif callable(value):
value = '%s.%s' % (wrapperName, value.__name__)
typeArgs += '%s=%s,' % (name, value)
return '%s(%s)' % (appyType.__class__.__name__, typeArgs) return '%s(%s)' % (appyType.__class__.__name__, typeArgs)
@classmethod @classmethod
def _appy_getBody(klass): def _appy_getBody(klass):
'''This method returns the code declaration of this class. We will dump '''This method returns the code declaration of this class. We will dump
this in appyWrappers.py in the resulting product.''' this in appyWrappers.py in the resulting product.'''
res = 'class %s(%sWrapper):\n' % (klass.__name__, klass.__name__) className = klass.__name__
if klass.__name__ == 'Tool': # Determine the name of the class and its wrapper. Because so much
res += ' folder=True\n' # attributes can be generated on a TranslationWrapper, shortcutting it
# to 'TW' may reduce the generated file from several kilobytes.
if className == 'Translation': wrapperName = 'WT'
else: wrapperName = 'W%s' % className
res = 'class %s(%s):\n' % (className, wrapperName)
# Tool must be folderish
if className == 'Tool': res += ' folder=True\n'
# First, scan all attributes, determine all used pages and create a # First, scan all attributes, determine all used pages and create a
# dict with it. It will prevent us from creating a new Page instance # dict with it. It will prevent us from creating a new Page instance
# for every field. # for every field.
pages = {} pages = {}
for attrName in klass._appy_attributes: layouts = []
exec 'appyType = klass.%s' % attrName for name in klass._appy_attributes:
exec 'appyType = klass.%s' % name
if appyType.page.name not in pages: if appyType.page.name not in pages:
pages[appyType.page.name] = appyType.page pages[appyType.page.name] = appyType.page
res += ' pages = {' res += ' pages = {'
@ -81,9 +121,10 @@ class ModelClass:
res += '"%s":Page("%s", show=%s),'% (page.name, page.name, pageShow) res += '"%s":Page("%s", show=%s),'% (page.name, page.name, pageShow)
res += '}\n' res += '}\n'
# Secondly, dump every attribute # Secondly, dump every attribute
for attrName in klass._appy_attributes: for name in klass._appy_attributes:
exec 'appyType = klass.%s' % attrName exec 'appyType = klass.%s' % name
res += ' %s=%s\n' % (attrName, klass._appy_getTypeBody(appyType)) typeBody = klass._appy_getTypeBody(appyType, wrapperName)
res += ' %s=%s\n' % (name, typeBody)
return res return res
# The User class --------------------------------------------------------------- # The User class ---------------------------------------------------------------
@ -115,8 +156,8 @@ class Translation(ModelClass):
po = Action(action=getPoFile, page=Page('actions', show='view'), po = Action(action=getPoFile, page=Page('actions', show='view'),
result='filetmp') result='filetmp')
title = String(show=False, indexed=True) title = String(show=False, indexed=True)
def computeLabel(self): pass def label(self): pass
def showField(self, name): pass def show(self, name): pass
# The Tool class --------------------------------------------------------------- # The Tool class ---------------------------------------------------------------
# Here are the prefixes of the fields generated on the Tool. # Here are the prefixes of the fields generated on the Tool.

View file

@ -564,11 +564,6 @@
<b class="appyTitle" tal:content="contextObj/title_or_id"></b> <b class="appyTitle" tal:content="contextObj/title_or_id"></b>
</td> </td>
</tr> </tr>
<tr tal:define="descrLabel python: contextObj.translate('%s_edit_descr' % contextObj.portal_type)"
tal:condition="descrLabel/strip" align="left">
<tal:comment replace="nothing">Content type description</tal:comment>
<td colspan="2" class="discreet" tal:content="descrLabel"/>
</tr>
<tr align="left"> <tr align="left">
<td class="documentByLine" colspan="2"> <td class="documentByLine" colspan="2">
<tal:comment replace="nothing">Creator and last modification date</tal:comment> <tal:comment replace="nothing">Creator and last modification date</tal:comment>

View file

@ -27,7 +27,7 @@ class <!genClassName!>(<!parents!>):
default_view = 'skyn/view' default_view = 'skyn/view'
suppl_views = () suppl_views = ()
typeDescription = '<!genClassName!>' typeDescription = '<!genClassName!>'
typeDescMsgId = '<!genClassName!>_edit_descr' typeDescMsgId = '<!genClassName!>'
i18nDomain = '<!applicationName!>' i18nDomain = '<!applicationName!>'
wrapperClass = <!genClassName!>_Wrapper wrapperClass = <!genClassName!>_Wrapper
schema = fullSchema schema = fullSchema

View file

@ -1,11 +1,12 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from appy.gen import * from appy.gen import *
from appy.gen.plone25.wrappers import AbstractWrapper from appy.gen.plone25.wrappers import AbstractWrapper
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT
from Globals import InitializeClass from Globals import InitializeClass
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
<!imports!> <!imports!>
<!User!> <!User!>

View file

@ -6,7 +6,7 @@ from appy.shared.utils import getOsTempFolder
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class TranslationWrapper(AbstractWrapper): class TranslationWrapper(AbstractWrapper):
def computeLabel(self, field): def label(self, field):
'''The label for a text to translate displays the text of the '''The label for a text to translate displays the text of the
corresponding message in the source translation.''' corresponding message in the source translation.'''
tool = self.tool tool = self.tool
@ -32,7 +32,7 @@ class TranslationWrapper(AbstractWrapper):
'<img src="help.png"/></acronym>%s</div>' % \ '<img src="help.png"/></acronym>%s</div>' % \
(fieldName, sourceMsg) (fieldName, sourceMsg)
def showField(self, field): def show(self, field):
'''We show a field (or its label) only if the corresponding source '''We show a field (or its label) only if the corresponding source
message is not empty.''' message is not empty.'''
tool = self.tool tool = self.tool