Added a new level of configurability in navigation: allow to hide/show every button on every page + bugfixes in page/phase navigation.

This commit is contained in:
Gaetan Delannay 2010-10-19 10:47:42 +02:00
parent 3cfc24fe02
commit 2aedf8c88a
12 changed files with 213 additions and 165 deletions

View file

@ -13,8 +13,7 @@ import appy.gen.descriptors
from appy.gen.po import PoMessage
from appy.gen import Date, String, State, Transition, Type, Search, \
Selection, Import, Role
from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage, \
sequenceTypes, getClassName
from appy.gen.utils import produceNiceMessage, getClassName
TABS = 4 # Number of blanks in a Python indentation.
# ------------------------------------------------------------------------------
@ -172,12 +171,14 @@ class FieldDescriptor:
messages.append(self.produceMessage(helpId, isLabel=False))
# Create i18n messages linked to pages and phases
messages = self.generator.labels
pageMsgId = '%s_page_%s' % (self.classDescr.name, self.appyType.page)
phaseMsgId = '%s_phase_%s' % (self.classDescr.name, self.appyType.phase)
pageMsgId = '%s_page_%s' % (self.classDescr.name,
self.appyType.page.name)
phaseMsgId = '%s_phase_%s' % (self.classDescr.name,
self.appyType.page.phase)
pagePoMsg = PoMessage(pageMsgId, '',
produceNiceMessage(self.appyType.page))
produceNiceMessage(self.appyType.page.name))
phasePoMsg = PoMessage(phaseMsgId, '',
produceNiceMessage(self.appyType.phase))
produceNiceMessage(self.appyType.page.phase))
for poMsg in (pagePoMsg, phasePoMsg):
if poMsg not in messages:
messages.append(poMsg)

View file

@ -149,7 +149,6 @@ class BaseMixin:
errorMessage = self.translate(
'Please correct the indicated errors.', domain='plone')
isNew = rq.get('is_new') == 'True'
# Go back to the consult view if the user clicked on 'Cancel'
if rq.get('buttonCancel.x', None):
if isNew:
@ -204,31 +203,29 @@ class BaseMixin:
# previous pages may have changed). Moreover, previous and next
# pages may not be available in "edit" mode, so we return the edit
# or view pages depending on page.show.
currentPage = rq.get('page')
phaseInfo = self.getAppyPhases(page=currentPage)
previousPage, show = self.getPreviousPage(phaseInfo, currentPage)
if previousPage:
phaseInfo = self.getAppyPhases(currentOnly=True, layoutType='edit')
pageName, pageInfo = self.getPreviousPage(phaseInfo, rq['page'])
if pageName:
# Return to the edit or view page?
if show != 'view':
rq.set('page', previousPage)
if pageInfo['showOnEdit']:
rq.set('page', pageName)
return obj.skyn.edit(obj)
else:
return self.goto(obj.getUrl(page=previousPage))
return self.goto(obj.getUrl(page=pageName))
else:
obj.plone_utils.addPortalMessage(msg)
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:
phaseInfo = self.getAppyPhases(currentOnly=True, layoutType='edit')
pageName, pageInfo = self.getNextPage(phaseInfo, rq['page'])
if pageName:
# Return to the edit or view page?
if show != 'view':
rq.set('page', nextPage)
if pageInfo['showOnEdit']:
rq.set('page', pageName)
return obj.skyn.edit(obj)
else:
return self.goto(obj.getUrl(page=nextPage))
return self.goto(obj.getUrl(page=pageName))
else:
obj.plone_utils.addPortalMessage(msg)
return self.goto(obj.getUrl())
@ -390,13 +387,13 @@ class BaseMixin:
className = className or self.__class__.__name__
return self.getProductConfig().attributes[className]
def getGroupedAppyTypes(self, layoutType, page):
def getGroupedAppyTypes(self, layoutType, pageName):
'''Returns the fields sorted by group. For every field, the appyType
(dict version) is given.'''
res = []
groups = {} # The already encountered groups
for appyType in self.getAllAppyTypes():
if appyType.page != page: continue
if appyType.page.name != pageName: continue
if not appyType.isShowable(self, layoutType): continue
if not appyType.group:
res.append(appyType.__dict__)
@ -408,12 +405,12 @@ class BaseMixin:
GroupDescr.addWidget(groupDescr, appyType.__dict__)
return res
def getAppyTypes(self, layoutType, page):
def getAppyTypes(self, layoutType, pageName):
'''Returns the list of appyTypes that belong to a given p_page, for a
given p_layoutType.'''
res = []
for appyType in self.getAllAppyTypes():
if appyType.page != page: continue
if appyType.page.name != pageName: continue
if not appyType.isShowable(self, layoutType): continue
res.append(appyType)
return res
@ -478,23 +475,23 @@ class BaseMixin:
res.append(transition)
return res
def getAppyPhases(self, currentOnly=False, page=None):
def getAppyPhases(self, currentOnly=False, layoutType='view'):
'''Gets the list of phases that are defined for this content type. If
p_currentOnly is True, the search is limited to the current phase.
If p_page is not None, the search is limited to the phase
where p_page lies.'''
p_currentOnly is True, the search is limited to the phase where the
current page (as defined in the request) lies.'''
# Get the list of phases
res = [] # Ordered list of phases
phases = {} # Dict of phases
for appyType in self.getAllAppyTypes():
if appyType.phase not in phases:
states = self.getAppyStates(appyType.phase)
phase = PhaseDescr(appyType.phase, states, self)
typePhase = appyType.page.phase
if typePhase not in phases:
states = self.getAppyStates(typePhase)
phase = PhaseDescr(typePhase, states, self)
res.append(phase.__dict__)
phases[appyType.phase] = phase
phases[typePhase] = phase
else:
phase = phases[appyType.phase]
phase.addPage(appyType, self)
phase = phases[typePhase]
phase.addPage(appyType, self, layoutType)
# Remove phases that have no visible page
for i in range(len(res)-1, -1, -1):
if not res[i]['pages']:
@ -504,15 +501,19 @@ class BaseMixin:
for ph in phases.itervalues():
ph.computeStatus(res)
ph.totalNbOfPhases = len(res)
# Restrict the result if we must not produce the whole list of phases
# Restrict the result to the current phase if required
if currentOnly:
for phaseInfo in res:
if phaseInfo['phaseStatus'] == 'Current':
return phaseInfo
elif page:
rq = self.REQUEST
page = rq.get('page', 'main')
for phaseInfo in res:
if page in phaseInfo['pages']:
return phaseInfo
# If I am here, it means that the page as defined in the request,
# or 'main' by default, is not existing nor visible in any phase.
# In this case I set the page as being the first visible page in
# the first visible phase.
rq.set('page', res[0]['pages'][0])
return res[0]
else:
return res
@ -522,15 +523,15 @@ class BaseMixin:
if pageIndex > 0:
# We stay on the same phase, previous page
res = phase['pages'][pageIndex-1]
show = phase['pageShows'][res]
return res, show
resInfo = phase['pagesInfo'][res]
return res, resInfo
else:
if phase['previousPhase']:
# We go to the last page of previous phase
previousPhase = phase['previousPhase']
res = previousPhase['pages'][-1]
show = previousPhase['pageShows'][res]
return res, show
resInfo = previousPhase['pagesInfo'][res]
return res, resInfo
else:
return None, None
@ -540,15 +541,15 @@ class BaseMixin:
if pageIndex < len(phase['pages'])-1:
# We stay on the same phase, next page
res = phase['pages'][pageIndex+1]
show = phase['pageShows'][res]
return res, show
resInfo = phase['pagesInfo'][res]
return res, resInfo
else:
if phase['nextPhase']:
# We go to the first page of next phase
nextPhase = phase['nextPhase']
res = nextPhase['pages'][0]
show = nextPhase['pageShows'][res]
return res, show
resInfo = nextPhase['pagesInfo'][res]
return res, resInfo
else:
return None, None

View file

@ -19,10 +19,10 @@ class ModelClass:
_appy_attributes = [] # We need to keep track of attributes order.
# When creating a new instance of a ModelClass, the following attributes
# must not be given in the constructor (they are computed attributes).
_appy_notinit = ('id', 'type', 'pythonType', 'slaves', 'phase', 'pageShow',
'isSelect', 'hasLabel', 'hasDescr', 'hasHelp',
'master_css', 'layouts', 'required', 'filterable',
'validable', 'backd', 'isBack', 'sync')
_appy_notinit = ('id', 'type', 'pythonType', 'slaves', 'isSelect',
'hasLabel', 'hasDescr', 'hasHelp', 'master_css',
'layouts', 'required', 'filterable', 'validable', 'backd',
'isBack', 'sync', 'pageName')
@classmethod
def _appy_addField(klass, fieldName, fieldType, classDescr):
@ -55,6 +55,8 @@ class ModelClass:
attrValue = 'Selection("%s")' % attrValue.methodName
elif isinstance(attrValue, Group):
attrValue = 'Group("%s")' % attrValue.name
elif isinstance(attrValue, Page):
attrValue = 'Page("%s")' % attrValue.name
elif type(attrValue) == types.FunctionType:
attrValue = '%sWrapper.%s'% (klass.__name__, attrValue.__name__)
typeArgs += '%s=%s,' % (attrName, attrValue)

View file

@ -6,12 +6,12 @@
tool contextObj/getTool;
appFolder tool/getAppFolder;
appName appFolder/getId;
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='edit');
phase phaseInfo/name;
page request/page|python:'main';
cssAndJs python: contextObj.getCssAndJs(layoutType, page);
css python: cssAndJs[0];
js python: cssAndJs[1];
phaseInfo python: contextObj.getAppyPhases(page=page);
phase phaseInfo/name">
js python: cssAndJs[1]">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"

View file

@ -646,56 +646,57 @@
<div metal:define-macro="buttons"
tal:define="previousPage python: contextObj.getPreviousPage(phaseInfo, page)[0];
nextPage python: contextObj.getNextPage(phaseInfo, page)[0];
isEdit python: layoutType == 'edit'">
<br/>
<tal:previousButton condition="previousPage">
<tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
title="label_previous" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/previous.png"/>
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
</tal:button>
<tal:link condition="not: isEdit">
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
<img tal:attributes="src string:$portal_url/skyn/previous.png"
title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
</a>
</tal:link>
</tal:previousButton>
isEdit python: layoutType == 'edit';
pageInfo python: phaseInfo['pagesInfo'][page]">
<br/>
<tal:previousButton condition="python: previousPage and pageInfo['showPrevious']">
<tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
title="label_previous" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/previous.png"/>
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
</tal:button>
<tal:link condition="not: isEdit">
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
<img tal:attributes="src string:$portal_url/skyn/previous.png"
title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
</a>
</tal:link>
</tal:previousButton>
<tal:saveButton condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonOk"
title="label_save" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/save.png"/>
</tal:saveButton>
<tal:saveButton condition="python: isEdit and pageInfo['showSave']">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonOk"
title="label_save" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/save.png"/>
</tal:saveButton>
<tal:cancelButton condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel"
title="label_cancel" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/cancel.png"/>
</tal:cancelButton>
<tal:cancelButton condition="python: isEdit and pageInfo['showCancel']">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonCancel"
title="label_cancel" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/cancel.png"/>
</tal:cancelButton>
<tal:editLink condition="python: not isEdit and (phaseInfo['pageShows'][page] != 'view')">
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
src string: $portal_url/skyn/editBig.png"
tal:condition="python: member.has_permission('Modify portal content', contextObj)"/>
</tal:editLink>
<tal:editLink condition="python: not isEdit and pageInfo['showOnEdit']">
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=page);
src string: $portal_url/skyn/editBig.png"
tal:condition="python: member.has_permission('Modify portal content', contextObj)"/>
</tal:editLink>
<tal:nextButton condition="nextPage">
<tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonNext"
title="label_next" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/next.png"/>
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
</tal:button>
<tal:link condition="not: isEdit">
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
<img tal:attributes="src string:$portal_url/skyn/next.png"
title="label_next" i18n:attributes="title" i18n:domain="plone"/>
</a>
</tal:link>
</tal:nextButton>
<tal:nextButton condition="python: nextPage and pageInfo['showNext']">
<tal:button condition="isEdit">
<input type="image" class="imageInput" style="cursor:pointer" name="buttonNext"
title="label_next" i18n:attributes="title" i18n:domain="plone"
tal:attributes="src string:$portal_url/skyn/next.png"/>
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
</tal:button>
<tal:link condition="not: isEdit">
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
<img tal:attributes="src string:$portal_url/skyn/next.png"
title="label_next" i18n:attributes="title" i18n:domain="plone"/>
</a>
</tal:link>
</tal:nextButton>
</div>
<tal:comment replace="nothing">

View file

@ -160,7 +160,7 @@
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=pageName);
src string: $portal_url/skyn/edit.gif"
tal:condition="python: member.has_permission('Modify portal content', contextObj) and phase['pageShows'][pageName] != 'view'"/>
tal:condition="python: member.has_permission('Modify portal content', contextObj) and phase['pagesInfo'][pageName]['showOnEdit']"/>
</td>
</tr>
</table>
@ -180,7 +180,7 @@
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=aPage);
src string: $portal_url/skyn/edit.gif"
tal:condition="python: member.has_permission('Modify portal content', contextObj) and phase['pageShows'][aPage] != 'view'"/>
tal:condition="python: member.has_permission('Modify portal content', contextObj) and phase['pagesInfo'][aPage]['showOnEdit']"/>
</td>
</tr>
</table>

View file

@ -15,17 +15,17 @@
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
<body>
<metal:fill fill-slot="main"
tal:define="contextObj python:context.getParentNode();
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
errors python:request.get('errors', {});
layoutType python:'view';
layout python: contextObj.getPageLayout(layoutType);
tool contextObj/getTool;
appFolder tool/getAppFolder;
appName appFolder/getId;
page request/page|python:'main';
phaseInfo python: contextObj.getAppyPhases(page=page);
phase phaseInfo/name;
tal:define="contextObj python:context.getParentNode();
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
errors python:request.get('errors', {});
layoutType python:'view';
layout python: contextObj.getPageLayout(layoutType);
tool contextObj/getTool;
appFolder tool/getAppFolder;
appName appFolder/getId;
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
page request/page|python:'main';
phase phaseInfo/name;
showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type)">
<metal:prologue use-macro="here/skyn/page/macros/prologue"/>
<metal:show use-macro="here/skyn/page/macros/show"/>

View file

@ -9,9 +9,9 @@
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.</tal:comment>
<a tal:define="includeShownInfo includeShownInfo | python:False;
navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['page'], repeat['obj'].number()+startNumber, totalNumber);
navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], repeat['obj'].number()+startNumber, totalNumber);
navInfo python: test(appyType['isBack'], '', navInfo);
pageName python: appyType['isBack'] and appyType['backd']['page'] or 'main';
pageName python: appyType['isBack'] and appyType['backd']['pageName'] or 'main';
fullUrl python: obj.getUrl(page=pageName, nav=navInfo)"
tal:attributes="href fullUrl" tal:content="python: (not includeShownInfo) and obj.Title() or contextObj.getReferenceLabel(fieldName, obj.appy())"></a>
</metal:objectTitle>
@ -41,7 +41,7 @@
</td>
<tal:comment replace="nothing">Edit the element</tal:comment>
<td class="noPadding" tal:condition="python: member.has_permission('Modify portal content', obj) and not appyType['noForm']">
<a tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['page'], repeat['obj'].number()+startNumber, totalNumber);"
<a tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], repeat['obj'].number()+startNumber, totalNumber);"
tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
<img title="label_edit" i18n:domain="plone" i18n:attributes="title"
tal:attributes="src string: $portal_url/skyn/edit.gif"/>
@ -63,7 +63,7 @@
through a reference widget. Indeed, If field was declared as "addable", we must provide
an icon for creating a new linked object (at least if multiplicities allow it).</tal:comment>
<img style="cursor:pointer" tal:condition="showPlusIcon"
tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['page'], 0, totalNumber);
tal:define="navInfo python:'ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, appyType['pageName'], 0, totalNumber);
formCall python:'window.location=\'%s/skyn/do?action=Create&type_name=%s&nav=%s\'' % (folder.absolute_url(), linkedPortalType, navInfo);
formCall python: test(appyType['addConfirm'], 'askConfirm(\'script\', &quot;%s&quot;, &quot;%s&quot;)' % (formCall, addConfirmMsg), formCall);
noFormCall python: navBaseCall.replace('**v**', '%d, \'CreateWithoutForm\'' % startNumber);