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:
parent
813b47843c
commit
3c95ac083d
|
@ -361,7 +361,7 @@ class Type:
|
|||
editDefault, show, page, group, layouts, move, indexed,
|
||||
searchable, specificReadPermission, specificWritePermission,
|
||||
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
|
||||
# interval (1,None), a list of string values ['choice1', 'choice2'],
|
||||
# a regular expression, a custom function, a Selection instance, etc.
|
||||
|
@ -465,10 +465,18 @@ class Type:
|
|||
self.filterable = False
|
||||
# Can this field have values that can be edited and validated?
|
||||
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):
|
||||
'''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
|
||||
application name (p_appName).'''
|
||||
self.name = name
|
||||
|
@ -477,9 +485,18 @@ class Type:
|
|||
self.id = id(self)
|
||||
if self.slaves: self.master_css = 'appyMaster master_%s' % self.id
|
||||
# Determine ids of i18n labels for this field
|
||||
if not klass: prefix = appName
|
||||
else: prefix = getClassName(klass, appName)
|
||||
self.labelId = '%s_%s' % (prefix, name)
|
||||
labelName = name
|
||||
prefix = None
|
||||
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.helpId = self.labelId + '_help'
|
||||
# Determine read and write permissions for this field
|
||||
|
@ -932,12 +949,13 @@ class Integer(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=6, height=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,
|
||||
editDefault, show, page, group, layouts, move, indexed,
|
||||
searchable, specificReadPermission,
|
||||
specificWritePermission, width, height, maxChars, colspan,
|
||||
master, masterValue, focus, historized, True, mapping)
|
||||
master, masterValue, focus, historized, True, mapping,
|
||||
label)
|
||||
self.pythonType = long
|
||||
|
||||
def validateValue(self, obj, value):
|
||||
|
@ -961,8 +979,8 @@ class Float(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=6, height=None,
|
||||
maxChars=13, colspan=1, master=None, masterValue=None,
|
||||
focus=False, historized=False, mapping=None, precision=None,
|
||||
sep=(',', '.')):
|
||||
focus=False, historized=False, mapping=None, label=None,
|
||||
precision=None, sep=(',', '.')):
|
||||
# The precision is the number of decimal digits. This number is used
|
||||
# for rendering the float, but the internal float representation is not
|
||||
# rounded.
|
||||
|
@ -981,7 +999,7 @@ class Float(Type):
|
|||
editDefault, show, page, group, layouts, move, indexed,
|
||||
False, specificReadPermission, specificWritePermission,
|
||||
width, height, maxChars, colspan, master, masterValue,
|
||||
focus, historized, True, mapping)
|
||||
focus, historized, True, mapping, label)
|
||||
self.pythonType = float
|
||||
|
||||
def getFormattedValue(self, obj, value):
|
||||
|
@ -1130,7 +1148,8 @@ class String(Type):
|
|||
indexed=False, searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=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
|
||||
# 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
|
||||
|
@ -1142,7 +1161,8 @@ class String(Type):
|
|||
editDefault, show, page, group, layouts, move, indexed,
|
||||
searchable, specificReadPermission,
|
||||
specificWritePermission, width, height, maxChars, colspan,
|
||||
master, masterValue, focus, historized, True, mapping)
|
||||
master, masterValue, focus, historized, True, mapping,
|
||||
label)
|
||||
self.isSelect = self.isSelection()
|
||||
# Default width, height and maxChars vary according to String format
|
||||
if width == None:
|
||||
|
@ -1374,12 +1394,13 @@ class Boolean(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=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,
|
||||
editDefault, show, page, group, layouts, move, indexed,
|
||||
searchable, specificReadPermission,
|
||||
specificWritePermission, width, height, None, colspan,
|
||||
master, masterValue, focus, historized, True, mapping)
|
||||
master, masterValue, focus, historized, True, mapping,
|
||||
label)
|
||||
self.pythonType = bool
|
||||
|
||||
def getDefaultLayouts(self):
|
||||
|
@ -1417,7 +1438,7 @@ class Date(Type):
|
|||
indexed=False, searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=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.calendar = calendar
|
||||
self.startYear = startYear
|
||||
|
@ -1429,7 +1450,8 @@ class Date(Type):
|
|||
editDefault, show, page, group, layouts, move, indexed,
|
||||
searchable, specificReadPermission,
|
||||
specificWritePermission, width, height, None, colspan,
|
||||
master, masterValue, focus, historized, True, mapping)
|
||||
master, masterValue, focus, historized, True, mapping,
|
||||
label)
|
||||
|
||||
def getCss(self, layoutType):
|
||||
if (layoutType == 'edit') and self.calendar:
|
||||
|
@ -1490,13 +1512,14 @@ class File(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=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
|
||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
||||
editDefault, show, page, group, layouts, move, indexed,
|
||||
False, specificReadPermission, specificWritePermission,
|
||||
width, height, None, colspan, master, masterValue, focus,
|
||||
historized, True, mapping)
|
||||
historized, True, mapping, label)
|
||||
|
||||
@staticmethod
|
||||
def getFileObject(filePath, fileName=None, zope=False):
|
||||
|
@ -1640,8 +1663,8 @@ class Ref(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=5,
|
||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||
focus=False, historized=False, mapping=None, queryable=False,
|
||||
queryFields=None, queryNbCols=1):
|
||||
focus=False, historized=False, mapping=None, label=None,
|
||||
queryable=False, queryFields=None, queryNbCols=1):
|
||||
self.klass = klass
|
||||
self.attribute = attribute
|
||||
# May the user add new objects through this ref ?
|
||||
|
@ -1656,6 +1679,7 @@ class Ref(Type):
|
|||
self.link = link
|
||||
# May the user unlink existing objects?
|
||||
self.unlink = unlink
|
||||
self.back = None
|
||||
if back:
|
||||
# It is a forward reference
|
||||
self.isBack = False
|
||||
|
@ -1691,7 +1715,7 @@ class Ref(Type):
|
|||
editDefault, show, page, group, layouts, move, indexed,
|
||||
False, specificReadPermission, specificWritePermission,
|
||||
width, height, None, colspan, master, masterValue, focus,
|
||||
historized, sync, mapping)
|
||||
historized, sync, mapping, label)
|
||||
self.validable = self.link
|
||||
|
||||
def getDefaultLayouts(self): return {'view': Table('l-f'), 'edit': 'lrv-f'}
|
||||
|
@ -1858,7 +1882,7 @@ class Computed(Type):
|
|||
specificWritePermission=False, width=None, height=None,
|
||||
maxChars=None, colspan=1, method=None, plainText=True,
|
||||
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
|
||||
self.method = method
|
||||
# Does field computation produce plain text or XHTML?
|
||||
|
@ -1875,7 +1899,7 @@ class Computed(Type):
|
|||
False, show, page, group, layouts, move, indexed, False,
|
||||
specificReadPermission, specificWritePermission, width,
|
||||
height, None, colspan, master, masterValue, focus,
|
||||
historized, sync, mapping)
|
||||
historized, sync, mapping, label)
|
||||
self.validable = False
|
||||
|
||||
def callMacro(self, obj, macroPath):
|
||||
|
@ -1925,7 +1949,7 @@ class Action(Type):
|
|||
specificWritePermission=False, width=None, height=None,
|
||||
maxChars=None, colspan=1, action=None, result='computation',
|
||||
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
|
||||
self.action = action
|
||||
# For the 'result' param:
|
||||
|
@ -1946,7 +1970,7 @@ class Action(Type):
|
|||
False, show, page, group, layouts, move, indexed, False,
|
||||
specificReadPermission, specificWritePermission, width,
|
||||
height, None, colspan, master, masterValue, focus,
|
||||
historized, False, mapping)
|
||||
historized, False, mapping, label)
|
||||
self.validable = False
|
||||
|
||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||
|
@ -1994,12 +2018,12 @@ class Info(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=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,
|
||||
False, show, page, group, layouts, move, indexed, False,
|
||||
specificReadPermission, specificWritePermission, width,
|
||||
height, None, colspan, master, masterValue, focus,
|
||||
historized, False, mapping)
|
||||
historized, False, mapping, label)
|
||||
self.validable = False
|
||||
|
||||
class Pod(Type):
|
||||
|
@ -2015,9 +2039,9 @@ class Pod(Type):
|
|||
searchable=False, specificReadPermission=False,
|
||||
specificWritePermission=False, width=None, height=None,
|
||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||
focus=False, historized=False, mapping=None, template=None,
|
||||
context=None, action=None, askAction=False, stylesMapping={},
|
||||
freezeFormat='pdf'):
|
||||
focus=False, historized=False, mapping=None, label=None,
|
||||
template=None, context=None, action=None, askAction=False,
|
||||
stylesMapping={}, freezeFormat='pdf'):
|
||||
# The following param stores the path to a POD template
|
||||
self.template = template
|
||||
# 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,
|
||||
searchable, specificReadPermission,
|
||||
specificWritePermission, width, height, None, colspan,
|
||||
master, masterValue, focus, historized, False, mapping)
|
||||
master, masterValue, focus, historized, False, mapping,
|
||||
label)
|
||||
self.validable = False
|
||||
|
||||
def isFrozen(self, obj):
|
||||
|
|
|
@ -147,17 +147,18 @@ class FieldDescriptor:
|
|||
(self.fieldName not in ('title', 'description')):
|
||||
self.classDescr.addIndexMethod(self)
|
||||
# i18n labels
|
||||
i18nPrefix = "%s_%s" % (self.classDescr.name, self.fieldName)
|
||||
# Create labels for generating them in i18n files.
|
||||
messages = self.generator.labels
|
||||
if self.appyType.hasLabel:
|
||||
messages.append(self.produceMessage(i18nPrefix))
|
||||
if self.appyType.hasDescr:
|
||||
descrId = i18nPrefix + '_descr'
|
||||
messages.append(self.produceMessage(descrId,isLabel=False))
|
||||
if self.appyType.hasHelp:
|
||||
helpId = i18nPrefix + '_help'
|
||||
messages.append(self.produceMessage(helpId, isLabel=False))
|
||||
if not self.appyType.label:
|
||||
# Create labels for generating them in i18n files, only if required.
|
||||
i18nPrefix = "%s_%s" % (self.classDescr.name, self.fieldName)
|
||||
if self.appyType.hasLabel:
|
||||
messages.append(self.produceMessage(i18nPrefix))
|
||||
if self.appyType.hasDescr:
|
||||
descrId = i18nPrefix + '_descr'
|
||||
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
|
||||
# than one page/phase for the class.
|
||||
ppMsgs = []
|
||||
|
@ -561,8 +562,8 @@ class TranslationClassDescriptor(ClassDescriptor):
|
|||
def addLabelField(self, messageId, page):
|
||||
'''Adds a Computed field that will display, in the source language, the
|
||||
content of the text to translate.'''
|
||||
field = Computed(method=self.modelClass.computeLabel, plainText=False,
|
||||
page=page, show=self.modelClass.showField, layouts='f')
|
||||
field = Computed(method=self.modelClass.label, plainText=False,
|
||||
page=page, show=self.modelClass.show, layouts='f')
|
||||
self.addField('%s_label' % messageId, field)
|
||||
|
||||
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
|
||||
fine-tuning the String type to generate for this field (one-line?
|
||||
several lines?...)'''
|
||||
params = {'page':page, 'layouts':'f', 'show':self.modelClass.showField}
|
||||
params = {'page':page, 'layouts':'f', 'show': self.modelClass.show}
|
||||
appName = self.generator.applicationName
|
||||
# Scan all messages corresponding to p_messageId from all translation
|
||||
# files. We will define field length from the longer found message
|
||||
|
|
|
@ -590,9 +590,8 @@ class Generator(AbstractGenerator):
|
|||
'''Generates the Plone tool that corresponds to this application.'''
|
||||
Msg = PoMessage
|
||||
# Create Tool-related i18n-related messages
|
||||
self.labels += [
|
||||
Msg(self.tool.name, '', Msg.CONFIG % self.applicationName),
|
||||
Msg('%s_edit_descr' % self.tool.name, '', ' ')]
|
||||
msg = Msg(self.tool.name, '', Msg.CONFIG % self.applicationName)
|
||||
self.labels.append(msg)
|
||||
|
||||
# Tune the Ref field between Tool and User
|
||||
Tool.users.klass = User
|
||||
|
@ -604,7 +603,6 @@ class Generator(AbstractGenerator):
|
|||
klassType = klass.name[len(self.applicationName):]
|
||||
klass.generateSchema()
|
||||
self.labels += [ Msg(klass.name, '', klassType),
|
||||
Msg('%s_edit_descr' % klass.name, '', ' '),
|
||||
Msg('%s_plural' % klass.name,'', klass.name+'s')]
|
||||
repls = self.repls.copy()
|
||||
repls.update({'fields': klass.schema, 'methods': klass.methods,
|
||||
|
@ -702,12 +700,10 @@ class Generator(AbstractGenerator):
|
|||
'implements': implements, 'baseSchema': baseSchema, 'static': '',
|
||||
'register': register, 'toolInstanceName': self.toolInstanceName})
|
||||
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.produceNiceDefault()
|
||||
self.labels.append(poMsg)
|
||||
poMsgDescr = PoMessage('%s_edit_descr' % classDescr.name, '', ' ')
|
||||
self.labels.append(poMsgDescr)
|
||||
poMsgPl = PoMessage('%s_plural' % classDescr.name, '',
|
||||
classDescr.klass.__name__+'s')
|
||||
poMsgPl.produceNiceDefault()
|
||||
|
|
|
@ -9,6 +9,33 @@
|
|||
import types
|
||||
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:
|
||||
'''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
|
||||
application model.'''
|
||||
_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
|
||||
def _appy_getTypeBody(klass, appyType):
|
||||
def _appy_getTypeBody(klass, appyType, wrapperName):
|
||||
'''This method returns the code declaration for p_appyType.'''
|
||||
typeArgs = ''
|
||||
for attrName, attrValue in appyType.__dict__.iteritems():
|
||||
if attrName in ModelClass._appy_notinit: continue
|
||||
if attrName == 'layouts':
|
||||
if klass.__name__ == 'Tool': continue
|
||||
proto = Protos.get(appyType)
|
||||
for name, value in appyType.__dict__.iteritems():
|
||||
# Some attrs can't be given to the constructor
|
||||
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
|
||||
# fields added to the Tool are config-related attributes whose
|
||||
# layouts must be standard.
|
||||
attrValue = appyType.getInputLayouts()
|
||||
elif isinstance(attrValue, basestring):
|
||||
attrValue = '"%s"' % attrValue
|
||||
elif isinstance(attrValue, Ref):
|
||||
if not attrValue.isBack: continue
|
||||
attrValue = klass._appy_getTypeBody(attrValue)
|
||||
elif type(attrValue) == type(ModelClass):
|
||||
moduleName = attrValue.__module__
|
||||
if moduleName.startswith('appy.gen'):
|
||||
attrValue = attrValue.__name__
|
||||
if klass.__name__ == 'Tool': continue
|
||||
layouts = appyType.getInputLayouts()
|
||||
# For the Translation class that has potentially thousands of
|
||||
# attributes, the most used layout is cached in a global var in
|
||||
# named "tfw" in appyWrappers.py.
|
||||
if (klass.__name__ == 'Translation') and \
|
||||
(layouts == '{"edit":"f","cell":"f","view":"f",}'):
|
||||
value = 'tfw'
|
||||
else:
|
||||
attrValue = '%s.%s' % (moduleName, attrValue.__name__)
|
||||
elif isinstance(attrValue, Selection):
|
||||
attrValue = 'Selection("%s")' % attrValue.methodName
|
||||
elif isinstance(attrValue, Group):
|
||||
attrValue = 'Group("%s")' % attrValue.name
|
||||
elif isinstance(attrValue, Page):
|
||||
attrValue = 'pages["%s"]' % attrValue.name
|
||||
elif callable(attrValue):
|
||||
attrValue = '%sWrapper.%s'% (klass.__name__, attrValue.__name__)
|
||||
typeArgs += '%s=%s,' % (attrName, attrValue)
|
||||
value = appyType.getInputLayouts()
|
||||
elif isinstance(value, basestring):
|
||||
value = '"%s"' % value
|
||||
elif isinstance(value, Ref):
|
||||
if not value.isBack: continue
|
||||
value = klass._appy_getTypeBody(value, wrapperName)
|
||||
elif type(value) == type(ModelClass):
|
||||
moduleName = value.__module__
|
||||
if moduleName.startswith('appy.gen'):
|
||||
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)
|
||||
|
||||
@classmethod
|
||||
def _appy_getBody(klass):
|
||||
'''This method returns the code declaration of this class. We will dump
|
||||
this in appyWrappers.py in the resulting product.'''
|
||||
res = 'class %s(%sWrapper):\n' % (klass.__name__, klass.__name__)
|
||||
if klass.__name__ == 'Tool':
|
||||
res += ' folder=True\n'
|
||||
className = klass.__name__
|
||||
# Determine the name of the class and its wrapper. Because so much
|
||||
# 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
|
||||
# dict with it. It will prevent us from creating a new Page instance
|
||||
# for every field.
|
||||
pages = {}
|
||||
for attrName in klass._appy_attributes:
|
||||
exec 'appyType = klass.%s' % attrName
|
||||
layouts = []
|
||||
for name in klass._appy_attributes:
|
||||
exec 'appyType = klass.%s' % name
|
||||
if appyType.page.name not in pages:
|
||||
pages[appyType.page.name] = appyType.page
|
||||
res += ' pages = {'
|
||||
|
@ -81,9 +121,10 @@ class ModelClass:
|
|||
res += '"%s":Page("%s", show=%s),'% (page.name, page.name, pageShow)
|
||||
res += '}\n'
|
||||
# Secondly, dump every attribute
|
||||
for attrName in klass._appy_attributes:
|
||||
exec 'appyType = klass.%s' % attrName
|
||||
res += ' %s=%s\n' % (attrName, klass._appy_getTypeBody(appyType))
|
||||
for name in klass._appy_attributes:
|
||||
exec 'appyType = klass.%s' % name
|
||||
typeBody = klass._appy_getTypeBody(appyType, wrapperName)
|
||||
res += ' %s=%s\n' % (name, typeBody)
|
||||
return res
|
||||
|
||||
# The User class ---------------------------------------------------------------
|
||||
|
@ -115,8 +156,8 @@ class Translation(ModelClass):
|
|||
po = Action(action=getPoFile, page=Page('actions', show='view'),
|
||||
result='filetmp')
|
||||
title = String(show=False, indexed=True)
|
||||
def computeLabel(self): pass
|
||||
def showField(self, name): pass
|
||||
def label(self): pass
|
||||
def show(self, name): pass
|
||||
|
||||
# The Tool class ---------------------------------------------------------------
|
||||
# Here are the prefixes of the fields generated on the Tool.
|
||||
|
|
|
@ -564,11 +564,6 @@
|
|||
<b class="appyTitle" tal:content="contextObj/title_or_id"></b>
|
||||
</td>
|
||||
</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">
|
||||
<td class="documentByLine" colspan="2">
|
||||
<tal:comment replace="nothing">Creator and last modification date</tal:comment>
|
||||
|
|
|
@ -27,7 +27,7 @@ class <!genClassName!>(<!parents!>):
|
|||
default_view = 'skyn/view'
|
||||
suppl_views = ()
|
||||
typeDescription = '<!genClassName!>'
|
||||
typeDescMsgId = '<!genClassName!>_edit_descr'
|
||||
typeDescMsgId = '<!genClassName!>'
|
||||
i18nDomain = '<!applicationName!>'
|
||||
wrapperClass = <!genClassName!>_Wrapper
|
||||
schema = fullSchema
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.gen import *
|
||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper
|
||||
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper
|
||||
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper
|
||||
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper as WTool
|
||||
from appy.gen.plone25.wrappers.UserWrapper import UserWrapper as WUser
|
||||
from appy.gen.plone25.wrappers.TranslationWrapper import TranslationWrapper as WT
|
||||
from Globals import InitializeClass
|
||||
from AccessControl import ClassSecurityInfo
|
||||
tfw = {"edit":"f","cell":"f","view":"f"} # Layout for Translation fields
|
||||
<!imports!>
|
||||
|
||||
<!User!>
|
||||
|
|
|
@ -6,7 +6,7 @@ from appy.shared.utils import getOsTempFolder
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
class TranslationWrapper(AbstractWrapper):
|
||||
def computeLabel(self, field):
|
||||
def label(self, field):
|
||||
'''The label for a text to translate displays the text of the
|
||||
corresponding message in the source translation.'''
|
||||
tool = self.tool
|
||||
|
@ -32,7 +32,7 @@ class TranslationWrapper(AbstractWrapper):
|
|||
'<img src="help.png"/></acronym>%s</div>' % \
|
||||
(fieldName, sourceMsg)
|
||||
|
||||
def showField(self, field):
|
||||
def show(self, field):
|
||||
'''We show a field (or its label) only if the corresponding source
|
||||
message is not empty.'''
|
||||
tool = self.tool
|
||||
|
|
Loading…
Reference in a new issue