diff --git a/gen/__init__.py b/gen/__init__.py index 2a8ad87..acffd86 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -675,7 +675,7 @@ class Type: '''p_value is a real p_obj(ect) value from a field from this type. This method returns a pretty, string-formatted version, for displaying purposes. Needs to be overridden by some child classes.''' - if value in nullValues: return '' + if self.isEmptyValue(value): return '' return value def getRequestValue(self, request): @@ -689,7 +689,7 @@ class Type: representation of the field value coming from the request. This method computes the real (potentially converted or manipulated in some other way) value as can be stored in the database.''' - if value in nullValues: return None + if self.isEmptyValue(value): return None return value def getMasterData(self): @@ -698,6 +698,10 @@ class Type: if self.master: return (self.master, self.masterValue) if self.group: return self.group.getMasterData() + def isEmptyValue(self, value, obj=None): + '''Returns True if the p_value must be considered as an empty value.''' + return value in nullValues + def validateValue(self, obj, value): '''This method may be overridden by child classes and will be called at the right moment by m_validate defined below for triggering @@ -711,7 +715,7 @@ class Type: definition. If it is the case, None is returned. Else, a translated error message is returned.''' # Check that a value is given if required. - if value in nullValues: + if self.isEmptyValue(value, obj): if self.required and self.isClientVisible(obj): # If the field is required, but not visible according to # master/slave relationships, we consider it not to be required. @@ -759,6 +763,28 @@ class Type: p_self type definition on p_obj.''' setattr(obj, self.name, value) + def clone(self, forTool=True): + '''Returns a clone of myself. If p_forTool is True, the clone will be + adapted to its life into the tool.''' + res = copy.copy(self) + res.group = copy.copy(self.group) + res.page = copy.copy(self.page) + if not forTool: return res + # A field added to the tool can't have parameters that would lead to the + # creation of new fields in the tool. + res.editDefault = False + res.optional = False + res.show = True + # Set default layouts for all Tool fields + res.layouts = res.formatLayouts(None) + res.specificReadPermission = False + res.specificWritePermission = False + res.multiplicity = (0, self.multiplicity[1]) + if type(res.validator) == types.FunctionType: + # We will not be able to call this function from the tool. + res.validator = None + return res + class Integer(Type): def __init__(self, validator=None, multiplicity=(0,1), index=None, default=None, optional=False, editDefault=False, show=True, @@ -781,7 +807,11 @@ class Integer(Type): return obj.translate('bad_%s' % self.pythonType.__name__) def getStorableValue(self, value): - if value not in nullValues: return self.pythonType(value) + if not self.isEmptyValue(value): return self.pythonType(value) + + def getFormattedValue(self, obj, value): + if self.isEmptyValue(value): return '' + return str(value) class Float(Type): allowedDecimalSeps = (',', '.') @@ -814,7 +844,7 @@ class Float(Type): self.pythonType = float def getFormattedValue(self, obj, value): - if value in nullValues: return '' + if self.isEmptyValue(value): return '' # Determine the field separator sep = self.sep[0] # Produce the rounded string representation @@ -847,7 +877,7 @@ class Float(Type): return obj.translate('bad_%s' % self.pythonType.__name__) def getStorableValue(self, value): - if value not in nullValues: + if not self.isEmptyValue(value): for sep in self.sep: value = value.replace(sep, '.') return self.pythonType(value) @@ -1021,7 +1051,7 @@ class String(Type): return value def getFormattedValue(self, obj, value): - if value in nullValues: return '' + if self.isEmptyValue(value): return '' res = value if self.isSelect: if isinstance(self.validator, Selection): @@ -1148,7 +1178,7 @@ class Boolean(Type): return res def getStorableValue(self, value): - if value not in nullValues: + if not self.isEmptyValue(value): exec 'res = %s' % value return res @@ -1187,7 +1217,7 @@ class Date(Type): 'jscalendar/calendar-en.js') def getFormattedValue(self, obj, value): - if value in nullValues: return '' + if self.isEmptyValue(value): return '' res = value.strftime('%d/%m/') + str(value.year()) if self.format == Date.WITH_HOUR: res += ' %s' % value.strftime('%H:%M') @@ -1212,7 +1242,7 @@ class Date(Type): return value def getStorableValue(self, value): - if value not in nullValues: + if not self.isEmptyValue(value): import DateTime return DateTime.DateTime(value) @@ -1246,13 +1276,21 @@ class File(Type): def getDefaultLayouts(self): return {'view':'lf','edit':'lrv-f'} + def isEmptyValue(self, value, obj=None): + '''Must p_value be considered as empty?''' + if not obj: return Type.isEmptyValue(self, value) + if value is not None: return False + # If "nochange", the value must not be considered as empty + return obj.REQUEST.get('%s_delete' % self.name) != 'nochange' + imageExts = ('.jpg', '.jpeg', '.png', '.gif') def validateValue(self, obj, value): form = obj.REQUEST.form action = '%s_delete' % self.name - if not value.filename and form.has_key(action) and not form[action]: + if (not value or not value.filename) and form.has_key(action) and \ + not form[action]: # If this key is present but empty, it means that the user selected - # "replace the file with a new one". So in this cas he must provide + # "replace the file with a new one". So in this case he must provide # a new file to upload. return obj.translate('file_required') # Check that, if self.isImage, the uploaded file is really an image @@ -1345,7 +1383,7 @@ class Ref(Type): historized, sync) self.validable = self.link - def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'} + def getDefaultLayouts(self): return {'view': Table('l-f'), 'edit': 'lrv-f'} def isShowable(self, obj, layoutType): res = Type.isShowable(self, obj, layoutType) @@ -1470,6 +1508,18 @@ class Ref(Type): refs = [obj.uid_catalog(UID=uid)[0].getObject() for uid in uids] exec 'obj.set%s%s(refs)' % (self.name[0].upper(), self.name[1:]) + def clone(self, forTool=True): + '''Produces a clone of myself.''' + res = Type.clone(self, forTool) + res.back = copy.copy(self.back) + if not forTool: return res + res.link = True + res.add = False + res.back.attribute += 'DefaultValue' + res.back.show = False + res.select = None # Not callable from tool. + return res + class Computed(Type): def __init__(self, validator=None, multiplicity=(0,1), index=None, default=None, optional=False, editDefault=False, show='view', @@ -1491,18 +1541,17 @@ class Computed(Type): def getValue(self, obj): '''Computes the value instead of getting it in the database.''' - if not self.method: return '' + if not self.method: return obj = obj.appy() try: - res = self.method(obj) - if not isinstance(res, basestring): - res = repr(res) + return self.method(obj) except Exception, e: obj.log(Traceback.get(), type='error') - res = str(e) - return res + return str(e) - def getFormattedValue(self, obj, value): return value + def getFormattedValue(self, obj, value): + if not isinstance(value, basestring): return str(value) + return value class Action(Type): '''An action is a workflow-independent Python method that can be triggered diff --git a/gen/layout.py b/gen/layout.py index 21738fe..6b32d91 100644 --- a/gen/layout.py +++ b/gen/layout.py @@ -40,7 +40,10 @@ # ------------------------------------------------------------------------------ rowDelimiters = {'-':'middle', '=':'top', '_':'bottom'} +rowDelms = ''.join(rowDelimiters.keys()) cellDelimiters = {'|': 'center', ';': 'left', '!': 'right'} +cellDelms = ''.join(cellDelimiters.keys()) + macroDict = { # Page-related elements 's': ('page', 'header'), 'w': ('page', 'widgets'), @@ -158,6 +161,10 @@ class Table(LayoutElement): res = layout for letter in Table.derivedRepls[derivedType]: res = res.replace(letter, '') + # Strip the derived layout + res = res.lstrip(rowDelms); res = res.lstrip(cellDelms) + if derivedType == 'cell': + res = res.rstrip(rowDelms); res = res.rstrip(cellDelms) return res def addCssClasses(self, css_class): diff --git a/gen/plone25/descriptors.py b/gen/plone25/descriptors.py index 7d9a1f6..4ed4794 100644 --- a/gen/plone25/descriptors.py +++ b/gen/plone25/descriptors.py @@ -6,13 +6,12 @@ # ------------------------------------------------------------------------------ import types, copy -from model import ModelClass, Tool, toolFieldPrefixes +from model import ModelClass, toolFieldPrefixes from utils import stringify import appy.gen import appy.gen.descriptors from appy.gen.po import PoMessage -from appy.gen import Date, String, State, Transition, Type, Search, \ - Selection, Import, Role +from appy.gen import * from appy.gen.utils import produceNiceMessage, getClassName TABS = 4 # Number of blanks in a Python indentation. @@ -131,7 +130,7 @@ class FieldDescriptor: self.generator.labels.append(msg) self.classDescr.labelsToPropagate.append(msg) # Add the POD-related fields on the Tool - Tool._appy_addPodRelatedFields(self) + self.generator.tool.addPodRelatedFields(self) notToValidateFields = ('Info', 'Computed', 'Action', 'Pod') def walkAppyType(self): @@ -140,10 +139,10 @@ class FieldDescriptor: # Manage things common to all Appy types # - optional ? if self.appyType.optional: - Tool._appy_addOptionalField(self) + self.generator.tool.addOptionalField(self) # - edit default value ? if self.appyType.editDefault: - Tool._appy_addDefaultField(self) + self.generator.tool.addDefaultField(self) # - put an index on this field? if self.appyType.indexed and \ (self.fieldName not in ('title', 'description')): @@ -381,11 +380,13 @@ class ToolClassDescriptor(ClassDescriptor): self.modelClass = self.klass self.predefined = True self.customized = False + def getParents(self, allClasses=()): res = ['Tool'] if self.customized: res.append('%s.%s' % (self.klass.__module__, self.klass.__name__)) return res + def update(self, klass, attributes): '''This method is called by the generator when he finds a custom tool definition. We must then add the custom tool elements in this default @@ -393,11 +394,133 @@ class ToolClassDescriptor(ClassDescriptor): self.orderedAttributes += attributes self.klass = klass self.customized = True + def isFolder(self, klass=None): return True def isRoot(self): return False def generateSchema(self): ClassDescriptor.generateSchema(self, configClass=True) + def addField(self, fieldName, fieldType, classDescr): + '''Adds a new field to the Tool.''' + exec "self.modelClass.%s = fieldType" % fieldName + self.modelClass._appy_attributes.append(fieldName) + self.orderedAttributes.append(fieldName) + self.modelClass._appy_classes[fieldName] = classDescr.name + + def addOptionalField(self, fieldDescr): + className = fieldDescr.classDescr.name + fieldName = 'optionalFieldsFor%s' % className + fieldType = getattr(self.modelClass, fieldName, None) + if not fieldType: + fieldType = String(multiplicity=(0,None)) + fieldType.validator = [] + self.addField(fieldName, fieldType, fieldDescr.classDescr) + fieldType.validator.append(fieldDescr.fieldName) + fieldType.page.name = 'data' + fieldType.group = Group(fieldDescr.classDescr.klass.__name__) + + def addDefaultField(self, fieldDescr): + className = fieldDescr.classDescr.name + fieldName = 'defaultValueFor%s_%s' % (className, fieldDescr.fieldName) + fieldType = fieldDescr.appyType.clone() + self.addField(fieldName, fieldType, fieldDescr.classDescr) + fieldType.page.name = 'data' + fieldType.group = Group(fieldDescr.classDescr.klass.__name__) + + def addPodRelatedFields(self, fieldDescr): + '''Adds the fields needed in the Tool for configuring a Pod field.''' + className = fieldDescr.classDescr.name + # On what page and group to display those fields ? + pg = {'page': 'documentGeneration', + 'group': Group(fieldDescr.classDescr.klass.__name__, ['50%']*2)} + # Add the field that will store the pod template. + fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName) + fieldType = File(**pg) + self.addField(fieldName, fieldType, fieldDescr.classDescr) + # Add the field that will store the output format(s) + fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName) + fieldType = String(validator=('odt', 'pdf', 'doc', 'rtf'), + multiplicity=(1,None), default=('odt',), **pg) + self.addField(fieldName, fieldType, fieldDescr.classDescr) + + def addQueryResultColumns(self, classDescr): + '''Adds, for class p_classDescr, the attribute in the tool that allows + to select what default columns will be shown on query results.''' + className = classDescr.name + fieldName = 'resultColumnsFor%s' % className + fieldType = String(multiplicity=(0,None), validator=Selection( + '_appy_getAllFields*%s' % className), page='userInterface', + group=classDescr.klass.__name__) + self.addField(fieldName, fieldType, classDescr) + + def addSearchRelatedFields(self, classDescr): + '''Adds, for class p_classDescr, attributes related to the search + functionality for class p_classDescr.''' + className = classDescr.name + # Field that defines if advanced search is enabled for class + # p_classDescr or not. + fieldName = 'enableAdvancedSearchFor%s' % className + fieldType = Boolean(default=True, page='userInterface', + group=classDescr.klass.__name__) + self.addField(fieldName, fieldType, classDescr) + # Field that defines how many columns are shown on the custom search + # screen. + fieldName = 'numberOfSearchColumnsFor%s' % className + fieldType = Integer(default=3, page='userInterface', + group=classDescr.klass.__name__) + self.addField(fieldName, fieldType, classDescr) + # Field that allows to select, among all indexed fields, what fields + # must really be used in the search screen. + fieldName = 'searchFieldsFor%s' % className + defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes( + condition='attrValue.indexed')] + fieldType = String(multiplicity=(0,None), validator=Selection( + '_appy_getSearchableFields*%s' % className), default=defaultValue, + page='userInterface', group=classDescr.klass.__name__) + self.addField(fieldName, fieldType, classDescr) + + def addImportRelatedFields(self, classDescr): + '''Adds, for class p_classDescr, attributes related to the import + functionality for class p_classDescr.''' + className = classDescr.name + # Field that defines the path of the files to import. + fieldName = 'importPathFor%s' % className + defValue = classDescr.getCreateMean('Import').path + fieldType = String(page='data', multiplicity=(1,1), default=defValue, + group=classDescr.klass.__name__) + self.addField(fieldName, fieldType, classDescr) + + def addWorkflowFields(self, classDescr): + '''Adds, for a given p_classDescr, the workflow-related fields.''' + className = classDescr.name + groupName = classDescr.klass.__name__ + # Adds a field allowing to show/hide completely any workflow-related + # information for a given class. + defaultValue = False + if classDescr.isRoot() or issubclass(classDescr.klass, ModelClass): + defaultValue = True + fieldName = 'showWorkflowFor%s' % className + fieldType = Boolean(default=defaultValue, page='userInterface', + group=groupName) + self.addField(fieldName, fieldType, classDescr) + # Adds the boolean field for showing or not the field "enter comments". + fieldName = 'showWorkflowCommentFieldFor%s' % className + fieldType = Boolean(default=defaultValue, page='userInterface', + group=groupName) + self.addField(fieldName, fieldType, classDescr) + # Adds the boolean field for showing all states in current state or not. + # If this boolean is True but the current phase counts only one state, + # we will not show the state at all: the fact of knowing in what phase + # we are is sufficient. If this boolean is False, we simply show the + # current state. + defaultValue = False + if len(classDescr.getPhases()) > 1: + defaultValue = True + fieldName = 'showAllStatesInPhaseFor%s' % className + fieldType = Boolean(default=defaultValue, page='userInterface', + group=groupName) + self.addField(fieldName, fieldType, classDescr) + class UserClassDescriptor(ClassDescriptor): '''Represents an Archetypes-compliant class that corresponds to the User for the generated application.''' diff --git a/gen/plone25/generator.py b/gen/plone25/generator.py index 9cd2802..b965df2 100644 --- a/gen/plone25/generator.py +++ b/gen/plone25/generator.py @@ -8,8 +8,9 @@ from appy.gen import * from appy.gen.po import PoMessage, PoFile, PoParser from appy.gen.generator import Generator as AbstractGenerator from appy.gen.utils import getClassName +from descriptors import ClassDescriptor, WorkflowDescriptor, \ + ToolClassDescriptor, UserClassDescriptor from model import ModelClass, User, Tool -from descriptors import * # Common methods that need to be defined on every Archetype class -------------- COMMON_METHODS = ''' @@ -352,8 +353,8 @@ class Generator(AbstractGenerator): wfInit += 'wf._states.append("%s")\n' % stateName wfInit += 'workflowInstances[%s] = wf\n' % className repls['workflowInstancesInit'] = wfInit - # Compute the list of ordered attributes (foward and backward, inherited - # included) for every Appy class. + # Compute the list of ordered attributes (forward and backward, + # inherited included) for every Appy class. attributes = [] attributesDict = [] for classDescr in self.getClasses(include='all'): @@ -622,21 +623,16 @@ class Generator(AbstractGenerator): for childDescr in classDescr.getChildren(): childFieldName = fieldName % childDescr.name fieldType.group = childDescr.klass.__name__ - Tool._appy_addField(childFieldName, fieldType, childDescr) + self.tool.addField(childFieldName, fieldType, childDescr) if classDescr.isRoot(): # We must be able to configure query results from the tool. - Tool._appy_addQueryResultColumns(classDescr) + self.tool.addQueryResultColumns(classDescr) # Add the search-related fields. - Tool._appy_addSearchRelatedFields(classDescr) + self.tool.addSearchRelatedFields(classDescr) importMean = classDescr.getCreateMean('Import') if importMean: - Tool._appy_addImportRelatedFields(classDescr) - Tool._appy_addWorkflowFields(self.user) - # Complete self.tool.orderedAttributes from the attributes that we - # just added to the Tool model class. - for fieldName in Tool._appy_attributes: - if fieldName not in self.tool.orderedAttributes: - self.tool.orderedAttributes.append(fieldName) + self.tool.addImportRelatedFields(classDescr) + self.tool.addWorkflowFields(self.user) self.tool.generateSchema() # Generate the Tool class @@ -663,7 +659,7 @@ class Generator(AbstractGenerator): k = classDescr.klass print 'Generating %s.%s (gen-class)...' % (k.__module__, k.__name__) if not classDescr.isAbstract(): - Tool._appy_addWorkflowFields(classDescr) + self.tool.addWorkflowFields(classDescr) # Determine base archetypes schema and class baseClass = 'BaseContent' baseSchema = 'BaseSchema' diff --git a/gen/plone25/mixins/ToolMixin.py b/gen/plone25/mixins/ToolMixin.py index 958d2a7..0f123a8 100644 --- a/gen/plone25/mixins/ToolMixin.py +++ b/gen/plone25/mixins/ToolMixin.py @@ -180,7 +180,8 @@ class ToolMixin(BaseMixin): sortMethod = importParams['sort'] if sortMethod: sortMethod = sortMethod.__get__('') elems = [] - importPath = getattr(self, 'importPathFor%s' % contentType) + importType = self.getAppyType('importPathFor%s' % contentType) + importPath = importType.getValue(self) for elem in os.listdir(importPath): elemFullPath = os.path.join(importPath, elem) elemInfo = onElement(elemFullPath) diff --git a/gen/plone25/mixins/__init__.py b/gen/plone25/mixins/__init__.py index dac6adb..4fc5a93 100644 --- a/gen/plone25/mixins/__init__.py +++ b/gen/plone25/mixins/__init__.py @@ -919,9 +919,14 @@ class BaseMixin: '''Translates a given p_label into p_domain with p_mapping.''' cfg = self.getProductConfig() if not domain: domain = cfg.PROJECTNAME - return self.Control_Panel.TranslationService.utranslate( - domain, label, mapping, self, default=default, - target_language=language) + try: + res = self.Control_Panel.TranslationService.utranslate( + domain, label, mapping, self, default=default, + target_language=language) + except AttributeError: + # When run in test mode, Zope does not create the TranslationService + res = label + return res def getPageLayout(self, layoutType): '''Returns the layout corresponding to p_layoutType for p_self.''' diff --git a/gen/plone25/model.py b/gen/plone25/model.py index 8b59967..fbefdc4 100644 --- a/gen/plone25/model.py +++ b/gen/plone25/model.py @@ -6,7 +6,7 @@ Plone (content types, catalogs, workflows, etc.)''' # ------------------------------------------------------------------------------ -import copy, types +import types from appy.gen import * # ------------------------------------------------------------------------------ @@ -24,13 +24,6 @@ class ModelClass: 'layouts', 'required', 'filterable', 'validable', 'backd', 'isBack', 'sync', 'pageName') - @classmethod - def _appy_addField(klass, fieldName, fieldType, classDescr): - exec "klass.%s = fieldType" % fieldName - klass._appy_attributes.append(fieldName) - if hasattr(klass, '_appy_classes'): - klass._appy_classes[fieldName] = classDescr.name - @classmethod def _appy_getTypeBody(klass, appyType): '''This method returns the code declaration for p_appyType.''' @@ -137,155 +130,4 @@ class Tool(ModelClass): exec 'del klass.%s' % k klass._appy_attributes = list(defaultToolFields) klass._appy_classes = {} - - @classmethod - def _appy_copyField(klass, appyType): - '''From a given p_appyType, produce a type definition suitable for - storing the default value for this field.''' - res = copy.copy(appyType) - # A field added to the tool can't have parameters that would lead to the - # creation of new fields in the tool. - res.editDefault = False - res.optional = False - res.show = True - res.group = copy.copy(appyType.group) - res.page = copy.copy(appyType.page) - # Set default layouts for all Tool fields - res.layouts = res.formatLayouts(None) - res.specificReadPermission = False - res.specificWritePermission = False - res.multiplicity = (0, appyType.multiplicity[1]) - if type(res.validator) == types.FunctionType: - # We will not be able to call this function from the tool. - res.validator = None - if isinstance(appyType, Ref): - res.link = True - res.add = False - res.back = copy.copy(appyType.back) - res.back.attribute += 'DefaultValue' - res.back.show = False - res.select = None # Not callable from tool. - return res - - @classmethod - def _appy_addOptionalField(klass, fieldDescr): - className = fieldDescr.classDescr.name - fieldName = 'optionalFieldsFor%s' % className - fieldType = getattr(klass, fieldName, None) - if not fieldType: - fieldType = String(multiplicity=(0,None)) - fieldType.validator = [] - klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr) - fieldType.validator.append(fieldDescr.fieldName) - fieldType.page.name = 'data' - fieldType.group = Group(fieldDescr.classDescr.klass.__name__) - - @classmethod - def _appy_addDefaultField(klass, fieldDescr): - className = fieldDescr.classDescr.name - fieldName = 'defaultValueFor%s_%s' % (className, fieldDescr.fieldName) - fieldType = klass._appy_copyField(fieldDescr.appyType) - klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr) - fieldType.page.name = 'data' - fieldType.group = Group(fieldDescr.classDescr.klass.__name__) - - @classmethod - def _appy_addPodRelatedFields(klass, fieldDescr): - '''Adds the fields needed in the Tool for configuring a Pod field.''' - className = fieldDescr.classDescr.name - # On what page and group to display those fields ? - pg = {'page': 'documentGeneration', - 'group': '%s_2' % fieldDescr.classDescr.klass.__name__} - # Add the field that will store the pod template. - fieldName = 'podTemplateFor%s_%s' % (className, fieldDescr.fieldName) - fieldType = File(**pg) - klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr) - # Add the field that will store the output format(s) - fieldName = 'formatsFor%s_%s' % (className, fieldDescr.fieldName) - fieldType = String(validator=('odt', 'pdf', 'doc', 'rtf'), - multiplicity=(1,None), default=('odt',), **pg) - klass._appy_addField(fieldName, fieldType, fieldDescr.classDescr) - - @classmethod - def _appy_addQueryResultColumns(klass, classDescr): - '''Adds, for class p_classDescr, the attribute in the tool that - allows to select what default columns will be shown on query - results.''' - className = classDescr.name - fieldName = 'resultColumnsFor%s' % className - fieldType = String(multiplicity=(0,None), validator=Selection( - '_appy_getAllFields*%s' % className), page='userInterface', - group=classDescr.klass.__name__) - klass._appy_addField(fieldName, fieldType, classDescr) - - @classmethod - def _appy_addSearchRelatedFields(klass, classDescr): - '''Adds, for class p_classDescr, attributes related to the search - functionality for class p_classDescr.''' - className = classDescr.name - # Field that defines if advanced search is enabled for class - # p_classDescr or not. - fieldName = 'enableAdvancedSearchFor%s' % className - fieldType = Boolean(default=True, page='userInterface', - group=classDescr.klass.__name__) - klass._appy_addField(fieldName, fieldType, classDescr) - # Field that defines how many columns are shown on the custom search - # screen. - fieldName = 'numberOfSearchColumnsFor%s' % className - fieldType = Integer(default=3, page='userInterface', - group=classDescr.klass.__name__) - klass._appy_addField(fieldName, fieldType, classDescr) - # Field that allows to select, among all indexed fields, what fields - # must really be used in the search screen. - fieldName = 'searchFieldsFor%s' % className - defaultValue = [a[0] for a in classDescr.getOrderedAppyAttributes( - condition='attrValue.indexed')] - fieldType = String(multiplicity=(0,None), validator=Selection( - '_appy_getSearchableFields*%s' % className), default=defaultValue, - page='userInterface', group=classDescr.klass.__name__) - klass._appy_addField(fieldName, fieldType, classDescr) - - @classmethod - def _appy_addImportRelatedFields(klass, classDescr): - '''Adds, for class p_classDescr, attributes related to the import - functionality for class p_classDescr.''' - className = classDescr.name - # Field that defines the path of the files to import. - fieldName = 'importPathFor%s' % className - defValue = classDescr.getCreateMean('Import').path - fieldType = String(page='data', multiplicity=(1,1), default=defValue, - group=classDescr.klass.__name__) - klass._appy_addField(fieldName, fieldType, classDescr) - - @classmethod - def _appy_addWorkflowFields(klass, classDescr): - '''Adds, for a given p_classDescr, the workflow-related fields.''' - className = classDescr.name - groupName = classDescr.klass.__name__ - # Adds a field allowing to show/hide completely any workflow-related - # information for a given class. - defaultValue = False - if classDescr.isRoot() or issubclass(classDescr.klass, ModelClass): - defaultValue = True - fieldName = 'showWorkflowFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) - klass._appy_addField(fieldName, fieldType, classDescr) - # Adds the boolean field for showing or not the field "enter comments". - fieldName = 'showWorkflowCommentFieldFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) - klass._appy_addField(fieldName, fieldType, classDescr) - # Adds the boolean field for showing all states in current state or not. - # If this boolean is True but the current phase counts only one state, - # we will not show the state at all: the fact of knowing in what phase - # we are is sufficient. If this boolean is False, we simply show the - # current state. - defaultValue = False - if len(classDescr.getPhases()) > 1: - defaultValue = True - fieldName = 'showAllStatesInPhaseFor%s' % className - fieldType = Boolean(default=defaultValue, page='userInterface', - group=groupName) - klass._appy_addField(fieldName, fieldType, classDescr) # ------------------------------------------------------------------------------ diff --git a/gen/plone25/skin/edit.pt b/gen/plone25/skin/edit.pt index b6b6a63..9971906 100644 --- a/gen/plone25/skin/edit.pt +++ b/gen/plone25/skin/edit.pt @@ -52,7 +52,7 @@ - + diff --git a/gen/plone25/skin/page.pt b/gen/plone25/skin/page.pt index 06159b2..cb77e19 100644 --- a/gen/plone25/skin/page.pt +++ b/gen/plone25/skin/page.pt @@ -387,7 +387,8 @@ layoutType We must know if we must render the widgets in a "view", "edit" or "cell" layout - +
diff --git a/gen/plone25/skin/widgets/string.pt b/gen/plone25/skin/widgets/string.pt index c43ced7..5f635b4 100644 --- a/gen/plone25/skin/widgets/string.pt +++ b/gen/plone25/skin/widgets/string.pt @@ -100,7 +100,7 @@ The list of values
diff --git a/gen/plone25/templates/Styles.css.dtml b/gen/plone25/templates/Styles.css.dtml index 595c398..5fdfedd 100644 --- a/gen/plone25/templates/Styles.css.dtml +++ b/gen/plone25/templates/Styles.css.dtml @@ -234,7 +234,6 @@ th { font-variant: small-caps; font-weight: bold; font-style: normal; - margin: 0.3em 0 0.3em 0; } .portletSep { border-top: 1px dashed #8cacbb; } .portletGroupItem { padding-left: 0.8em; font-style: italic; }