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
					
				
					 8 changed files with 162 additions and 103 deletions
				
			
		| 
						 | 
				
			
			@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue