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,
|
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
|
||||||
|
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
|
if not klass: prefix = appName
|
||||||
else: prefix = getClassName(klass, appName)
|
else: prefix = getClassName(klass, appName)
|
||||||
self.labelId = '%s_%s' % (prefix, name)
|
# 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):
|
||||||
|
|
|
@ -147,9 +147,10 @@ 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 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:
|
if self.appyType.hasLabel:
|
||||||
messages.append(self.produceMessage(i18nPrefix))
|
messages.append(self.produceMessage(i18nPrefix))
|
||||||
if self.appyType.hasDescr:
|
if self.appyType.hasDescr:
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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!>
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue