From 7d605d1fbb650e9f7acdcf9e02f9c79f3b891ac8 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Thu, 1 Mar 2012 17:35:23 +0100 Subject: [PATCH] appy.gen: bugfixes in the List field. --- gen/__init__.py | 69 ++++++++++++++++++++++++++++------------- gen/mixins/ToolMixin.py | 2 +- gen/mixins/__init__.py | 9 ++++-- gen/ui/appy.css | 14 ++++----- gen/ui/appy.js | 54 +++++++++++++++++++++++++------- gen/ui/edit.pt | 2 +- gen/ui/search.pt | 2 +- gen/ui/template.pt | 2 +- 8 files changed, 107 insertions(+), 47 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index e84af8f..f7f3091 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -583,7 +583,6 @@ class Type: else: master, masterValue = masterData reqValue = master.getRequestValue(obj.REQUEST) - reqValue = master.getStorableValue(reqValue) # reqValue can be a list or not if type(reqValue) not in sequenceTypes: return reqValue in masterValue @@ -783,10 +782,21 @@ class Type: return res return value - def getRequestValue(self, request): - '''Gets the string (or list of strings if multi-valued) - representation of this field as found in the p_request.''' - return request.get(self.name, None) + def getRequestValue(self, request, requestName=None): + '''Gets a value for this field as carried in the request object. In the + simplest cases, the request value is a single value whose name in the + request is the name of the field. + + Sometimes (ie: a Date: see the overriden method in the Date class), + several request values must be combined. + + Sometimes (ie, a field which is a sub-field in a List), the name of + the request value(s) representing the field value do not correspond + to the field name (ie: the request name includes information about + the container field). In this case, p_requestName must be used for + searching into the request, instead of the field name (self.name).''' + name = requestName or self.name + return request.get(name, None) def getStorableValue(self, value): '''p_value is a valid value initially computed through calling @@ -1474,11 +1484,12 @@ class Date(Type): res += ' %s' % value.strftime('%H:%M') return res - def getRequestValue(self, request): + def getRequestValue(self, request, requestName=None): + name = requestName or self.name # Manage the "date" part value = '' for part in self.dateParts: - valuePart = request.get('%s_%s' % (self.name, part), None) + valuePart = request.get('%s_%s' % (name, part), None) if not valuePart: return None value += valuePart + '/' value = value[:-1] @@ -1486,7 +1497,7 @@ class Date(Type): if self.format == self.WITH_HOUR: value += ' ' for part in self.hourParts: - valuePart = request.get('%s_%s' % (self.name, part), None) + valuePart = request.get('%s_%s' % (name, part), None) if not valuePart: return None value += valuePart + ':' value = value[:-1] @@ -1544,8 +1555,9 @@ class File(Type): if not value: return value return value._zopeFile - def getRequestValue(self, request): - return request.get('%s_file' % self.name) + def getRequestValue(self, request, requestName=None): + name = requestName or self.name + return request.get('%s_file' % name) def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'} @@ -2251,25 +2263,22 @@ class List(Type): for n, field in self.fields: if n == name: return field - def getRequestValue(self, request): + def getRequestValue(self, request, requestName=None): '''Concatenates the list from distinct form elements in the request.''' - prefix = self.name + '*' + self.fields[0][0] + '*' + name = requestName or self.name # A List may be into another List (?) + prefix = name + '*' + self.fields[0][0] + '*' res = {} for key in request.keys(): if not key.startswith(prefix): continue # I have found a row. Gets its index row = Object() + if '_' in key: key = key[:key.index('_')] rowIndex = int(key.split('*')[-1]) if rowIndex == -1: continue # Ignore the template row. - for name, field in self.fields: - keyName = '%s*%s*%s' % (self.name, name, rowIndex) - if request.has_key(keyName): - # Simulate the request as if it was for a single value - request.set(field.name, request[keyName]) - v = field.getRequestValue(request) - else: - v = None - setattr(row, name, v) + for subName, subField in self.fields: + keyName = '%s*%s*%s' % (name, subName, rowIndex) + v = subField.getRequestValue(request, requestName=keyName) + setattr(row, subName, v) res[rowIndex] = row # Produce a sorted list. keys = res.keys() @@ -2280,7 +2289,7 @@ class List(Type): # instead of taking it from the specific request key. Indeed, specific # request keys contain row indexes that may be wrong after row deletions # by the user. - request.set(self.name, res) + request.set(name, res) return res def getStorableValue(self, value): @@ -2301,6 +2310,22 @@ class List(Type): if i >= len(outerValue): return '' return getattr(outerValue[i], name, '') + def getCss(self, layoutType): + '''Gets the CSS required by sub-fields if any.''' + res = () + for name, field in self.fields: + css = field.getCss(layoutType) + if css: res += css + return res + + def getJs(self, layoutType): + '''Gets the JS required by sub-fields if any.''' + res = () + for name, field in self.fields: + js = field.getJs(layoutType) + if js: res += js + return res + # Workflow-specific types and default workflows -------------------------------- appyToZopePermissions = { 'read': ('View', 'Access contents information'), diff --git a/gen/mixins/ToolMixin.py b/gen/mixins/ToolMixin.py index 7c964cb..db85a04 100644 --- a/gen/mixins/ToolMixin.py +++ b/gen/mixins/ToolMixin.py @@ -121,7 +121,7 @@ class ToolMixin(BaseMixin): p_code.''' return languages.get(code)[2] - def getCssJs(self): + def getGlobalCssJs(self): '''Returns the list of CSS and JS files to include in the main template. The method ensures that appy.css and appy.js come first.''' names = self.getPhysicalRoot().ui.objectIds('File') diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index a199253..9442dd8 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -562,7 +562,10 @@ class BaseMixin: field is supposed to belong to self's class.''' isInnerType = '*' in name # An inner type lies within a List type. subName = None - if isInnerType: name, subName, i = name.split('*') + if isInnerType: + elems = name.split('*') + if len(elems) == 2: name, subName = elems + else: name, subName, i = elems if not className: klass = self.__class__.wrapperClass else: @@ -614,10 +617,10 @@ class BaseMixin: res.append(appyType) return res - def getCssAndJs(self, fields, layoutType): + def getCssJs(self, fields, layoutType): '''Gets the list of Javascript and CSS files required by Appy types p_fields when shown on p_layoutType.''' - # lists css and js below are not sets, because order of Javascript + # Lists css and js below are not sets, because order of Javascript # inclusion can be important, and this could be losed by using sets. css = [] js = [] diff --git a/gen/ui/appy.css b/gen/ui/appy.css index f7321bb..35907a3 100644 --- a/gen/ui/appy.css +++ b/gen/ui/appy.css @@ -12,18 +12,18 @@ acronym {cursor: help;} input[type=image] { border: 0; background: none; } input[type=checkbox] { border: 0; background: none; cursor: pointer;} input[type=radio] { border: 0; background: none; cursor: pointer;} -input[type=file] { border: 0px solid #cccccc; +input[type=file] { border: 0px solid #D7DEE4; background-color: #f8f8f8; cursor: pointer;} -input[type=button] { border: 1px solid #cccccc; +input[type=button] { border: 1px solid #D7DEE4; background-color: #f8f8f8; cursor: pointer;} -input[type=submit] { border: 1px solid #cccccc; background-color: #f8f8f8; +input[type=submit] { border: 1px solid #D7DEE4; background-color: #f8f8f8; cursor: pointer; } -input[type=password] { border: 1px solid #cccccc; background-color: #f8f8f8; +input[type=password] { border: 1px solid #D7DEE4; background-color: #f8f8f8; font-family: Helvetica,Arial,sans-serif;} -input[type=text] { border: 1px solid #cccccc; background-color: #f8f8f8; +input[type=text] { border: 1px solid #D7DEE4; background-color: #f8f8f8; font-family: Helvetica,Arial,sans-serif; margin-bottom: 1px} -select { border: 1px solid #cccccc; background-color: #f8f8f8;} +select { border: 1px solid #D7DEE4; background-color: #f8f8f8;} textarea { width: 99%; font: 100% Helvetica,Arial,sans-serif; border: 1px solid #a79e9e; background-color: #f8f8f8;} @@ -82,7 +82,7 @@ img {border: 0} .section2 { font-size: 110%; font-style: italic; margin: 0.45em 0em 0.2em 0; border-bottom: 2px solid grey; } .section3 { font-size: 100%; font-style: italic; margin: 0.45em 0em 0.1em 0; - background-color: #efeae8; text-align: center; color: grey; } + background-color: #F4F5F6; text-align: center; color: grey; } .odd { background-color: white; } .even { background-color: #F4F5F6; } .summary {margin-bottom: 5px;} diff --git a/gen/ui/appy.js b/gen/ui/appy.js index 99fdd96..a1ca1ab 100644 --- a/gen/ui/appy.js +++ b/gen/ui/appy.js @@ -502,26 +502,58 @@ function updateRowNumber(row, rowIndex, action) { with new p_rowIndex. If p_action is 'set', p_rowIndex becomes the new index. If p_action is 'add', new index becomes: existing index + p_rowIndex. */ - tagTypes = ['input', 'select']; - currentIndex = -1; + var tagTypes = ['input', 'select', 'img']; + var currentIndex = -1; for (var i=0; i < tagTypes.length; i++) { - widgets = row.getElementsByTagName(tagTypes[i]); + var widgets = row.getElementsByTagName(tagTypes[i]); for (var j=0; j < widgets.length; j++) { - id = widgets[j].id; - name = widgets[j].name; + var id = widgets[j].id; + if (!id) continue; + var name = widgets[j].name; + // Extract the suffix if there is one (ie, if the field is a Date part: + // _img, _day,...). + var iSuffix = id.lastIndexOf('_'); + var idSuffix = ''; + if (iSuffix != -1) { + idSuffix = id.substring(iSuffix); + id = id.substring(0, iSuffix); + } + var nSuffix = name.lastIndexOf('_'); + var nameSuffix = ''; + if (nSuffix != -1) { + nameSuffix = id.substring(nSuffix); + name = name.substring(0, nSuffix); + } + // Compute the current row index if not already done. idNbIndex = id.lastIndexOf('*') + 1; nameNbIndex = name.lastIndexOf('*') + 1; - // Compute the current row index if not already done. if (currentIndex == -1) { currentIndex = parseInt(id.substring(idNbIndex)); } // Compute the new values for attributes "id" and "name". newId = id.substring(0, idNbIndex); - newName = id.substring(0, nameNbIndex); + newName = name.substring(0, nameNbIndex); newIndex = rowIndex; if (action == 'add') newIndex = newIndex + currentIndex; - widgets[j].id = newId + String(newIndex); - widgets[j].name = newName + String(newIndex); + var oldId = widgets[j].id; + widgets[j].id = newId + String(newIndex) + idSuffix; + if (name) widgets[j].name = newName + String(newIndex) + nameSuffix; + /* In the case of an img that must show a calendar, update the script that + is triggered when clicking on it. */ + if ((tagTypes[i] == 'img') && (idSuffix == '_img')) { + var scripts = row.getElementsByTagName('script'); + for (var k=0; k < scripts.length; k++) { + var text = scripts[k].text; + if (text.indexOf(oldId) != -1) { + var oldIdField = oldId.substring(0, oldId.length-4); + var newIdField = widgets[j].id.substring(0, widgets[j].id.length-4); + text = text.replace(oldIdField, newIdField); + scripts[k].text = text.replace(oldId, widgets[j].id); + eval(scripts[k].text); + break; + } + } + } } } } @@ -530,9 +562,9 @@ function insertRow(tableId) { table = document.getElementById(tableId); newRow = table.rows[1].cloneNode(true); newRow.style.display = 'table-row'; - // Within newRow, I must include in field names and ids the row number - updateRowNumber(newRow, table.rows.length-3, 'set'); + // Within newRow, incorporate the row number within field names and ids. table.tBodies[0].appendChild(newRow); + updateRowNumber(newRow, table.rows.length-4, 'set'); } function deleteRow(tableId, deleteImg) { diff --git a/gen/ui/edit.pt b/gen/ui/edit.pt index b5560d3..9d579bd 100644 --- a/gen/ui/edit.pt +++ b/gen/ui/edit.pt @@ -9,7 +9,7 @@ phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType=layoutType); phase phaseInfo/name; page request/page|python:'main'; - cssJs python: contextObj.getCssAndJs(contextObj.getAppyTypes(layoutType, page), layoutType); + cssJs python: contextObj.getCssJs(contextObj.getAppyTypes(layoutType, page), layoutType); confirmMsg request/confirmMsg | nothing;" tal:on-error="structure python: tool.manageError(error)"> diff --git a/gen/ui/search.pt b/gen/ui/search.pt index 065eeab..9d91287 100644 --- a/gen/ui/search.pt +++ b/gen/ui/search.pt @@ -4,7 +4,7 @@ tal:define="className request/className; refInfo request/ref|nothing; searchInfo python: tool.getSearchInfo(className, refInfo); - cssJs python: tool.getCssAndJs(searchInfo['fields'], 'edit')"> + cssJs python: tool.getCssJs(searchInfo['fields'], 'edit')"> Include type-specific CSS and JS. - +