From 7240561f7fb878aaa02a4dd6931f54ef91045438 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Wed, 31 Oct 2012 13:20:25 +0100 Subject: [PATCH] [gen] Added param Search.default allowing to define a default Search. The default search, if present, will be triggered when clicking on the main link for a class, instead of the query that collects all instances of this class; appy.gen.Type: removed 3 obsolete params: 'index', 'editDefault' and 'optional'. For achieving the same result than using 'editDefault', one may define 'by hand' an attribute on the Tool for storing the editable default value, and define, on the appropriate field in param 'default', a method that returns the value of the tool attribute; Added Type.defaultForSearch, allowing, for some sub-types, to define a default value when displaying the corresponding widget on the search screen; added a default 'state' field allowing to include workflow state among search criteria in the search screens; removed obsolete test applications. --- gen/__init__.py | 323 ++++++++---------- gen/calendar.py | 10 +- gen/descriptors.py | 53 +-- gen/generator.py | 2 + gen/indexer.py | 2 +- gen/installer.py | 7 +- gen/mixins/ToolMixin.py | 27 +- gen/mixins/__init__.py | 9 + gen/model.py | 5 +- gen/ogone.py | 7 +- gen/po.py | 2 - gen/test/applications/AppyCar/Engine.odt | Bin 6853 -> 0 bytes gen/test/applications/AppyCar/Engine.py | 6 - .../applications/AppyCar/Interior/Radio.py | 5 - .../applications/AppyCar/Interior/__init__.py | 0 gen/test/applications/AppyCar/Wheel.py | 6 - gen/test/applications/AppyCar/__init__.py | 34 -- gen/test/applications/AppyMeeting.py | 10 - gen/test/applications/Meeting.odt | Bin 8565 -> 0 bytes gen/test/applications/MoreGossips.odt | Bin 8254 -> 0 bytes gen/test/applications/Readme.txt | 10 - gen/test/applications/SordidGossips.odt | Bin 8254 -> 0 bytes gen/test/applications/ZopeComponent.py | 130 ------- gen/test/applications/Zzz.py | 67 ---- gen/ui/portlet.pt | 75 ++-- gen/ui/widgets/float.pt | 8 +- gen/ui/widgets/integer.pt | 8 +- gen/ui/widgets/string.pt | 9 +- gen/wrappers/ToolWrapper.py | 15 +- 29 files changed, 255 insertions(+), 575 deletions(-) delete mode 100644 gen/test/applications/AppyCar/Engine.odt delete mode 100644 gen/test/applications/AppyCar/Engine.py delete mode 100644 gen/test/applications/AppyCar/Interior/Radio.py delete mode 100644 gen/test/applications/AppyCar/Interior/__init__.py delete mode 100644 gen/test/applications/AppyCar/Wheel.py delete mode 100644 gen/test/applications/AppyCar/__init__.py delete mode 100644 gen/test/applications/AppyMeeting.py delete mode 100644 gen/test/applications/Meeting.odt delete mode 100644 gen/test/applications/MoreGossips.odt delete mode 100644 gen/test/applications/Readme.txt delete mode 100644 gen/test/applications/SordidGossips.odt delete mode 100644 gen/test/applications/ZopeComponent.py delete mode 100644 gen/test/applications/Zzz.py diff --git a/gen/__init__.py b/gen/__init__.py index 401f7a5..ef0071d 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -300,12 +300,15 @@ class Import: class Search: '''Used for specifying a search for a given type.''' def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None, - **fields): + default=False, **fields): self.name = name self.group = group # Searches may be visually grouped in the portlet self.sortBy = sortBy self.sortOrder = sortOrder self.limit = limit + # If this search is the default one, it will be triggered by clicking + # on main link. + self.default = default # In the dict below, keys are indexed field names and values are # search values. self.fields = fields @@ -368,46 +371,32 @@ class Type: jsFiles = {} dLayouts = 'lrv-d-f' - def __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, sync, mapping, label): + def __init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, searchable, specificReadPermission, + specificWritePermission, width, height, maxChars, colspan, + master, masterValue, focus, historized, sync, mapping, label, + defaultForSearch): # 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. self.validator = validator - # Multiplicity is a tuple indicating the minimum and maximum + # Multiplicity is a 2-tuple indicating the minimum and maximum # occurrences of values. self.multiplicity = multiplicity - # Type of the index on the values. If you want to represent a simple - # (ordered) list of values, specify None. If you want to - # index your values with unordered integers or with other types like - # strings (thus creating a dictionary of values instead of a list), - # specify a type specification for the index, like Integer() or - # String(). Note that this concept of "index" has nothing to do with - # the concept of "database index" (see fields "indexed" and - # "searchable" below). self.index is not yet used. - self.index = index - # Default value - self.default = default - # Is the field optional or not ? - self.optional = optional # Is the field required or not ? (derived from multiplicity) self.required = self.multiplicity[0] > 0 - # May the user configure a default value ? - self.editDefault = editDefault + # Default value + self.default = default # Must the field be visible or not? self.show = show # When displaying/editing the whole object, on what page and phase must # this field value appear? self.page = Page.get(page) self.pageName = self.page.name - # Within self.page, in what group of fields must this field value - # appear? + # Within self.page, in what group of fields must this one appear? self.group = Group.get(group) # The following attribute allows to move a field back to a previous - # position (useful for content types that inherit from others). + # position (useful for moving fields above predefined ones). self.move = move # If indexed is True, a database index will be set on the field for # fast access. @@ -448,8 +437,7 @@ class Type: self.slaves = [] # The behaviour of this field may depend on another, "master" field self.master = master - if master: - self.master.slaves.append(self) + if master: self.master.slaves.append(self) # When master has some value(s), there is impact on this field. self.masterValue = initMasterValue(masterValue) # If a field must retain attention in a particular way, set focus=True. @@ -481,6 +469,10 @@ class Type: # prefix and another name. If you want to specify a new name only, and # not a prefix, write (None, newName). self.label = label + # When you specify a default value "for search", on a search screen, in + # the search field corresponding to this field, this default value will + # be present. + self.defaultForSearch = defaultForSearch def init(self, name, klass, appName): '''When the application server starts, this secondary constructor is @@ -569,13 +561,6 @@ class Type: def isShowable(self, obj, layoutType): '''When displaying p_obj on a given p_layoutType, must we show this field?''' - # Do not show field if it is optional and not selected in tool - if self.optional: - tool = obj.getTool().appy() - fieldName = 'optionalFieldsFor%s' % obj.meta_type - fieldValue = getattr(tool, fieldName, ()) - if self.name not in fieldValue: - return False # Check if the user has the permission to view or edit the field if layoutType == 'edit': perm = self.writePermission else: perm = self.readPermission @@ -759,24 +744,19 @@ class Type: '''Gets, on_obj, the value conforming to self's type definition.''' value = getattr(obj.aq_base, self.name, None) if self.isEmptyValue(value): - # If there is no value, get the default value if any - if not self.editDefault: - # Return self.default, of self.default() if it is a method - if callable(self.default): - try: - return self.callMethod(obj, self.default) - except Exception, e: - # Already logged. Here I do not raise the exception, - # because it can be raised as the result of reindexing - # the object in situations that are not foreseen by - # method in self.default. - return None - else: - return self.default - # If value is editable, get the default value from the tool - portalTypeName = obj._appy_getPortalType(obj.REQUEST) - tool = obj.getTool().appy() - return getattr(tool, 'defaultValueFor%s' % self.labelId) + # If there is no value, get the default value if any: return + # self.default, of self.default() if it is a method. + if callable(self.default): + try: + return self.callMethod(obj, self.default) + except Exception, e: + # Already logged. Here I do not raise the exception, + # because it can be raised as the result of reindexing + # the object in situations that are not foreseen by + # method in self.default. + return None + else: + return self.default return value def getFormattedValue(self, obj, value): @@ -932,10 +912,6 @@ class Type: 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) @@ -984,19 +960,18 @@ class Type: return obj.goto(obj.absolute_url()) class Integer(Type): - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(0,1), default=None, + show=True, page='main', group=None, layouts=None, move=0, + indexed=False, searchable=False, specificReadPermission=False, specificWritePermission=False, width=6, height=None, maxChars=13, colspan=1, master=None, masterValue=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, + focus=False, historized=False, mapping=None, label=None, + defaultForSearch=('','')): + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, True, mapping, - label) + label, defaultForSearch) self.pythonType = long def validateValue(self, obj, value): @@ -1015,14 +990,14 @@ class Integer(Type): class Float(Type): allowedDecimalSeps = (',', '.') allowedThousandsSeps = (' ', '') - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(0,1), default=None, + show=True, page='main', group=None, layouts=None, move=0, + indexed=False, searchable=False, specificReadPermission=False, specificWritePermission=False, width=6, height=None, maxChars=13, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - precision=None, sep=(',', '.'), tsep=' '): + defaultForSearch=('',''), precision=None, sep=(',', '.'), + tsep=' '): # The precision is the number of decimal digits. This number is used # for rendering the float, but the internal float representation is not # rounded. @@ -1036,13 +1011,14 @@ class Float(Type): # Check that the separator(s) are among allowed decimal separators for sep in self.sep: if sep not in Float.allowedDecimalSeps: - raise 'Char "%s" is not allowed as decimal separator.' % sep + raise Exception('Char "%s" is not allowed as decimal ' \ + 'separator.' % sep) self.tsep = tsep - Type.__init__(self, validator, multiplicity, index, default, optional, - editDefault, show, page, group, layouts, move, indexed, - False, specificReadPermission, specificWritePermission, - width, height, maxChars, colspan, master, masterValue, - focus, historized, True, mapping, label) + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, False, specificReadPermission, + specificWritePermission, width, height, maxChars, colspan, + master, masterValue, focus, historized, True, mapping, + label, defaultForSearch) self.pythonType = float def getFormattedValue(self, obj, value): @@ -1167,15 +1143,15 @@ class String(Type): XHTML = 2 PASSWORD = 3 CAPTCHA = 4 - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, format=LINE, - show=True, page='main', group=None, layouts=None, move=0, - 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, label=None, - transform='none', styles=('p','h1','h2','h3','h4'), - allowImageUpload=True, richText=False): + def __init__(self, validator=None, multiplicity=(0,1), default=None, + format=LINE, show=True, page='main', group=None, layouts=None, + move=0, 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, + label=None, defaultForSearch='', transform='none', + styles=('p','h1','h2','h3','h4'), allowImageUpload=True, + richText=False): # According to format, the widget will be different: input field, # textarea, inline editor... Note that there can be only one String # field of format CAPTCHA by page, because the captcha challenge is @@ -1197,13 +1173,15 @@ class String(Type): # CSS property: "none" (default), "uppercase", "capitalize" or # "lowercase". self.transform = transform - Type.__init__(self, validator, multiplicity, index, default, optional, - editDefault, show, page, group, layouts, move, indexed, - searchable, specificReadPermission, + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, True, mapping, - label) + label, defaultForSearch) self.isSelect = self.isSelection() + # If self.isSelect, self.defaultForSearch must be a list of value(s). + if self.isSelect and not defaultForSearch: + self.defaultForSearch = [] # Default width, height and maxChars vary according to String format if width == None: if format == String.TEXT: self.width = 60 @@ -1473,20 +1451,19 @@ class String(Type): return self.getCaptchaChallenge({})['text'] class Boolean(Type): - - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts = None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + '''Field for storing boolean values.''' + def __init__(self, validator=None, multiplicity=(0,1), default=None, + show=True, page='main', group=None, layouts = None, move=0, + 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, label=None): - Type.__init__(self, validator, multiplicity, index, default, optional, - editDefault, show, page, group, layouts, move, indexed, - searchable, specificReadPermission, + focus=False, historized=False, mapping=None, label=None, + defaultForSearch=False): + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, True, mapping, - label) + label, defaultForSearch) self.pythonType = bool dLayouts = {'view': 'lf', 'edit': Table('flrv;=d', width=None)} @@ -1521,8 +1498,7 @@ class Date(Type): WITHOUT_HOUR = 1 dateParts = ('year', 'month', 'day') hourParts = ('hour', 'minute') - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, + def __init__(self, validator=None, multiplicity=(0,1), default=None, format=WITH_HOUR, calendar=True, startYear=time.localtime()[0]-10, endYear=time.localtime()[0]+10, reverseYears=False, @@ -1530,7 +1506,8 @@ 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, label=None): + focus=False, historized=False, mapping=None, label=None, + defaultForSearch=None): self.format = format self.calendar = calendar self.startYear = startYear @@ -1538,12 +1515,11 @@ class Date(Type): # If reverseYears is True, in the selection box, available years, from # self.startYear to self.endYear will be listed in reverse order. self.reverseYears = reverseYears - Type.__init__(self, validator, multiplicity, index, default, optional, - editDefault, show, page, group, layouts, move, indexed, - searchable, specificReadPermission, + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, True, mapping, - label) + label, defaultForSearch) def getCss(self, layoutType, res): # CSS files are only required if the calendar must be shown. @@ -1598,20 +1574,19 @@ class Date(Type): return DateTime.DateTime(value) class File(Type): - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(0,1), default=None, + show=True, page='main', group=None, layouts=None, move=0, + 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, label=None, - isImage=False): + isImage=False, defaultForSearch=''): 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, label) + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, True, mapping, + label, defaultForSearch) @staticmethod def getFileObject(filePath, fileName=None, zope=False): @@ -1756,17 +1731,17 @@ class Ref(Type): wdLayouts = {'view': Table('l-d-f', width='100%')} def __init__(self, klass=None, attribute=None, validator=None, - multiplicity=(0,1), index=None, default=None, optional=False, - editDefault=False, add=False, addConfirm=False, delete=None, - noForm=False, link=True, unlink=None, back=None, show=True, - page='main', group=None, layouts=None, showHeaders=False, - shownInfo=(), select=None, maxPerPage=30, move=0, - indexed=False, searchable=False, specificReadPermission=False, - specificWritePermission=False, width=None, height=5, - maxChars=None, colspan=1, master=None, masterValue=None, - focus=False, historized=False, mapping=None, label=None, - queryable=False, queryFields=None, queryNbCols=1, - navigable=False, searchSelect=None, changeOrder=True): + multiplicity=(0,1), default=None, add=False, addConfirm=False, + delete=None, noForm=False, link=True, unlink=None, back=None, + show=True, page='main', group=None, layouts=None, + showHeaders=False, shownInfo=(), select=None, maxPerPage=30, + move=0, indexed=False, searchable=False, + specificReadPermission=False, specificWritePermission=False, + width=None, height=5, maxChars=None, colspan=1, master=None, + masterValue=None, focus=False, historized=False, mapping=None, + label=None, queryable=False, queryFields=None, queryNbCols=1, + navigable=False, searchSelect=None, changeOrder=True, + defaultForSearch=''): self.klass = klass self.attribute = attribute # May the user add new objects through this ref ? @@ -1852,11 +1827,11 @@ class Ref(Type): # If changeOrder is False, it even if the user has the right to modify # the field, it will not be possible to move objects or sort them. self.changeOrder = changeOrder - 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, sync, mapping, label) + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, sync, mapping, + label, defaultForSearch) self.validable = self.link def getDefaultLayouts(self): @@ -2125,14 +2100,14 @@ def autoref(klass, field): setattr(klass, field.back.attribute, field.back) class Computed(Type): - def __init__(self, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show='view', - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(0,1), default=None, + show='view', page='main', group=None, layouts=None, move=0, + indexed=False, searchable=False, specificReadPermission=False, 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, label=None, context={}): + sync=True, mapping=None, label=None, defaultForSearch='', + context={}): # The Python method used for computing the field value self.method = method # Does field computation produce plain text or XHTML? @@ -2145,11 +2120,11 @@ class Computed(Type): # to the macro specified in self.method. If the dict contains key # "someKey", it will be available to the macro as "options/someKey". self.context = context - Type.__init__(self, None, multiplicity, index, default, optional, - False, show, page, group, layouts, move, indexed, False, - specificReadPermission, specificWritePermission, width, - height, None, colspan, master, masterValue, focus, - historized, sync, mapping, label) + Type.__init__(self, None, multiplicity, default, show, page, group, + layouts, move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, sync, mapping, + label, defaultForSearch) self.validable = False def callMacro(self, obj, macroPath): @@ -2192,10 +2167,9 @@ class Action(Type): by the user on a given gen-class. For example, the custom installation procedure of a gen-application is implemented by an action on the custom tool class. An action is rendered as a button.''' - def __init__(self, validator=None, multiplicity=(1,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(1,1), default=None, + show=True, page='main', group=None, layouts=None, move=0, + indexed=False, searchable=False, specificReadPermission=False, specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, action=None, result='computation', confirm=False, master=None, masterValue=None, focus=False, @@ -2216,11 +2190,11 @@ class Action(Type): # If following field "confirm" is True, a popup will ask the user if # she is really sure about triggering this action. self.confirm = confirm - 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, label) + Type.__init__(self, None, (0,1), default, show, page, group, layouts, + move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, False, mapping, + label, None) self.validable = False def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'} @@ -2258,18 +2232,17 @@ class Action(Type): class Info(Type): '''An info is a field whose purpose is to present information (text, html...) to the user.''' - def __init__(self, validator=None, multiplicity=(1,1), index=None, - default=None, optional=False, editDefault=False, show='view', - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, validator=None, multiplicity=(1,1), default=None, + show='view', page='main', group=None, layouts=None, move=0, + 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, 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, label) + Type.__init__(self, None, (0,1), default, show, page, group, layouts, + move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, False, mapping, + label, None) self.validable = False class Pod(Type): @@ -2279,8 +2252,7 @@ class Pod(Type): POD_ERROR = 'An error occurred while generating the document. Please ' \ 'contact the system administrator.' DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.' - def __init__(self, validator=None, index=None, default=None, - optional=False, editDefault=False, show=('view', 'result'), + def __init__(self, validator=None, default=None, show=('view', 'result'), page='main', group=None, layouts=None, move=0, indexed=False, searchable=False, specificReadPermission=False, specificWritePermission=False, width=None, height=None, @@ -2303,12 +2275,11 @@ class Pod(Type): self.stylesMapping = stylesMapping # Freeze format is by PDF by default self.freezeFormat = freezeFormat - Type.__init__(self, None, (0,1), index, default, optional, - False, show, page, group, layouts, move, indexed, - searchable, specificReadPermission, + Type.__init__(self, None, (0,1), default, show, page, group, layouts, + move, indexed, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, False, mapping, - label) + label, None) self.validable = False def isFrozen(self, obj): @@ -2441,19 +2412,18 @@ class Pod(Type): class List(Type): '''A list.''' - def __init__(self, fields, validator=None, multiplicity=(0,1), index=None, - default=None, optional=False, editDefault=False, show=True, - page='main', group=None, layouts=None, move=0, indexed=False, - searchable=False, specificReadPermission=False, + def __init__(self, fields, validator=None, multiplicity=(0,1), default=None, + show=True, page='main', group=None, layouts=None, move=0, + 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, label=None, subLayouts=Table('fv', width=None)): - 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, label) + Type.__init__(self, validator, multiplicity, default, show, page, group, + layouts, move, indexed, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, historized, True, mapping, + label, None) self.validable = True # Tuples of (names, Type instances) determining the format of every # element in the list. @@ -2941,8 +2911,7 @@ class Selection: # ------------------------------------------------------------------------------ class Model: pass class Tool(Model): - '''If you want so define a custom tool class, she must inherit from this - class.''' + '''If you want to extend or modify the Tool class, subclass me.''' class User(Model): '''If you want to extend or modify the User class, subclass me.''' diff --git a/gen/calendar.py b/gen/calendar.py index 3f7e6b7..0dc8f56 100644 --- a/gen/calendar.py +++ b/gen/calendar.py @@ -21,11 +21,11 @@ class Calendar(Type): otherCalendars=None, additionalInfo=None, startDate=None, endDate=None, defaultDate=None, preCompute=None, applicableEvents=None): - Type.__init__(self, validator, (0,1), None, default, False, False, - show, page, group, layouts, move, False, False, - specificReadPermission, specificWritePermission, - width, height, None, colspan, master, masterValue, focus, - False, True, mapping, label) + Type.__init__(self, validator, (0,1), default, show, page, group, + layouts, move, False, False, specificReadPermission, + specificWritePermission, width, height, None, colspan, + master, masterValue, focus, False, True, mapping, label, + None) # eventTypes can be a "static" list or tuple of strings that identify # the types of events that are supported by this calendar. It can also # be a method that computes such a "dynamic" list or tuple. When diff --git a/gen/descriptors.py b/gen/descriptors.py index 85ef09c..155edef 100644 --- a/gen/descriptors.py +++ b/gen/descriptors.py @@ -131,22 +131,15 @@ class ClassDescriptor(Descriptor): parentWrapper = '%s_Wrapper' % k.name return (parentWrapper, parentClass) - def generateSchema(self, configClass=False): - '''Generates i18n and other related stuff for this class. If this class - is in the configuration (tool, user, etc) we must avoid having - attributes that rely on the configuration (ie attributes that are - optional, with editDefault=True, etc).''' + def generateSchema(self): + '''Generates i18n and other related stuff for this class.''' for attrName in self.orderedAttributes: try: attrValue = getattr(self.klass, attrName) except AttributeError: attrValue = getattr(self.modelClass, attrName) - if isinstance(attrValue, gen.Type): - if configClass: - attrValue = copy.copy(attrValue) - attrValue.optional = False - attrValue.editDefault = False - FieldDescriptor(attrName, attrValue, self).generate() + if not isinstance(attrValue, gen.Type): continue + FieldDescriptor(attrName, attrValue, self).generate() def isAbstract(self): '''Is self.klass abstract?''' @@ -392,13 +385,7 @@ class FieldDescriptor: '''Walks into the Appy type definition and gathers data about the i18n labels.''' # Manage things common to all Appy types - # - optional ? - if self.appyType.optional: - self.generator.tool.addOptionalField(self) - # - edit default value ? - if self.appyType.editDefault: - self.generator.tool.addDefaultField(self) - # - put an index on this field? + # Put an index on this field? if self.appyType.indexed and (self.fieldName != 'title'): self.classDescr.addIndexMethod(self) # i18n labels @@ -477,28 +464,6 @@ class ToolClassDescriptor(ClassDescriptor): def isFolder(self, klass=None): return True def isRoot(self): return False - def generateSchema(self): - ClassDescriptor.generateSchema(self, configClass=True) - - 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) - fieldType.validator.append(fieldDescr.fieldName) - fieldType.page.name = 'data' - fieldType.group = gen.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) - fieldType.page.name = 'data' - fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__) def addPodRelatedFields(self, fieldDescr): '''Adds the fields needed in the Tool for configuring a Pod field.''' @@ -607,8 +572,6 @@ class UserClassDescriptor(ClassDescriptor): self.klass = klass self.customized = True def isFolder(self, klass=None): return False - def generateSchema(self): - ClassDescriptor.generateSchema(self, configClass=True) class GroupClassDescriptor(ClassDescriptor): '''Represents the class that corresponds to the Group for the generated @@ -634,8 +597,6 @@ class GroupClassDescriptor(ClassDescriptor): self.klass = klass self.customized = True def isFolder(self, klass=None): return False - def generateSchema(self): - ClassDescriptor.generateSchema(self, configClass=True) class TranslationClassDescriptor(ClassDescriptor): '''Represents the set of translation ids for a gen-application.''' @@ -648,8 +609,6 @@ class TranslationClassDescriptor(ClassDescriptor): def getParents(self, allClasses=()): return ('Translation',) def isFolder(self, klass=None): return False - def generateSchema(self): - ClassDescriptor.generateSchema(self, configClass=True) def addLabelField(self, messageId, page): '''Adds a Computed field that will display, in the source language, the @@ -714,6 +673,4 @@ class PageClassDescriptor(ClassDescriptor): self.klass = klass self.customized = True def isFolder(self, klass=None): return True - def generateSchema(self): - ClassDescriptor.generateSchema(self, configClass=True) # ------------------------------------------------------------------------------ diff --git a/gen/generator.py b/gen/generator.py index 4f3eff6..0fd6baf 100644 --- a/gen/generator.py +++ b/gen/generator.py @@ -762,6 +762,8 @@ class ZopeGenerator(Generator): if classDescr.name in self.referers: for field in self.referers[classDescr.name]: names.append(field.appyType.back.attribute) + # Add the 'state' attribute + names.append('state') qNames = ['"%s"' % name for name in names] attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames))) repls['attributes'] = ',\n '.join(attributes) diff --git a/gen/indexer.py b/gen/indexer.py index 7fc4ac0..79d069b 100644 --- a/gen/indexer.py +++ b/gen/indexer.py @@ -7,7 +7,7 @@ from appy.shared.utils import normalizeText # Default Appy indexes --------------------------------------------------------- defaultIndexes = { - 'State': 'FieldIndex', 'UID': 'FieldIndex', 'Title': 'TextIndex', + 'State': 'ListIndex', 'UID': 'FieldIndex', 'Title': 'TextIndex', 'SortableTitle': 'FieldIndex', 'SearchableText': 'TextIndex', 'Creator': 'FieldIndex', 'Created': 'DateIndex', 'ClassName': 'FieldIndex', 'Allowed': 'KeywordIndex'} diff --git a/gen/installer.py b/gen/installer.py index a0670cb..b66cecc 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -354,9 +354,14 @@ class ZopeInstaller: wrapperClass = klass.wrapperClass if not hasattr(wrapperClass, 'title'): # Special field "type" is mandatory for every class. - title = gen.String(multiplicity=(1,1), show='edit',indexed=True) + title = gen.String(multiplicity=(1,1),show='edit',indexed=True) title.init('title', None, 'appy') setattr(wrapperClass, 'title', title) + # Special field "state" must be added for every class + state = gen.String(validator=gen.Selection('_appy_listStates'), + show='result') + state.init('state', None, 'workflow') + setattr(wrapperClass, 'state', state) names = self.config.attributes[wrapperClass.__name__[:-8]] wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names] # Post-initialise every Appy type diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index b7556ff..0cc4efb 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -1,8 +1,6 @@ # ------------------------------------------------------------------------------ import os, os.path, sys, re, time, random, types, base64, urllib -from appy.shared import mimeTypes -from appy.shared.utils import getOsTempFolder, sequenceTypes -from appy.shared.data import languages +from appy import Object import appy.gen from appy.gen import Type, Search, Selection, String from appy.gen.utils import SomeObjects, getClassName @@ -10,6 +8,9 @@ from appy.gen.mixins import BaseMixin from appy.gen.wrappers import AbstractWrapper from appy.gen.descriptors import ClassDescriptor from appy.gen.mail import sendMail +from appy.shared import mimeTypes +from appy.shared.utils import getOsTempFolder, sequenceTypes +from appy.shared.data import languages try: from AccessControl.ZopeSecurityPolicy import _noroles except ImportError: @@ -681,11 +682,15 @@ class ToolMixin(BaseMixin): return obj, fieldName def getSearches(self, contentType): - '''Returns the list of searches that are defined for p_contentType. - Every list item is a dict that contains info about a search or about - a group of searches.''' + '''Returns an object with 2 attributes: + * "searches" stores the searches that are defined for p_contentType; + * "default" stores the search defined as default one. + Every item representing a search is a dict containing info about a + search or about a group of searches. + ''' appyClass = self.getAppyClass(contentType) - res = [] + searches = [] + default = None # Also retrieve the default one here. visitedGroups = {} # Names of already visited search groups for search in ClassDescriptor.getSearches(appyClass): # Determine first group label, we will need it. @@ -699,7 +704,7 @@ class ToolMixin(BaseMixin): 'label': self.translate(groupLabel), 'descr': self.translate('%s_descr' % groupLabel), } - res.append(group) + searches.append(group) visitedGroups[search.group] = group # Add the search itself searchLabel = '%s_search_%s' % (contentType, search.name) @@ -709,8 +714,10 @@ class ToolMixin(BaseMixin): if search.group: visitedGroups[search.group]['searches'].append(dSearch) else: - res.append(dSearch) - return res + searches.append(dSearch) + if search.default: + default = dSearch + return Object(searches=searches, default=default).__dict__ def getQueryUrl(self, contentType, searchName, startNumber=None): '''This method creates the URL that allows to perform a (non-Ajax) diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index 6924678..11eb33f 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -1285,6 +1285,15 @@ class BaseMixin: return stateShow(workflow, self.appy()) else: return stateShow + def _appy_listStates(self): + '''Lists the possible states for this object.''' + res = [] + workflow = self.getWorkflow() + for elem in dir(workflow): + if getattr(workflow, elem).__class__.__name__ != 'State': continue + res.append((elem, self.translate(self.getWorkflowLabel(elem)))) + return res + def _appy_managePermissions(self): '''When an object is created or updated, we must update "add" permissions accordingly: if the object is a folder, we must set on diff --git a/gen/model.py b/gen/model.py index 48dc848..9f30ad5 100644 --- a/gen/model.py +++ b/gen/model.py @@ -209,10 +209,9 @@ setattr(Page, Page.pages.back.attribute, Page.pages.back) # The Tool class --------------------------------------------------------------- # Prefixes of the fields generated on the Tool. -toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns', +toolFieldPrefixes = ('podTemplate', 'formats', 'resultColumns', 'enableAdvancedSearch', 'numberOfSearchColumns', - 'searchFields', 'optionalFields', 'showWorkflow', - 'showAllStatesInPhase') + 'searchFields', 'showWorkflow', 'showAllStatesInPhase') defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom', 'appyVersion', 'dateFormat', 'hourFormat', 'users', 'connectedUsers', 'groups', 'translations', diff --git a/gen/ogone.py b/gen/ogone.py index 7f732c3..2dc32bc 100644 --- a/gen/ogone.py +++ b/gen/ogone.py @@ -35,10 +35,11 @@ class Ogone(Type): specificWritePermission=False, width=None, height=None, colspan=1, master=None, masterValue=None, focus=False, mapping=None, label=None): - Type.__init__(self, None, (0,1), None, None, False, False, show, page, - group, layouts, move, False, False,specificReadPermission, + Type.__init__(self, None, (0,1), None, show, page, group, layouts, move, + False, False,specificReadPermission, specificWritePermission, width, height, None, colspan, - master, masterValue, focus, False, True, mapping, label) + master, masterValue, focus, False, True, mapping, label, + None) # orderMethod must contain a method returning a dict containing info # about the order. Following keys are mandatory: # * orderID An identifier for the order. Don't use the object UID diff --git a/gen/po.py b/gen/po.py index 1da8e1a..a108de2 100644 --- a/gen/po.py +++ b/gen/po.py @@ -29,14 +29,12 @@ class PoMessage: # The following messages (starting with MSG_) correspond to tool # attributes added for every gen-class (warning: the message IDs correspond # to MSG_). - MSG_defaultValue = "Default value for field '%s'" MSG_podTemplate = "POD template for field '%s'" MSG_formats = "Output format(s) for field '%s'" MSG_resultColumns = "Columns to display while showing query results" MSG_enableAdvancedSearch = "Enable advanced search" MSG_numberOfSearchColumns = "Number of search columns" MSG_searchFields = "Search fields" - MSG_optionalFields = 'Optional fields' MSG_showWorkflow = 'Show workflow-related information' MSG_showAllStatesInPhase = 'Show all states in phase' POD_ASKACTION = 'Trigger related action' diff --git a/gen/test/applications/AppyCar/Engine.odt b/gen/test/applications/AppyCar/Engine.odt deleted file mode 100644 index 85c4be8ff3b02e3e6a545f4b45bcdc0d16cc4699..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6853 zcma)B1yqz<*QP^4kPr|F6;K+aq>+&B?x6>Xfnn%w0YL!;1VNCH?iw0Ix|^Xphb{^K z;oi?rufN{)?OE@v{jPVO=j;=+&pywqEQg9ljC5`1iYI)AUpR3Y5dQUrg}7{iw&qT5 zAaei+WCJt>I05bL*qrUmSnUB|Aehx2WNv3>Zwj$Bw{v23GIw=S{s$QX|6ij(c;fbU z7C=jg<6ksjPByR;zzG620XVY#jl)7Tc^$-mK?vtMkY8~CQ&V#ra|DgOBbzD2(eb)< zS6drYBxO0w>!6WPuD=lW=WxgnPk*Jgu(1a?nST%F8vkoBzv~3Z9s~g)1_1J(PW%o> z`J2{2j`o(0=HQ>Y|2yRG;=FF`$DsYMC?|V+n}0`9|0YVHEx^(o%=Q%MWD5X+|7D(V z5MWb#JE#B5MTVw|J&cCHd$zFXqKNnDRHp7G-Gp2@aF9{KnrHhBit{+Tt8dK8?2d2z zxb2V*ESzZ8Yt~`P3)9fs)yAx1@ym^TO*f9jRdOx$$Eh0{SGEU)2CLMg_HUs%Xv=g% zWzVY|L#>zRcu^}#%nu9pdq1fXG1+8Gx7e;*W$P8VYd(>P1LRv3!)X|XP+i-dF9sCo zJhnpRs4@#F8^w7Zn~A*HDpnq*ts7M9h|)}x6(31_%eVI+{Mn=7LXCPz0Sm{>&PnOz zvV0=NZC+kpK3BD7uKCH{c3%@>aL1(y>FrXKlks2yPHvH?yP4_G6~Dxe4BAM+&7A3x z7?R@LzSv;97YTVUk*F$9q803S=fus zD75rEUo9t@0Fx`-gCm6v>&^MhT0rdHA1=U8a)b*g0^jgIv?Xlq1Qq z1`Fa;E;^E0U1mRP)YIy16QRC736*LWcbpqrue@M5n=wC;n7D0HOP61-=@iM`I35h?E>Y5X)e zZ9Jc+pQGN$2`zI72=G*bdDZ8CTofx6ci(%CKa9lK(=4AdPSTyXttG$@Bb{ul3Q|}3 zNKXBLsDRrN>#~*HA1psoR2@^ydlYe>R+pT!7Gp%J)>lYXiKqKs1G763?a*8XV=#w4+Syy%0L8Q$c)ukNRLSsMLYW+Jd7R@$!hqlN3y2 zP^L-b+eW2>Mz*cgO5sPX4n%Li!RHp~W%jB$Mt!kivW}uqFv!jE!Tk8^T*}_Axk!|A zX1cW&f)sh{rUneyq3MB-%G{0*X7$V2M`8=Bxua5@pao;aXHb7^-*c`zy2)WUg)DI+ z5xEqD6O_8b3oSiIx1F8ki-mynk)%`#cZCm*pjW8Zxvfh-c32e^3F+#__uTfol!F$Z5~#U7n$dl(Oa&#V!^YJA2@PjRr-Fb3tk8KX>T`&hUCmThr{v|qYOjgQxk zv`U)ogi5qChzcdtrS^P!I;!nvgLf%Q>zW_1J;2sw_M?!W|*O9^gPDn$$?f%MD z$e4Bc0=eE2W3C)M3x_j9Jb9BDkkrsMZdjL+1oZ%yUMjuKEpLo<(3Rd7n+_NrmCD*HpGuXeRVUpz3&>Y@JCDvk4xzN?*#twS1pWsD1r)3=$r?Wzx5 zsrAY(Y#IqGBXjI^&VojdMxM4}d;9TMm98JUFErfW?$J4Vx$<^hT1X%_S><&(xt6)n zpeWmWUkt!a=^?Y>vR8E#5Nh{fSur2ofr!^h=FL+L$5YlJ&#vx(o#H${1Tg|RQSi}U zMu!_P#V4g)VV+BT%YC+!%_YR+o%x2dL5km-hI~6g!l1+=8i=33DPXEoou%rKh@3$6 zX=U7xU}#A_ucyks580zvvy4<>2>K`_?j`Q|Wo7*Eb{3gWnsj`p9 zZ3gerMB-JV@4-65ZnW@|s;jHTrSgdw27V13)l9wl0i!QrKSy019S@%}BEy1Mx1w0t z@y>%PI|99I$9Pt*;w~WyP{L#V-m1$lO=z!>@7t&;12xE(I)LN({0qWinLN0IG{rf+ zZkd7p!FCY5m&O@__gWf|2Awo@cVeTgY*FSg! zsKi>uh!TCQ%F9|z%;`~Lr%pGjfYDe}qQ6itX?ti#=x>WUhx#$Utj&fA99&Zd&(Ea| z&(9Zke5?ElYNACV-yE6wfRL+?))F7j#CIdE_N6h+PL8fc#W4miO|URdsyH4Mg9Qz7 z#}j6y7m9;>TlCwSJ;D&CcFRh$C&*BP4)YvyRt zUZV~v8_G-9za>bD4)2MK&6y;|WMtE(h6mo| z<&&f!!m3e^qXsC}^oL7lafG!p7&MD|G~K@hnW{%|mqc5yalYbM<5Sxrn;uuy3C9#3 z_a7n;g>nf;*Ef*&eC$1nR({}|rJc(5rZ3XPQ>ccY8Z7t7W#tvTTdXx0-fepKaPQc4 z`Y3c2Qe}v$w5@Js1~Q**#y?0ZNzQ&J8l$ARoE_Vpe5+@tteO4{FOITt?C=HUR;Y7W z09@nq?8J1(V40qOOjV3n)u575Z+Imfr-mib})%2%asvqVsR?m@K}kIF79cP4vW zT-GfS(2ZH|mJ&zH9UGknmzT*BuB)meqekNHU(lw8B}<3gJ^7GUChPU$&}KOv9@g-H z^mBTKTiWF!SLf)Yq2dqqIqiomX8D%AXbL8R7NAgN{ju9&n=4Np>JV;&{=)(oPAhkV zvi|zvH_QsQumM@~1ixvGf>Y=yJdMv9J$>RpQ!j+)F<^Uep)^Fx}HdXI6um2?Jf zB^-HN=*egBg=VPzEh{!bKU0YoLe7K~l=gFEjDz^s_+lxEF?Xtc#33#rXSE)H^)5xz z^e%s6CRlq_ciFiLriaO)@m3yW=d-I9dZOTcF@i{%=bw0{#ULm6!Ws$mLj0JkN(>Xb zls+g43T1s;4n1#j3zUk>O9*SC+a{~$2jbM$Yq`ogsf(rHm41kW;bOG(}g*VcN%ASQ3>>}qX z1+Ao5n|hTCVO~@Pw^DS14%AK_EG18RCqWw?c+9JWJpXt!ikp^E%ErL45}8Ige=|?%t519Vw;R`+(!HEe4g(CtaRE_3{<)6->o8&N z1i0RkUd9?AibLYyof9VJ+)6SZXY4S8?8y*%+l_SdF_IvnkX{U&-K48?K$3Rxh)ng8 zP@hLH4#p0ZqAk${x@};zdAx)JnXg$Gj!xYC&gJbgZ0ekw0uVkdj6kN*N*WgHrol#? z3|h5pTo0BPX;NhOfH=Y0QtFudCbVN*dja&z!&qloxKX3dXo~={G}`dk0*8`myW&Da z$xDftA4&6Pwbq*>3%zN~xDsT#;bG{Fb@egw{QcxKfHl zO@Ub`2KGDMj9iAX?e!eJ*cq?eB_ilu&ETvNCid_hUWddbm#8``zDatW`j*_tAvD?` zk}@&*0Ideqtj;+iE_K^GD!e@)in1x=~~JC{#$ ze6Cl)r;3Bteqsj?y`dHEH`m?!K=&B3ns?Kb4Q0D zY?MJgaxyYYAOB7d7XPFD*=3PYz2ymjJV) zDR_MhEiL2W9?LRq%n6MhojQ`N^fL; zm?@zkYhZkkwXU1hr--&>rRKQ4rgwkb8Zu@#KD)=%q@Cp-qP zBu%K59%x?5A6%5x)|4`S8xuOexY$f7ER2CGX#?sZHD{|@T79)1E}03_u4nsw<#`U5 zd*>@lJFI;^$ET+w1wu6!Y1;?z?r|Lh!^)NBjRteb!Br2BQWQ+Y-cnq;=oPV8u-0RBJ+{p=OXZcrcTCQUNp5!O+YG}B(=z?-q zRgD+Z&yjvO*x!f*Tsc-4$61pab${@rv0+CxSibq0T*Q%tiJWD|$V6$)e$T4pJw-!N zVB|gzpFlo31I_)2>ZBQrsE^LeYP;2YV|a@6g8uFiDsO$cNbtV&6L>ux$|zVZv=5N) z;OFdt7?hwnO-n%J#R{@UW+Mv(OwVG1=JwBc;>lx1b4SiRz4;sXB)RjG4UCMmreR0@ zSj&M*H@V+Lc|X5FXoNR`eP)tWe4x8s^6ndTQXc79FZauG>QR{eR37e<2#Fm{as3k= zp$fSgymqt8pq7h~=S>A}JF6ET_EJ%Jnz^(aIJ#nIKtO+~?M&!P5TEsJ`Y)(kL{U99 zx8Y|a=G!TYMc>ec-*4+5W^Hyggad9`@==S*ANr3*bUa$ayWmJeq7MjGAtx>!c&!iw z#+&5`_F5yNKWy546PQ85YN{<-yPY#VPd~tfB zbz+3@Vw~u~?Q-4ua%*8yW%qc#;S%vlKm23K5WG77`C@;&G>-VK7q#Ig{UU@hgvhpo zHo-YnOw3k^k{Ba)<#$gv9eeIjQ8@*Lt5 zTlZf)j93&_v-uo~-4ZLQi6MKt!_XoR%Vi$@vk#?29(9Qs?gsA{;q-)^{3Gl!+{R4j zabhUvZjE|@U?#`o8VR4)R{!Ruw^wvBJtcj$=}esgVNbEtabnc{Dq5A+{U#G+)%#au z@9dK}g)R>|rT0DV>-(55?Mx$sez_g4Ek5b%YtJ|1!QZC4R!ST@-AN!{ks#Mu^ps;K zuz)w=m8vT&M4>R*sy1ii>kYhh9*0w^w~3nZ!MR=Zaq=ty8-Uk<=5AxfjQz1K&jqK^ z0C7rJoJZ3icF}KIGti2xt?E;@-{g;VP-8#KgMG?VEQ=52oac;ZlMPl)b09sIa}gWZ zycdqyXcB7@9=7~>D-QjxpIMrmW^MYq21^kms?=KhB+7h%ime{TDQ+dZLhHUt0Ms=2 z%}o(Aw%z*;f@>XbMYr$IYu`6Qk4&i2oAwx=YzZ-TITUhW8cyo)roSI^cB4gkH~WDJ z9$u!`@P&&-5Ec<`yrfbs~bT-=rCY4Mv!l5J*BEQ1EF@Hx~5-i|Ke zogllrh1|5#W#&%s7ie9n+xrG(ur)j?^1Qu=nly`g`cP04xCE%_;Gd$#|JXzN%uhLU zSP+BTKgdm$d0WPjX<$H)*97LGRIdEYWrEC*q*)0*u|+8Um`k1QO~V~k8%E4qAd)6( zib2z_LFEbDsH!5)`iJ|I{bc63cQ*nj*Oou+V?0e&%w|0f`u666d+{|UYsk~N zsfiC5xvges&Ek%(g#*Lb4Dw$DpmV11?Wrc8uCn=_wx;Zf|c}lIy)IqO(X$(lcW2lSX2?!4=kBkiCoHaqrk0Vg`YBq^)K*%fstL z854cl=p^*WtL})1CQRqGDN0(eD~irI}${;Q;<{_W06smVzUL<0WHkIe+iZ4Z{)07*a<{Wgn|ZU zG+`PX4k#>#XzR%I`{RUbAr{2ZFfIJ0A)eSwZ2|1`6RG<>R>hNgYTNA5#|1=6Js&^y zKbn2c>%3rkY1j}tvJU_>ts}pH4Snc5r+lCFUV5OMN?L8R@m_hJE_MHrAzgSRtZ)<7 zQRexHbx!O~-a)hLCsxia?qN@?acrz6EA*trSb;I#XIkKVE3y-2)|wDA`MbWA?_WKg zp{r4FJFx3F4Qr!W)47GpFe%C$fSJ*6oc#5nMwkP?Nw0~67GaaIAUml)Cbeq25)Dc7 zGZCqDM_!>IL)_k(L=%uLKq9*E9Sa)cMpevZosqJm+7Xuw?}qMfB^f+c>AkdKxjRwv zRy^vvU1+>pu%I-{t3P z?fQoWA>3bi{-9+4TX5GU)DN3NxWAJA(6RqHFh5X8zjW~yVkm!Q`B~TgGtafM{ljE` z$MX-J`_D}O>{sA#n11Nq|0@ghH!MGN@ISL$E8#zE_ctv6PZ$52?E5PCS*W{K#(x+K zVsV)M(8<5g^mpLThpB5F?}t^P|E%i$iu<|z{_7RUe}M{!q6_g3=dXj;uc)7^wttSl z8vdUP^R@pZQRVWNoJ-{wwbIdsHX6A9@i;sZxWp-rUM GcmD^dyM*)r diff --git a/gen/test/applications/AppyCar/Engine.py b/gen/test/applications/AppyCar/Engine.py deleted file mode 100644 index 37b416c..0000000 --- a/gen/test/applications/AppyCar/Engine.py +++ /dev/null @@ -1,6 +0,0 @@ -from appy.gen import * - -class Engine: - engineType = String() - description = String(format=String.XHTML) - pod = True diff --git a/gen/test/applications/AppyCar/Interior/Radio.py b/gen/test/applications/AppyCar/Interior/Radio.py deleted file mode 100644 index c93d9f8..0000000 --- a/gen/test/applications/AppyCar/Interior/Radio.py +++ /dev/null @@ -1,5 +0,0 @@ -from appy.gen import * - -class Radio: - abstract = True - name = String() diff --git a/gen/test/applications/AppyCar/Interior/__init__.py b/gen/test/applications/AppyCar/Interior/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/gen/test/applications/AppyCar/Wheel.py b/gen/test/applications/AppyCar/Wheel.py deleted file mode 100644 index 9020281..0000000 --- a/gen/test/applications/AppyCar/Wheel.py +++ /dev/null @@ -1,6 +0,0 @@ -from appy.gen import * - -class Wheel: - title = String(multiplicity=(1,1)) - description = String(format=String.XHTML) - diff --git a/gen/test/applications/AppyCar/__init__.py b/gen/test/applications/AppyCar/__init__.py deleted file mode 100644 index 5f6b0c4..0000000 --- a/gen/test/applications/AppyCar/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -from appy.gen import * -from AppyCar.Interior.Radio import Radio - -class RallyCarWorkflow: - # Roles - carDriver = 'CarDriver' - driverM = ('Manager', carDriver) - # Specific permissions - readColor = ReadPermission('Car.color') - writeColor = WritePermission('Car.color') - # States - created = State({r:driverM, w:driverM, d:driverM, - readColor: driverM, writeColor: driverM}, initial=True) - running = State({r:driverM, w:driverM, d:driverM, - readColor: 'Manager', writeColor: 'Manager'}) - # Transitions - run = Transition( (created, running), condition=driverM) - stop = Transition( (running, created), condition=driverM) - -class Car: - sport = Boolean() - color = String(specificReadPermission=True, specificWritePermission=True) - description = String(format=String.TEXT) - -class RallyCar(Car): - root = True - workflow = RallyCarWorkflow - test = Integer() - -class StandardRadio(Radio): - test1 = Integer() - -c = Config() -c.languages = ('en', 'fr') diff --git a/gen/test/applications/AppyMeeting.py b/gen/test/applications/AppyMeeting.py deleted file mode 100644 index 080d0e4..0000000 --- a/gen/test/applications/AppyMeeting.py +++ /dev/null @@ -1,10 +0,0 @@ -from appy.gen import * - -class Meeting: - place = String(editDefault=True) - date = Date() - myObservations = String(format=String.XHTML, optional=True) - annex = File(optional=True) - leader = String(validator=['andyStein', 'joelLambillotte']) - root = True - pod = ['Meeting'] diff --git a/gen/test/applications/Meeting.odt b/gen/test/applications/Meeting.odt deleted file mode 100644 index 3d04f779f5928dbf7b9b48fa06e37d6292129502..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8565 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 M0>b(s1w%mmKb2;AT>t<8 diff --git a/gen/test/applications/MoreGossips.odt b/gen/test/applications/MoreGossips.odt deleted file mode 100644 index d0d9a81b669b152012b5d476ea0078b856635fba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8254 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 OefD9JkHB9m@c#kWhv$m` diff --git a/gen/test/applications/Readme.txt b/gen/test/applications/Readme.txt deleted file mode 100644 index 18ad130..0000000 --- a/gen/test/applications/Readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -This folder contains some example Appy applications used for testing purposes. - -Directly in the folder, you have the application "AppyMeeting" which consists in -a single Python file (module AppyMeeting.py) and a POD template for producing -documents (Meeting.odt). You also have simple applications Zzz.py and -ZopeComponent.py that are one-file applications. - -In sub-folder AppyCar, you have a more complex application that is structured -as a Python package (a hierarchy of folders). - diff --git a/gen/test/applications/SordidGossips.odt b/gen/test/applications/SordidGossips.odt deleted file mode 100644 index d0d9a81b669b152012b5d476ea0078b856635fba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8254 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 OefD9JkHB9m@c#kWhv$m` diff --git a/gen/test/applications/ZopeComponent.py b/gen/test/applications/ZopeComponent.py deleted file mode 100644 index 4fb816a..0000000 --- a/gen/test/applications/ZopeComponent.py +++ /dev/null @@ -1,130 +0,0 @@ -from appy.gen import * - -class BunchOfGeek: - description = String(format=String.TEXT) - -class ZopeComponentTool(Tool): - someUsefulConfigurationOption = String() - def onInstall(self): - self.someUsefulConfigurationOption = 'My app is configured now!' - install = Action(action=onInstall) - -class ZopeComponentWorkflow: - # Specific permissions - wf = WritePermission('ZopeComponent.funeralDate') - rb = ReadPermission('ZopeComponent.responsibleBunch') - # Roles - zManager = 'ZManager' - zLeader = 'ZLeader' - managerM = (zManager, 'Manager') - leaderM = (zLeader, 'Manager') - everybody = (zManager, zLeader, 'Manager') - # States - created = State({r:leaderM, w:('Owner', 'Manager'), d:leaderM, wf:'Owner', - rb:everybody}, initial=True) - validated = State({r:everybody, w:everybody, d:None, wf:everybody, - rb:everybody}) - underDevelopment = State({r:everybody, w:leaderM, d:None, wf:leaderM, - rb:everybody}) - whereIsTheClient = State({r:everybody, w:managerM, d:None, wf:managerM, - rb:everybody}) - # Transitions - def funeralOk(self, obj): return obj.funeralDate - validate = Transition( (created, validated), - condition=managerM + (funeralOk,)) - def updateDescription(self, obj): - obj.description = 'Description edited by my manager was silly.' - startDevelopment = Transition( (validated, underDevelopment), - condition=leaderM, action=updateDescription) - cancelDevelopment = Transition( (underDevelopment, whereIsTheClient), - condition=managerM) - cancel = Transition( ( (whereIsTheClient, underDevelopment), - (underDevelopment, validated), - (validated, created)), condition='Manager') - -class ZopeComponent: - root = True - workflow = ZopeComponentWorkflow - def showDate(self): - return True - def validateDescription(self, value): - res = True - if value.find('simple') != -1: - res = self.translate('zope_3_is_not_simple') - return res - description = String(editDefault=True) - technicalDescription = String(format=String.XHTML, - validator=validateDescription) - #status = String(validator=['underDevelopement', 'stillSomeWorkToPerform', - # 'weAreAlmostFinished', 'alphaReleaseIsBugged', 'whereIsTheClient'], - # optional=True, editDefault=True) - funeralDate = Date(optional=True, specificWritePermission=True) - responsibleBunch = Ref(BunchOfGeek, multiplicity=(1,1), add=False, - link=True, back=Ref(attribute='components'), - specificReadPermission=True) - -class CobolComponentWorkflow(ZopeComponentWorkflow): - p = ZopeComponentWorkflow # Shortcut to workflow parent - # An additional state - finished = State(p.whereIsTheClient.permissions) - # Override validate: condition on funeralDate has no sense here - validate = Transition(p.validate.states, condition=p.managerM) - # Override cancelDevelopment: go to finished instead - cancelDevelopment = Transition( (p.underDevelopment, finished), - condition=p.managerM) - # Update cancel accordingly - cancel = Transition( ((finished, p.underDevelopment),) +p.cancel.states[1:], - condition=p.cancel.condition) - -class CobolComponent: - root = True - workflow = CobolComponentWorkflow - description = String() - -class Person: - abstract = True - pod = True - title = String(show=False) - n = 'name_3' - firstName = String(group=n, width=15) - middleInitial = String(group=n, width=3) - name = String(multiplicity=(1,1), group=n, width=30) - contractDetails = String(format=String.XHTML) - cia = {'page': 'contactInformation', 'group': 'address_2'} - street = String(**cia) - number = Integer(**cia) - country = String(**cia) - zipCode = Integer(**cia) - cio = {'page': 'contactInformation', 'group': 'numbers_2', 'width': 20} - phoneNumber = String(**cio) - faxNumber = String(**cio) - mobilePhone = String(**cio) - workPhoneNumber = String(**cio) - workFaxNumber = String(**cio) - workMobilePhone = String(**cio) - def onEdit(self, created): - self.title = self.firstName + ' ' + self.name - -class Worker(Person): - root = True - productivity = Integer() - -class Parasite(Person): - root = True - pod = ['SordidGossips', 'MoreGossips'] - hairColor = String(group='hairyCharacteristics') - sordidGossips = String(format = String.XHTML, page='Gossips') - parasiteIndex = String(validator=['low', 'medium', 'high', - 'unquantifiable'], - page='contactInformation', group='numbers') - details = String(page='contactInformation', group='numbers', - master=parasiteIndex, masterValue='unquantifiable') - avoidAnyPhysicalContact = Boolean(page='contactInformation') - def validate(self, new, errors): - if (new.hairColor == 'flashy') and (new.firstName == 'Gerard'): - errors.hairColor = True - errors.firstName = "Flashy Gerards are disgusting." - -c = Config() -c.languages = ('en', 'fr') -c.defaultCreators += ['ZLeader'] diff --git a/gen/test/applications/Zzz.py b/gen/test/applications/Zzz.py deleted file mode 100644 index a1bea19..0000000 --- a/gen/test/applications/Zzz.py +++ /dev/null @@ -1,67 +0,0 @@ -from appy.gen import * -class Zzz: - root = True - def show_f2(self): return True - def validate_i2(self, value): - if (value != None) and (value < 10): - return 'Value must be higher or equal to 10.' - return True - title=String(multiplicity=(0,1), show=False) - i1 = Integer(show=False) - i2 = Integer(validator = validate_i2) - f1 = Float(show=show_f2, page='other') - f2 = Float(multiplicity=(1,1)) - -class SeveralStrings: - root=True - anEmail = String(validator=String.EMAIL) - anUrl = String(validator=String.URL) - anAlphanumericValue = String(validator=String.ALPHANUMERIC) - aSingleSelectedValue = String(validator=['valueA', 'valueB', 'valueC']) - aSingleMandatorySelectedValue = String( - validator=['valueX', 'valueY', 'valueZ'], multiplicity=(1,1)) - aMultipleSelectedValue = String( - validator=['valueS', 'valueT', 'valueU', 'valueV'], - multiplicity=(1,None), searchable=True) - aBooleanValue = Boolean(default=True) - dateWithHour = Date() - dateWithoutHour = Date(format=Date.WITHOUT_HOUR) - anAttachedFile = File() - anAttachedImage = File(isImage=True) - -class Product: - root = True - description = String(format=String.TEXT) - stock = Integer() - def needOrder(self): return self.stock < 3 - def orderProduct(self): self.stock = 3 - order = Action(action=orderProduct, show=needOrder) - -class Order: - description = String(format=String.TEXT) - number = Float(show=False) - # Reference field - def getReference(self): return 'OR-%f' % self.number - reference = Computed(method=getReference) - def filterProducts(self, allProducts): - return [f for f in allProducts if f.description.find('Descr') != -1] - products = Ref(Product, add=False, link=True, multiplicity=(1,None), - back=Ref(attribute='orders'), showHeaders=True, - shownInfo=('description','title', 'order'), - select=filterProducts) - def onEdit(self, created): - if created: - import random - self.number = random.random() - -class Client: - root = True - folder = True - title = String(show=False) - firstName = String() - name = String() - orders = Ref(Order, add=True, link=False, multiplicity=(0,None), - back=Ref(attribute='client'), showHeaders=True, - shownInfo=('reference', 'description', 'products'), wide=True) - def onEdit(self, created): - self.title = self.firstName + ' ' + self.name diff --git a/gen/ui/portlet.pt b/gen/ui/portlet.pt index 7384b7b..d81795c 100644 --- a/gen/ui/portlet.pt +++ b/gen/ui/portlet.pt @@ -26,9 +26,10 @@
-
- Section title, with action icons - + Section title (link triggers the default search), with action icons + @@ -56,41 +57,41 @@ Searches for this content type. - - - - Group name -
- - -
- Group searches -
-
- -
-
-
-
-
- - -
+ + + + Group name +
+ + +
+ Group searches +
+
+ +
+
+
+
+
+ +
- +
+ diff --git a/gen/ui/widgets/float.pt b/gen/ui/widgets/float.pt index e06b87f..2586199 100644 --- a/gen/ui/widgets/float.pt +++ b/gen/ui/widgets/float.pt @@ -23,10 +23,14 @@
   - + - +
diff --git a/gen/ui/widgets/integer.pt b/gen/ui/widgets/integer.pt index 9ff09c8..b39e7e2 100644 --- a/gen/ui/widgets/integer.pt +++ b/gen/ui/widgets/integer.pt @@ -23,10 +23,14 @@
   - + - +
diff --git a/gen/ui/widgets/string.pt b/gen/ui/widgets/string.pt index 70f2020..1544719 100644 --- a/gen/ui/widgets/string.pt +++ b/gen/ui/widgets/string.pt @@ -89,7 +89,8 @@ + style python: 'text-transform:%s' % widget['transform']; + value widget/defaultForSearch"/> Show a multi-selection box for fields whose validator defines a list of values, with a "AND/OR" checkbox. @@ -105,9 +106,11 @@
The list of values - diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index c81a44d..5de6021 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -83,16 +83,9 @@ class ToolWrapper(AbstractWrapper): '''Some names of Tool attributes are not easy to guess. For example, the attribute that stores the names of the columns to display in query results for class A that is in package x.y is - "tool.resultColumnsForx_y_A". Other example: the attribute that - stores the editable default value of field "f1" of class x.y.A is - "tool.defaultValueForx_y_A_f1". This method generates the attribute + "tool.resultColumnsForx_y_A". This method generates the attribute name based on p_attributeType, a p_klass from the application, and a - p_attrName (given only if needed, for example if p_attributeType is - "defaultValue"). p_attributeType may be: - - "defaultValue" - Stores the editable default value for a given p_attrName of a - given p_klass. + p_attrName (given only if needed). p_attributeType may be: "podTemplate" Stores the pod template for p_attrName. @@ -117,10 +110,6 @@ class ToolWrapper(AbstractWrapper): Determines, among all indexed fields for p_klass, which one will really be used in the search screen. - "optionalFields" - Stores the list of optional attributes that are in use in the - tool for the given p_klass. - "showWorkflow" Stores the boolean field indicating if we must show workflow- related information for p_klass or not.