From c2a3551a94ee01482cae06048642f93e4788d22e Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Fri, 17 Sep 2010 15:32:48 +0200 Subject: [PATCH] Improved and generalized intra- and inter-objects navigation. --- gen/plone25/mixins/ToolMixin.py | 26 ++++----- gen/plone25/mixins/__init__.py | 88 ++++++++++++++++++------------ gen/plone25/skin/import.pt | 8 +-- gen/plone25/skin/macros.pt | 12 ++-- gen/plone25/skin/navigate.pt | 2 +- gen/plone25/skin/page.pt | 23 ++++---- gen/plone25/skin/portlet.pt | 47 ++++++++++------ gen/plone25/skin/widgets/action.pt | 2 +- gen/plone25/skin/widgets/pod.pt | 2 +- gen/plone25/skin/widgets/ref.pt | 15 +++-- gen/plone25/skin/widgets/show.pt | 6 +- gen/plone25/skin/widgets/string.pt | 2 +- 12 files changed, 132 insertions(+), 101 deletions(-) diff --git a/gen/plone25/mixins/ToolMixin.py b/gen/plone25/mixins/ToolMixin.py index 64f9b65..da3332e 100644 --- a/gen/plone25/mixins/ToolMixin.py +++ b/gen/plone25/mixins/ToolMixin.py @@ -285,20 +285,16 @@ class ToolMixin(AbstractMixin): appName = self.getProductConfig().PROJECTNAME return self.utranslate(label, self.translationMapping, domain=appName) - def getPublishedObject(self, rootClasses): + def getPublishedObject(self): '''Gets the currently published object, if its meta_class is among - p_rootClasses or if it is the corresponding tool or flavour.''' + application classes.''' rq = self.REQUEST obj = rq['PUBLISHED'] parent = obj.getParentNode() if parent.id == 'skyn': obj = parent.getParentNode() - if obj.meta_type in rootClasses: + if obj.meta_type in self.getProductConfig().attributes: return obj - else: - appName = self.getAppName() - if obj.meta_type in ('%sTool' % appName, '%sFlavour' % appName): - return obj return None def getAppyClass(self, contentType): @@ -579,8 +575,9 @@ class ToolMixin(AbstractMixin): label = '%s_search_%s' % (d1.split(':')[0], searchName) res['backText'] = self.translate(label) else: + fieldName, pageName = d2.split(':') sourceObj = self.uid_catalog(UID=d1)[0].getObject() - label = '%s_%s' % (sourceObj.meta_type, d2) + label = '%s_%s' % (sourceObj.meta_type, fieldName) res['backText'] = u'%s : %s' % (sourceObj.Title().decode('utf-8'), self.translate(label)) newNav = '%s.%s.%s.%%d.%s' % (t, d1, d2, totalNumber) @@ -600,7 +597,6 @@ class ToolMixin(AbstractMixin): if (nextIndex < lastIndex): lastNeeded = True # Get the list of available UIDs surrounding the current object if t == 'ref': # Manage navigation from a reference - fieldName = d2 masterObj = self.getObject(d1) batchSize = masterObj.getAppyType(fieldName).maxPerPage uids = getattr(masterObj, '_appy_%s' % fieldName) @@ -611,9 +607,9 @@ class ToolMixin(AbstractMixin): startNumberKey = '%s%s_startNumber' % (masterObj.UID(), fieldName) startNumber = self.computeStartNumberFrom(res['currentNumber']-1, res['totalNumber'], batchSize) - res['sourceUrl'] = '%s?%s=%s' % (masterObj.getUrl(), - startNumberKey, startNumber) - else: # Manage navigation from a search + res['sourceUrl'] = masterObj.getUrl(**{startNumberKey:startNumber, + 'page':pageName, 'nav':''}) + else: # Manage navigation from a search contentType, flavourNumber = d1.split(':') flavourNumber = int(flavourNumber) searchName = keySuffix = d2 @@ -661,9 +657,9 @@ class ToolMixin(AbstractMixin): if uid: brain = self.uid_catalog(UID=uid) if brain: - baseUrl = brain[0].getObject().getUrl() - navUrl = baseUrl + '/?nav=' + newNav % (index + 1) - res[urlKey] = navUrl + sibling = brain[0].getObject() + res[urlKey] = sibling.getUrl(nav=newNav % (index + 1), + page='main') return res def tabularize(self, data, numberOfRows): diff --git a/gen/plone25/mixins/__init__.py b/gen/plone25/mixins/__init__.py index fbeede2..186f966 100644 --- a/gen/plone25/mixins/__init__.py +++ b/gen/plone25/mixins/__init__.py @@ -91,7 +91,7 @@ class AbstractMixin: baseUrl = self.absolute_url() objId = self.generateUniqueId(rq.get('type_name')) urlBack = '%s/portal_factory/%s/%s/skyn/edit' % \ - (baseUrl, rq.get('type_name'), objId) + (baseUrl, rq.get('type_name'), objId) return self.goto(urlBack) def intraFieldValidation(self, errors, values): @@ -148,10 +148,10 @@ class AbstractMixin: # Go back to the Plone site (no better solution at present). urlBack = self.portal_url.getPortalObject().absolute_url() else: - urlBack = self.absolute_url() + urlBack = self.getUrl() self.plone_utils.addPortalMessage( self.translate('Changes canceled.', domain='plone')) - return self.goto(urlBack, True) + return self.goto(urlBack) # Object for storing validation errors errors = AppyObject() @@ -180,7 +180,7 @@ class AbstractMixin: if rq.get('buttonOk.x', None): # Go to the consult view for this object obj.plone_utils.addPortalMessage(msg) - return self.goto('%s/skyn/view' % obj.absolute_url(), True) + return self.goto(obj.getUrl()) if rq.get('buttonPrevious.x', None): # Go to the previous page for this object. # We recompute the list of phases and pages because things @@ -193,34 +193,30 @@ class AbstractMixin: phaseInfo = self.getAppyPhases(page=currentPage) previousPage, show = self.getPreviousPage(phaseInfo, currentPage) if previousPage: - # Return the edit or view page? + # Return to the edit or view page? if show != 'view': rq.set('page', previousPage) return obj.skyn.edit(obj) else: - urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(), - previousPage) - return self.goto(urlBack) + return self.goto(obj.getUrl(page=previousPage)) else: obj.plone_utils.addPortalMessage(msg) - return self.goto('%s/skyn/view' % obj.absolute_url()) + return self.goto(obj.getUrl()) if rq.get('buttonNext.x', None): # Go to the next page for this object currentPage = rq.get('page') phaseInfo = self.getAppyPhases(page=currentPage) nextPage, show = self.getNextPage(phaseInfo, currentPage) if nextPage: - # Return the edit or view page? + # Return to the edit or view page? if show != 'view': rq.set('page', nextPage) return obj.skyn.edit(obj) else: - urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(), - nextPage) - return self.goto(urlBack) + return self.goto(obj.getUrl(page=nextPage)) else: obj.plone_utils.addPortalMessage(msg) - return self.goto('%s/skyn/view' % obj.absolute_url()) + return self.goto(obj.getUrl()) return obj.skyn.edit(obj) def onDelete(self): @@ -228,7 +224,7 @@ class AbstractMixin: msg = self.translate('delete_done') self.delete() self.plone_utils.addPortalMessage(msg) - self.goto(rq['HTTP_REFERER'], True) + self.goto(self.getUrl(rq['HTTP_REFERER'])) def rememberPreviousData(self): '''This method is called before updating an object and remembers, for @@ -280,17 +276,7 @@ class AbstractMixin: def goto(self, url, addParams=False): '''Brings the user to some p_url after an action has been executed.''' - rq = self.REQUEST - if not addParams: return rq.RESPONSE.redirect(url) - # Add some context-related parameters if needed. - params = [] - if rq.get('page', ''): params.append('page=%s' % rq['page']) - if rq.get('nav', ''): params.append('nav=%s' % rq['nav']) - params = '&'.join(params) - if not params: return rq.RESPONSE.redirect(url) - if url.find('?') != -1: params = '&' + params - else: params = '?' + params - return rq.RESPONSE.redirect(url+params) + return self.REQUEST.RESPONSE.redirect(url) def showField(self, name, layoutType='view'): '''Must I show field named p_name on this p_layoutType ?''' @@ -300,9 +286,14 @@ class AbstractMixin: '''Returns the method named p_methodName.''' return getattr(self, methodName, None) - def getFieldValue(self, name): - '''Returns the database value of field named p_name for p_self.''' - return self.getAppyType(name).getValue(self) + def getFieldValue(self, name, onlyIfSync=False, layoutType=None): + '''Returns the database value of field named p_name for p_self. + If p_onlyIfSync is True, it returns the value only if appyType can be + retrieved in synchronous mode.''' + appyType = self.getAppyType(name) + if not onlyIfSync or (onlyIfSync and appyType.sync[layoutType]): + return appyType.getValue(self) + return None def getFormattedFieldValue(self, name, value): '''Gets a nice, string representation of p_value which is a value from @@ -759,7 +750,7 @@ class AbstractMixin: msg = self.translate(label) if (resultType == 'computation') or not successfull: self.plone_utils.addPortalMessage(msg) - return self.goto(rq['HTTP_REFERER'], True) + return self.goto(self.getUrl(rq['HTTP_REFERER'])) else: # msg does not contain a message, but a complete file to show as is. # (or, if your prefer, the message must be shown directly to the @@ -1018,9 +1009,38 @@ class AbstractMixin: exec 'self.set%s%s([])' % (appyType.name[0].upper(), appyType.name[1:]) - def getUrl(self): - '''Returns the Appy URL for viewing this object.''' - return self.absolute_url() + '/skyn/view' + getUrlDefaults = {'page':True, 'nav':True} + def getUrl(self, base=None, mode='view', **kwargs): + '''Returns a Appy URL. + * If p_base is None, it will be the base URL for this object + (ie, self.absolute_url()). + * p_mode can de "edit" or "view". + * p_kwargs can store additional parameters to add to the URL. + In this dict, every value that is a string will be added to the + URL as-is. Every value that is True will be replaced by the value + in the request for the corresponding key (if existing; else, the + param will not be included in the URL at all).''' + # Define base URL if ommitted + if not base: base = self.absolute_url() + # Manage default args + if not kwargs: kwargs = self.getUrlDefaults + if 'page' not in kwargs: kwargs['page'] = True + if 'nav' not in kwargs: kwargs['nav'] = True + # Create URL parameters from kwargs + params = [] + for name, value in kwargs.iteritems(): + if isinstance(value, basestring): + params.append('%s=%s' % (name, value)) + elif self.REQUEST.get(name, ''): + params.append('%s=%s' % (name, self.REQUEST[name])) + if params: + params = '&'.join(params) + if base.find('?') != -1: params = '&' + params + else: params = '?' + params + else: + params = '' + # Return the full constructed URL + return '%s/skyn/%s%s' % (base, mode, params) def translate(self, label, mapping={}, domain=None, default=None): '''Translates a given p_label into p_domain with p_mapping.''' @@ -1030,7 +1050,7 @@ class AbstractMixin: domain, label, mapping, self, default=default) def getPageLayout(self, layoutType): - '''Returns the layout coresponding to p_layoutType for p_self.''' + '''Returns the layout corresponding to p_layoutType for p_self.''' appyClass = self.wrapperClass.__bases__[-1] if hasattr(appyClass, 'layouts'): layout = appyClass.layouts[layoutType] diff --git a/gen/plone25/skin/import.pt b/gen/plone25/skin/import.pt index 8da3c5f..0206a17 100644 --- a/gen/plone25/skin/import.pt +++ b/gen/plone25/skin/import.pt @@ -86,14 +86,14 @@ + style="cursor:pointer" onClick="toggleViewableElements()" align="left" /> + onClick="toggleCheckboxes()" style="cursor:pointer"/> @@ -118,7 +118,7 @@ Button for importing several elements at once.


-

diff --git a/gen/plone25/skin/macros.pt b/gen/plone25/skin/macros.pt index 9d4099e..70a6779 100644 --- a/gen/plone25/skin/macros.pt +++ b/gen/plone25/skin/macros.pt @@ -91,8 +91,8 @@ Mandatory column "Title"/"Name" + tal:define="navInfo python:'search.%s:%d.%s.%d.%d' % (contentType, flavourNumber, searchName, repeat['obj'].number()+startNumber, totalNumber);" + tal:content="obj/Title" tal:attributes="href python: obj.getUrl(nav=navInfo, page='main')"> Columns corresponding to other fields @@ -124,8 +124,10 @@ Edit the element - @@ -134,7 +136,7 @@ + onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
+ +
diff --git a/gen/plone25/skin/navigate.pt b/gen/plone25/skin/navigate.pt index d5af944..3cb56cd 100644 --- a/gen/plone25/skin/navigate.pt +++ b/gen/plone25/skin/navigate.pt @@ -46,7 +46,7 @@ -
+
Buttons for going to next/previous elements if this one is among bunch of referenced or searched objects. currentNumber starts with 1. diff --git a/gen/plone25/skin/page.pt b/gen/plone25/skin/page.pt index 0aa3571..0e80f10 100644 --- a/gen/plone25/skin/page.pt +++ b/gen/plone25/skin/page.pt @@ -242,7 +242,8 @@ } // Function that allows to generate a document from a pod template. function generatePodDocument(contextUid, templateUid, fieldName, podFormat) { - var theForm = document.forms["podTemplateForm"]; + var theForm = document.getElementsByName("podTemplateForm")[0]; + alert('The form =' + theForm); theForm.objectUid.value = contextUid; theForm.templateUid.value = templateUid; theForm.fieldName.value = fieldName; @@ -395,7 +396,7 @@ tal:repeat="podTemplate podTemplates"> + tal:attributes="onclick python: 'generatePodDocument(\'%s\',\'%s\', \'\', \'\')' % (contextObj.UID(), podTemplate.UID())" > @@ -404,7 +405,7 @@
@@ -522,7 +523,7 @@ + onClick python: 'triggerTransition(\'%s\')' % transition['id'];"/> @@ -560,7 +561,7 @@ Creator and last modification date Plus/minus icon for accessing history -     @@ -659,7 +660,7 @@
+ isEdit python: layoutType == 'edit'">
@@ -669,7 +670,7 @@ - + @@ -689,10 +690,8 @@ - @@ -705,7 +704,7 @@ - + diff --git a/gen/plone25/skin/portlet.pt b/gen/plone25/skin/portlet.pt index 5834d5b..59e58a3 100644 --- a/gen/plone25/skin/portlet.pt +++ b/gen/plone25/skin/portlet.pt @@ -5,7 +5,7 @@ tal:define="queryUrl python: '%s/skyn/query' % appFolder.absolute_url(); currentSearch request/search|nothing; currentType request/type_name|nothing; - contextObj python: tool.getPublishedObject(rootClasses)"> + contextObj python: tool.getPublishedObject()"> Portlet title, with link to tool.
If there is only one flavour, clicking on the portlet @@ -22,7 +22,7 @@ @@ -30,7 +30,7 @@
-
@@ -88,7 +88,7 @@   + onClick python:'toggleCookie(\'%s\')' % group['labelId']"/>  Group searches @@ -136,9 +136,9 @@

- -
@@ -150,7 +150,7 @@ currently shown object, made of phases and contained pages. - @@ -161,27 +161,38 @@ displayLink python: (phase['phaseStatus'] != 'Future') and ('/portal_factory' not in contextObj.absolute_url()) and (len(phase['pages']) == 1)" tal:attributes="class python: (len(phases) > 1) and ('appyPhase step%s' % phase['phaseStatus']) or 'appyPhase'">
- + + A single page in the phase +
+ + + + +
+ + +
+ Several pages in the phase
-
- - +
diff --git a/gen/plone25/skin/widgets/action.pt b/gen/plone25/skin/widgets/action.pt index 8020dc3..2f5769c 100644 --- a/gen/plone25/skin/widgets/action.pt +++ b/gen/plone25/skin/widgets/action.pt @@ -9,7 +9,7 @@ + onClick python: 'askConfirm(\'%s\')' % formId"/> The previous onClick is simply used to prevent Plone diff --git a/gen/plone25/skin/widgets/pod.pt b/gen/plone25/skin/widgets/pod.pt index e9b8c05..f0c3536 100644 --- a/gen/plone25/skin/widgets/pod.pt +++ b/gen/plone25/skin/widgets/pod.pt @@ -9,7 +9,7 @@ diff --git a/gen/plone25/skin/widgets/ref.pt b/gen/plone25/skin/widgets/ref.pt index 9662a8a..b03ce1d 100644 --- a/gen/plone25/skin/widgets/ref.pt +++ b/gen/plone25/skin/widgets/ref.pt @@ -8,10 +8,11 @@ allows to reach the correct page where the forward reference is defined. If we are on a forward reference, the "nav" parameter is added to the URL for allowing to navigate from one object to the next/previous on skyn/view. - @@ -39,7 +40,9 @@ Edit the element - + @@ -49,7 +52,7 @@ + onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/> diff --git a/gen/plone25/skin/widgets/show.pt b/gen/plone25/skin/widgets/show.pt index c2717c7..f6b47dc 100644 --- a/gen/plone25/skin/widgets/show.pt +++ b/gen/plone25/skin/widgets/show.pt @@ -55,8 +55,8 @@ layout python: widget['layouts'][layoutType]; name widget/name; sync python: widget['sync'][layoutType]; - rawValue python: sync and contextObj.getFieldValue(name) or None; - value python: sync and contextObj.getFormattedFieldValue(name, rawValue) or None; + 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: (); @@ -103,7 +103,7 @@ id tabId"> + tal:attributes="onClick python: 'showTab(\'%s_%d_%d\')' % (widget['name'], repeat['widgetRow'].number(), len(widget['widgets']))"> diff --git a/gen/plone25/skin/widgets/string.pt b/gen/plone25/skin/widgets/string.pt index e12d28c..c43ced7 100644 --- a/gen/plone25/skin/widgets/string.pt +++ b/gen/plone25/skin/widgets/string.pt @@ -30,7 +30,7 @@ tal:attributes="name name; id name; multiple python: isMultiple and 'multiple' or ''; - onchange python: isMaster and ('javascript:updateSlaves(getMasterValue(this), \'%s\')' % widget['id']) or ''; + onchange python: isMaster and ('updateSlaves(getMasterValue(this), \'%s\')' % widget['id']) or ''; class widget/master_css; size python: isMultiple and widget['height'] or 1">