diff --git a/gen/__init__.py b/gen/__init__.py index d7040f6..d9d7146 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -22,6 +22,13 @@ validatorTypes = (types.FunctionType, types.UnboundMethodType, emptyTuple = () labelTypes = ('label', 'descr', 'help') +def initMasterValue(v): + '''Standardizes p_v as a list.''' + if not v: res = [] + elif type(v) not in sequenceTypes: res = [v] + else: res = v + return res + # Descriptor classes used for refining descriptions of elements in types # (pages, groups,...) ---------------------------------------------------------- class Page: @@ -153,26 +160,10 @@ class Group: # Header labels will be used as labels for the tabs. self.hasHeaders = True self.css_class = css_class - self.master = None - self.masterValue = None - if master: - self._addMaster(master, masterValue) - self.label = label # See similar attr of Type class. - - def _addMaster(self, master, masterValue): - '''Specifies this group being a slave of another field: we will add css - classes allowing to show/hide, in Javascript, its widget according - to master value.''' self.master = master - self.masterValue = masterValue - classes = 'slave_%s' % self.master.id - if type(self.masterValue) not in sequenceTypes: - masterValues = [self.masterValue] - else: - masterValues = self.masterValue - for masterValue in masterValues: - classes += ' slaveValue_%s_%s' % (self.master.id, masterValue) - self.css_class += ' ' + classes + self.masterValue = initMasterValue(masterValue) + if master: master.slaves.append(self) + self.label = label # See similar attr of Type class. def _setColumns(self): '''Standardizes field "columns" as a list of Column instances. Indeed, @@ -440,17 +431,14 @@ class Type: # If the widget is in a group with multiple columns, the following # attribute specifies on how many columns to span the widget. self.colspan = colspan + # The list of slaves of this field, if it is a master + self.slaves = [] # The behaviour of this field may depend on another, "master" field self.master = master - self.slaves = [] # The list of slaves of this field, if it is a master - # Every HTML input field corresponding to a master must get some - # CSS classes for controlling its slaves. - self.master_css = '' if master: self.master.slaves.append(self) - self.master.master_css = 'appyMaster master_%s' % self.master.id # When master has some value(s), there is impact on this field. - self.masterValue = masterValue + self.masterValue = initMasterValue(masterValue) # If a field must retain attention in a particular way, set focus=True. # It will be rendered in a special way. self.focus = focus @@ -494,7 +482,8 @@ class Type: # Recompute the ID (and derived attributes) that may have changed if # we are in debug mode (because we recreate new Type instances). self.id = id(self) - if self.slaves: self.master_css = 'appyMaster master_%s' % self.id + # Remember master name on every slave + for slave in self.slaves: slave.masterName = name # Determine ids of i18n labels for this field labelName = name trPrefix = None @@ -591,13 +580,10 @@ class Type: master, masterValue = masterData reqValue = master.getRequestValue(obj.REQUEST) reqValue = master.getStorableValue(reqValue) - # Manage the fact that values can be lists or single values - multiMaster = type(masterValue) in sequenceTypes - multiReq = type(reqValue) in sequenceTypes - if not multiMaster and not multiReq: return reqValue == masterValue - elif multiMaster and not multiReq: return reqValue in masterValue - elif not multiMaster and multiReq: return masterValue in reqValue - else: # multiMaster and multiReq + # reqValue can be a list or not + if type(reqValue) not in sequenceTypes: + return reqValue in masterValue + else: for m in masterValue: for r in reqValue: if m == r: return True @@ -674,19 +660,6 @@ class Type: layouts['cell'] = Table(other=layouts['view'], derivedType='cell') # Put the required CSS classes in the layouts layouts['cell'].addCssClasses('noStyle') - if self.master: - # This type has a master (so is a slave): we add css classes - # allowing to show/hide, in Javascript, its widget according to - # master value. - classes = 'slave_%s' % self.master.id - if type(self.masterValue) not in sequenceTypes: - masterValues = [self.masterValue] - else: - masterValues = self.masterValue - for masterValue in masterValues: - classes += ' slaveValue_%s_%s' % (self.master.id, masterValue) - layouts['view'].addCssClasses(classes) - layouts['edit'].addCssClasses(classes) if self.focus: # We need to make it flashy layouts['view'].addCssClasses('appyFocus') diff --git a/gen/plone25/mixins/ToolMixin.py b/gen/plone25/mixins/ToolMixin.py index 39ffed1..cfc4b2d 100644 --- a/gen/plone25/mixins/ToolMixin.py +++ b/gen/plone25/mixins/ToolMixin.py @@ -1,5 +1,5 @@ # ------------------------------------------------------------------------------ -import re, os, os.path, time, Cookie, types +import re, os, os.path, time, types from appy.shared import mimeTypes from appy.shared.utils import getOsTempFolder import appy.gen @@ -84,7 +84,6 @@ class ToolMixin(BaseMixin): '''Returns the (translated) names of fields of p_contentType.''' res = [] for appyType in self.getAllAppyTypes(className=contentType): - if appyType.name == 'title': continue # Will be included by default. res.append((appyType.name, self.translate(appyType.labelId))) # Add object state res.append(('state', self.translate('workflow_state'))) @@ -611,13 +610,6 @@ class ToolMixin(BaseMixin): res.append(dSearch) return res - def getCookieValue(self, cookieId, default=''): - '''Server-side code for getting the value of a cookie entry.''' - cookie = Cookie.SimpleCookie(self.REQUEST['HTTP_COOKIE']) - cookieValue = cookie.get(cookieId) - if cookieValue: return cookieValue.value - return default - def getQueryUrl(self, contentType, searchName, startNumber=None): '''This method creates the URL that allows to perform a (non-Ajax) request for getting queried objects from a search named p_searchName diff --git a/gen/plone25/model.py b/gen/plone25/model.py index c37e05b..4acd667 100644 --- a/gen/plone25/model.py +++ b/gen/plone25/model.py @@ -14,9 +14,9 @@ class Protos: protos = {} # List of attributes that can't be given to a Type constructor notInit = ('id', 'type', 'pythonType', 'slaves', 'isSelect', 'hasLabel', - 'hasDescr', 'hasHelp', 'master_css', 'required', 'filterable', - 'validable', 'backd', 'isBack', 'sync', 'pageName', - 'shownInfoWidths') + 'hasDescr', 'hasHelp', 'required', 'filterable', 'validable', + 'backd', 'isBack', 'sync', 'pageName', 'shownInfoWidths', + 'masterName') @classmethod def get(self, appyType): '''Returns a prototype instance for p_appyType.''' diff --git a/gen/plone25/skin/appy.css b/gen/plone25/skin/appy.css index 7d4edb3..e02e318 100644 --- a/gen/plone25/skin/appy.css +++ b/gen/plone25/skin/appy.css @@ -8,18 +8,26 @@ table { font-size: 100%; border-spacing: 0px; border-collapse:collapse;} form { margin: 0; padding: 0;} p { margin: 0;} acronym {cursor: help;} -input { border: 1px solid #cccccc; background-color: #f8f8f8; - font-family: Lucida,Helvetica,Arial,sans-serif; margin-bottom: 1px} -input[type=image] { border-width: 0px; background: none; } -input[type=button] { cursor: pointer; } +input[type=image] { border: 0; background: none; } +input[type=checkbox] { border: 0; background: none; cursor: pointer;} +input[type=button] { border: 1px solid #cccccc; background-color: #f8f8f8; + cursor: pointer;} +input[type=submit] { border: 1px solid #cccccc; background-color: #f8f8f8; + cursor: pointer; } +input[type=text] { border: 1px solid #cccccc; background-color: #f8f8f8; + font-family: Lucida,Helvetica,Arial,sans-serif; + margin-bottom: 1px} +select { border: 1px solid #cccccc; background-color: #f8f8f8;} + textarea { width: 99%; font: 100% Lucida,Helvetica,Arial,sans-serif; border: 1px solid #a79e9e; background-color: #f8f8f8;} label { font-weight: 600; font-style: italic; line-height: 1.4em;} -legend { padding-bottom: 2px; padding-right: 3px;} +legend { padding-bottom: 2px; padding-right: 3px; color: black;} ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0; list-style: none outside none;} li { margin: 0; background-image: url("skyn/li.gif"); padding-left: 10px; background-repeat: no-repeat; background-position: 0 4px;} +img {border: 0;} .main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9; border-style: solid; border-width: 1px; border-color: grey; } diff --git a/gen/plone25/skin/appy.js b/gen/plone25/skin/appy.js index 2a04f9a..5ad6094 100644 --- a/gen/plone25/skin/appy.js +++ b/gen/plone25/skin/appy.js @@ -177,8 +177,8 @@ function askRefField(hookId, objectUrl, fieldName, innerRef, startNumber, action, actionParams){ // Sends an Ajax request for getting the content of a reference field. var startKey = hookId + '_startNumber'; - var params = {'fieldName': fieldName, 'innerRef': innerRef, }; - params[startKey] = startNumber; + var params = {'fieldName': fieldName, 'innerRef': innerRef }; + params[startKey] = startNumber; if (action) params['action'] = action; if (actionParams) { for (key in actionParams) { params[key] = actionParams[key]; }; @@ -201,70 +201,94 @@ function toggleCheckbox(visibleCheckbox, hiddenBoolean) { } // Functions used for master/slave relationships between widgets -function getMasterValue(widget) { - // Returns an array of selected options in a select widget - res = new Array(); - if (widget.type == 'checkbox') { - var mv = widget.checked + ''; - mv = mv.charAt(0).toUpperCase() + mv.substr(1); - res.push(mv); +function getSlaveInfo(slave, infoType) { + // Returns the appropriate info about slavery, depending on p_infoType. + cssClasses = slave.className.split(' '); + // Find the CSS class containing master-related info. + for (var j=0; j < cssClasses.length; j++) { + if (cssClasses[j].indexOf('slave_') == 0) { + // Extract, from this CSS class, master name or master values. + masterInfo = cssClasses[j].split('_'); + if (infoType == 'masterName') return masterInfo[1]; + else return masterInfo.slice(2); // Master values + } + } +} + +function getMasterValues(master) { + // Returns the list of values that p_master currently has. + if (master.tagName == 'SPAN') { + res = master.attributes['value'].value; + if ((res[0] == '(') || (res[0] == '[')) { + // There are multiple values, split it + values = res.substring(1, res.length-1).split(','); + res = []; + for (var i=0; i < values.length; i++){ + v = values[i].replace(' ', ''); + res.push(v.substring(1, v.length-1)); + } + } + else res = [res]; // A single value + } + else if (master.type == 'checkbox') { + res = master.checked + ''; + res = res.charAt(0).toUpperCase() + res.substr(1); + res = [res]; } else { // SELECT widget - for (var i=0; i < widget.options.length; i++) { - if (widget.options[i].selected) res.push(widget.options[i].value); + res = []; + for (var i=0; i < master.options.length; i++) { + if (master.options[i].selected) res.push(master.options[i].value); } } return res; } -function updateSlaves(masterValues, appyTypeId) { - // Given the value(s) selected in a master field, this function updates the - // state of all corresponding slaves. - var slaves = cssQuery('table.slave_' + appyTypeId); - for (var i=0; i< slaves.length; i++){ - slaves[i].style.display = "none"; - } - for (var i=0; i < masterValues.length; i++) { - var activeSlaves = cssQuery('table.slaveValue_' + appyTypeId + '_' + masterValues[i]); - for (var j=0; j < activeSlaves.length; j++){ - activeSlaves[j].style.display = ""; +function getSlaves(master) { + // Gets all the slaves of master. + allSlaves = document.getElementsByName('slave'); + res = []; + slavePrefix = 'slave_' + master.attributes['name'].value + '_'; + for (var i=0; i < slaves.length; i++){ + cssClasses = slaves[i].className.split(' '); + for (var j=0; j < cssClasses.length; j++) { + if (cssClasses[j].indexOf(slavePrefix) == 0) { + res.push(slaves[i]); + break; + } } } + return res; +} + +function updateSlaves(master) { + // Given the value(s) in a master field, we must update slave's visibility. + slaves = getSlaves(master); + masterValues = getMasterValues(master); + for (var i=0; i < slaves.length; i++) { + showSlave = false; + slaveryValues = getSlaveInfo(slaves[i], 'masterValues'); + for (var j=0; j < slaveryValues.length; j++) { + for (var k=0; k< masterValues.length; k++) { + if (slaveryValues[j] == masterValues[k]) showSlave = true; + } + } + if (showSlave) slaves[i].style.display = ""; + else slaves[i].style.display = "none"; + } } function initSlaves() { // When the current page is loaded, we must set the correct state for all // slave fields. - var masters = cssQuery('.appyMaster'); - for (var i=0; i < masters.length; i++) { - var cssClasses = masters[i].className.split(' '); - for (var j=0; j < cssClasses.length; j++) { - if (cssClasses[j].indexOf('master_') == 0) { - var appyId = cssClasses[j].split('_')[1]; - var masterValue = []; - if (masters[i].nodeName == 'SPAN'){ - var idField = masters[i].id; - if (idField == '') { - masterValue.push(idField); - } - else { - if ((idField[0] == '(') || (idField[0] == '[')) { - // There are multiple values, split it - var subValues = idField.substring(1, idField.length-1).split(','); - for (var k=0; k < subValues.length; k++){ - var subValue = subValues[k].replace(' ',''); - masterValue.push(subValue.substring(1, subValue.length-1)); - } - } - else { masterValue.push(masters[i].id); - } - } - } - else { masterValue = getMasterValue(masters[i]); - } - updateSlaves(masterValue, appyId); - } - } + slaves = document.getElementsByName('slave'); + walkedMasters = {}; // Remember the already walked masters. + for (var i=0; i < slaves.length; i++) { + masterName = getSlaveInfo(slaves[i], 'masterName'); + if (masterName in walkedMasters) continue; + master = document.getElementById(masterName); + updateSlaves(master); + walkedMasters[masterName] = 'walked'; } } diff --git a/gen/plone25/skin/import.pt b/gen/plone25/skin/import.pt index afff3f5..bb91d41 100644 --- a/gen/plone25/skin/import.pt +++ b/gen/plone25/skin/import.pt @@ -24,7 +24,7 @@ Form for importing several meetings at once. @@ -98,9 +94,10 @@ + tal:attributes="id python: alreadyImported and 'importedElem' or 'notImportedElem'; + name python: alreadyImported and 'importedElem' or 'notImportedElem'; + style python: alreadyImported and 'display:none' or 'display:table-row'; + class python: odd and 'even' or 'odd'"> @@ -109,8 +106,10 @@ value python: tool.translate('query_import')"/> - + + + diff --git a/gen/plone25/skin/page.pt b/gen/plone25/skin/page.pt index 71dea09..6d4032f 100644 --- a/gen/plone25/skin/page.pt +++ b/gen/plone25/skin/page.pt @@ -187,7 +187,7 @@ tal:define="showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type); hasHistory contextObj/hasHistory; historyMaxPerPage options/maxPerPage|python: 5; - historyExpanded python: tool.getCookieValue('appyHistory', default='collapsed') == 'expanded'; + historyExpanded python: request.get('appyHistory', 'collapsed') == 'expanded'; creator contextObj/Creator" tal:condition="not: contextObj/isTemporary"> diff --git a/gen/plone25/skin/portlet.pt b/gen/plone25/skin/portlet.pt index ffe3541..e4f84d8 100644 --- a/gen/plone25/skin/portlet.pt +++ b/gen/plone25/skin/portlet.pt @@ -56,7 +56,7 @@ + expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'"> Group name
Query result
- diff --git a/gen/plone25/skin/widgets/boolean.pt b/gen/plone25/skin/widgets/boolean.pt index b486886..893c6d9 100644 --- a/gen/plone25/skin/widgets/boolean.pt +++ b/gen/plone25/skin/widgets/boolean.pt @@ -1,6 +1,7 @@ View macro for a Boolean. - + Edit macro for an Boolean. @@ -9,8 +10,8 @@ tal:attributes="name python: name + '_visible'; id name; checked python:contextObj.checkboxChecked(name, rawValue); - onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(getMasterValue(this), \'%s\')' % (name, name, widget['id']); - class python: 'noborder ' + widget['master_css']"/> + onClick python:'toggleCheckbox(\'%s\', \'%s_hidden\');;updateSlaves(this)' % (name, name); + class python: 'noborder %s' % masterCss"/> View macro for a Float. + tal:attributes="value value; class masterCss; name name; id name"> Edit macro for an Float. diff --git a/gen/plone25/skin/widgets/integer.pt b/gen/plone25/skin/widgets/integer.pt index c5e2437..904d575 100644 --- a/gen/plone25/skin/widgets/integer.pt +++ b/gen/plone25/skin/widgets/integer.pt @@ -1,6 +1,7 @@ View macro for an Integer. - + Edit macro for an Integer. diff --git a/gen/plone25/skin/widgets/show.pt b/gen/plone25/skin/widgets/show.pt index dd44798..3ca00d4 100644 --- a/gen/plone25/skin/widgets/show.pt +++ b/gen/plone25/skin/widgets/show.pt @@ -9,15 +9,23 @@ contextMacro The base folder containing the macros to call for rendering the elements within the layout. Defaults to app.skyn + slaveId The name and id of the main tag for this layout (used + for master/slave relationships). + slaveCss The CSS class for a slave. + tal:define="contextMacro contextMacro| python: app.skyn; + slaveId slaveId|python:''; + slaveCss slaveCss|python:''; + layoutCss layout/css_class;"> + class python: slaveCss and ('%s %s' % (slaveCss, layoutCss)) or layoutCss; + style layout/style; + id slaveId; + name slaveId;"> The table header row
+ tal:define="contextMacro python: app.skyn.widgets; + layout python: widget['layouts'][layoutType]; + name widget/name; + sync python: widget['sync'][layoutType]; + rawValue python: contextObj.getFieldValue(name, onlyIfSync=True, layoutType=layoutType); + value python: contextObj.getFormattedFieldValue(name, rawValue); + requestValue python: request.get(name, None); + inRequest python: request.has_key(name); + errors errors | python: (); + inError python: (widget['name'] in errors) and True or False; + isMultiple python: (widget['multiplicity'][1] == None) or (widget['multiplicity'][1] > 1); + masterCss python: widget['slaves'] and ('master_%s' % name) or ''; + slaveCss python: widget['master'] and ('slave_%s_%s' % (widget['masterName'], '_'.join(widget['masterValue']))) or ''; + slaveId python: widget['master'] and 'slave' or ''"> @@ -65,7 +76,11 @@ layoutType "edit"? "view"? "cell?" widget The widget to render - +
@@ -81,9 +96,9 @@ - +
First row: the tabs.
@@ -132,9 +147,10 @@ tal:define="cellgap widget/cellgap" tal:attributes="width widget/wide; align widget/align; - class widget/css_class; + class groupCss; cellspacing widget/cellspacing; - cellpadding widget/cellpadding"> + cellpadding widget/cellpadding; + id slaveId; name slaveId"> Display the title of the group if it is not rendered a fieldset.
+ tal:attributes="class masterCss; value rawValue; name name; id name">
@@ -31,8 +31,8 @@ tal:attributes="name name; id name; multiple python: isMultiple and 'multiple' or ''; - onchange python: isMaster and ('updateSlaves(getMasterValue(this), \'%s\')' % widget['id']) or ''; - class widget/master_css; + onchange python: isMaster and 'updateSlaves(this)' or ''; + class masterCss; size python: isMultiple and widget['height'] or 1">