From 0dae8b18880c1107241e35d6dc49a8d4a77541f7 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Fri, 14 Dec 2012 09:23:33 +0100 Subject: [PATCH] [gen] Added the possibility to span widgets on several columns on the search screen (param Field.scolspan). --- gen/__init__.py | 62 ++++++++++++++++++++------------------ gen/calendar.py | 2 +- gen/mixins/ToolMixin.py | 39 +++++++++++++++++------- gen/ogone.py | 2 +- gen/ui/search.pt | 12 +++++--- gen/ui/widgets/computed.pt | 4 +-- gen/ui/widgets/float.pt | 4 +-- gen/ui/widgets/integer.pt | 4 +-- gen/ui/widgets/string.pt | 6 ++-- 9 files changed, 79 insertions(+), 56 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index 013fccb..35da42b 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -379,7 +379,7 @@ class Type: layouts, move, indexed, searchable, specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, sync, mapping, label, - defaultForSearch): + sdefault, scolspan): # 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. @@ -473,10 +473,12 @@ 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 + # When you specify a default value "for search" (= "sdefault"), on a + # search screen, in the search field corresponding to this field, this + # default value will be present. + self.sdefault = sdefault + # Colspan for rendering the search widget corresponding to this field. + self.scolspan = scolspan def init(self, name, klass, appName): '''When the application server starts, this secondary constructor is @@ -952,12 +954,12 @@ class Integer(Type): specificWritePermission=False, width=6, height=None, maxChars=13, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - defaultForSearch=('','')): + sdefault=('',''), scolspan=1): 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, defaultForSearch) + label, sdefault, scolspan) self.pythonType = long def validateValue(self, obj, value): @@ -982,7 +984,7 @@ class Float(Type): specificWritePermission=False, width=6, height=None, maxChars=13, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - defaultForSearch=('',''), precision=None, sep=(',', '.'), + sdefault=('',''), scolspan=1, 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 @@ -1004,7 +1006,7 @@ class Float(Type): layouts, move, indexed, False, specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, True, mapping, - label, defaultForSearch) + label, sdefault, scolspan) self.pythonType = float def getFormattedValue(self, obj, value): @@ -1135,9 +1137,9 @@ class String(Type): 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): + label=None, sdefault='', scolspan=1, + 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 @@ -1164,11 +1166,11 @@ class String(Type): layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, maxChars, colspan, master, masterValue, focus, historized, True, mapping, - label, defaultForSearch) + label, sdefault, scolspan) self.isSelect = self.isSelection() - # If self.isSelect, self.defaultForSearch must be a list of value(s). - if self.isSelect and not defaultForSearch: - self.defaultForSearch = [] + # If self.isSelect, self.sdefault must be a list of value(s). + if self.isSelect and not sdefault: + self.sdefault = [] # Default width, height and maxChars vary according to String format if width == None: if format == String.TEXT: self.width = 60 @@ -1446,12 +1448,12 @@ class Boolean(Type): specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - defaultForSearch=False): + sdefault=False, scolspan=1): 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, defaultForSearch) + label, sdefault, scolspan) self.pythonType = bool dLayouts = {'view': 'lf', 'edit': Table('flrv;=d', width=None)} @@ -1495,7 +1497,7 @@ class Date(Type): specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - defaultForSearch=None): + sdefault=None, scolspan=1): self.format = format self.calendar = calendar self.startYear = startYear @@ -1507,7 +1509,7 @@ class Date(Type): layouts, move, indexed, searchable,specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, True, mapping, - label, defaultForSearch) + label, sdefault, scolspan) def getCss(self, layoutType, res): # CSS files are only required if the calendar must be shown. @@ -1574,13 +1576,13 @@ class File(Type): specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, master=None, masterValue=None, focus=False, historized=False, mapping=None, label=None, - isImage=False, defaultForSearch=''): + isImage=False, sdefault='', scolspan=1): self.isImage = isImage 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) + label, sdefault, scolspan) @staticmethod def getFileObject(filePath, fileName=None, zope=False): @@ -1735,7 +1737,7 @@ class Ref(Type): masterValue=None, focus=False, historized=False, mapping=None, label=None, queryable=False, queryFields=None, queryNbCols=1, navigable=False, searchSelect=None, changeOrder=True, - defaultForSearch=''): + sdefault='', scolspan=1): self.klass = klass self.attribute = attribute # May the user add new objects through this ref ? @@ -1815,7 +1817,7 @@ class Ref(Type): layouts, move, indexed, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, sync, mapping, - label, defaultForSearch) + label, sdefault, scolspan) self.validable = self.link def getDefaultLayouts(self): @@ -2078,7 +2080,7 @@ class Computed(Type): specificWritePermission=False, width=None, height=None, maxChars=None, colspan=1, method=None, plainText=True, master=None, masterValue=None, focus=False, historized=False, - sync=True, mapping=None, label=None, defaultForSearch='', + sync=True, mapping=None, label=None, sdefault='', scolspan=1, context={}): # The Python method used for computing the field value self.method = method @@ -2096,7 +2098,7 @@ class Computed(Type): layouts, move, indexed, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, sync, mapping, - label, defaultForSearch) + label, sdefault, scolspan) self.validable = False def callMacro(self, obj, macroPath): @@ -2166,7 +2168,7 @@ class Action(Type): move, indexed, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, False, mapping, - label, None) + label, None, None) self.validable = False def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'} @@ -2214,7 +2216,7 @@ class Info(Type): move, indexed, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, False, mapping, - label, None) + label, None, None) self.validable = False class Pod(Type): @@ -2253,7 +2255,7 @@ class Pod(Type): move, indexed, searchable, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, False, mapping, - label, None) + label, None, None) self.validable = False def isFrozen(self, obj): @@ -2397,7 +2399,7 @@ class List(Type): layouts, move, indexed, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, historized, True, mapping, - label, None) + label, None, None) self.validable = True # Tuples of (names, Type instances) determining the format of every # element in the list. diff --git a/gen/calendar.py b/gen/calendar.py index b68181d..d842fd6 100644 --- a/gen/calendar.py +++ b/gen/calendar.py @@ -25,7 +25,7 @@ class Calendar(Type): layouts, move, False, False, specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, False, True, mapping, label, - None) + None, 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/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 90a59f8..5a11728 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -870,21 +870,35 @@ class ToolMixin(BaseMixin): page=self.REQUEST.get('page', 'main')) return res - def tabularize(self, data, numberOfRows): - '''This method transforms p_data, which must be a "flat" list or tuple, - into a list of lists, where every sub-list has length p_numberOfRows. - This method is typically used for rendering elements in a table of - p_numberOfRows rows.''' + def getGroupedSearchFields(self, searchInfo): + '''This method transforms p_searchInfo['fieldDicts'], which is a "flat" + list of fields, into a list of lists, where every sub-list having + length p_searchInfo['nbOfColumns']. For every field, scolspan + (=colspan "for search") is taken into account.''' res = [] row = [] - for elem in data: - row.append(elem) - if len(row) == numberOfRows: + rowLength = 0 + for field in searchInfo['fieldDicts']: + # Can I insert this field in the current row? + remaining = searchInfo['nbOfColumns'] - rowLength + if field['scolspan'] <= remaining: + # Yes. + row.append(field) + rowLength += field['scolspan'] + else: + # We must put the field on a new line. Complete the current one + # if not complete. + while rowLength < searchInfo['nbOfColumns']: + row.append(None) + rowLength += 1 res.append(row) - row = [] + row = [field] + rowLength = field['scolspan'] # Complete the last unfinished line if required. if row: - while len(row) < numberOfRows: row.append(None) + while rowLength < searchInfo['nbOfColumns']: + row.append(None) + rowLength += 1 res.append(row) return res @@ -1171,6 +1185,9 @@ class ToolMixin(BaseMixin): klass = self.getAppyClass(className, wrapper=True) method = getattr(klass, name).searchSelect tool = self.appy() - objects = method.__get__(tool)(tool) + if method.__class__.__name__ == 'function': + objects = method(tool) + else: + objects = method.__get__(tool)(tool) return [(o.uid, o) for o in objects] # ------------------------------------------------------------------------------ diff --git a/gen/ogone.py b/gen/ogone.py index 2dc32bc..c20adde 100644 --- a/gen/ogone.py +++ b/gen/ogone.py @@ -39,7 +39,7 @@ class Ogone(Type): False, False,specificReadPermission, specificWritePermission, width, height, None, colspan, master, masterValue, focus, False, True, mapping, label, - None) + None, 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/ui/search.pt b/gen/ui/search.pt index fed0d57..2948a35 100644 --- a/gen/ui/search.pt +++ b/gen/ui/search.pt @@ -24,17 +24,21 @@ - - + +
- + + +
-
diff --git a/gen/ui/widgets/computed.pt b/gen/ui/widgets/computed.pt index 7a65f74..94bcf3b 100644 --- a/gen/ui/widgets/computed.pt +++ b/gen/ui/widgets/computed.pt @@ -38,6 +38,6 @@
   + maxlength maxChars; size widget/width; + value widget/sdefault"/> diff --git a/gen/ui/widgets/float.pt b/gen/ui/widgets/float.pt index 2586199..7a631f1 100644 --- a/gen/ui/widgets/float.pt +++ b/gen/ui/widgets/float.pt @@ -25,12 +25,12 @@ + value python: widget['sdefault'][0]"/> + value python: widget['sdefault'][1]"/>
diff --git a/gen/ui/widgets/integer.pt b/gen/ui/widgets/integer.pt index b39e7e2..b6c37c3 100644 --- a/gen/ui/widgets/integer.pt +++ b/gen/ui/widgets/integer.pt @@ -25,12 +25,12 @@ + value python: widget['sdefault'][0]"/> + value python: widget['sdefault'][1]"/>
diff --git a/gen/ui/widgets/string.pt b/gen/ui/widgets/string.pt index 970946a..58bb1a8 100644 --- a/gen/ui/widgets/string.pt +++ b/gen/ui/widgets/string.pt @@ -94,9 +94,9 @@ + value widget/sdefault"/> Show a multi-selection box for fields whose validator defines a list of values, with a "AND/OR" checkbox. @@ -112,7 +112,7 @@
The list of values -