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/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; } |