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

@ -4,8 +4,8 @@ from appy.shared.utils import Traceback
from appy.gen.layout import Table from appy.gen.layout import Table
from appy.gen.layout import defaultFieldLayouts from appy.gen.layout import defaultFieldLayouts
from appy.gen.po import PoMessage from appy.gen.po import PoMessage
from appy.gen.utils import sequenceTypes, PageDescr, GroupDescr, Keywords, \ from appy.gen.utils import sequenceTypes, GroupDescr, Keywords, FileWrapper, \
FileWrapper, getClassName, SomeObjects getClassName, SomeObjects
from appy.shared.data import languages from appy.shared.data import languages
# Default Appy permissions ----------------------------------------------------- # Default Appy permissions -----------------------------------------------------
@ -22,10 +22,67 @@ emptyTuple = ()
# (pages, groups,...) ---------------------------------------------------------- # (pages, groups,...) ----------------------------------------------------------
class Page: class Page:
'''Used for describing a page, its related phase, show condition, etc.''' '''Used for describing a page, its related phase, show condition, etc.'''
def __init__(self, name, phase='main', show=True): subElements = ('save', 'cancel', 'previous', 'next')
def __init__(self, name, phase='main', show=True, showSave=True,
showCancel=True, showPrevious=True, showNext=True):
self.name = name self.name = name
self.phase = phase self.phase = phase
self.show = show self.show = show
# When editing the page, must I show the "save" button?
self.showSave = showSave
# When editing the page, must I show the "cancel" button?
self.showCancel = showCancel
# When editing the page, and when a previous page exists, must I show
# the "previous" button?
self.showPrevious = showPrevious
# When editing the page, and when a next page exists, must I show the
# "next" button?
self.showNext = showNext
@staticmethod
def get(pageData):
'''Produces a Page instance from p_pageData. User-defined p_pageData
can be:
(a) a string containing the name of the page;
(b) a string containing <pageName>_<phaseName>;
(c) a Page instance.
This method returns always a Page instance.'''
res = pageData
if res and isinstance(res, basestring):
# Page data is given as a string.
pageElems = pageData.rsplit('_', 1)
if len(pageElems) == 1: # We have case (a)
res = Page(pageData)
else: # We have case (b)
res = Page(pageData[0], phase=pageData[1])
return res
def isShowable(self, obj, layoutType, elem='page'):
'''Must this page be shown for p_obj? "Show value" can be True, False
or 'view' (page is available only in "view" mode).
If p_elem is not "page", this method returns the fact that a
sub-element is viewable or not (button "save", "cancel", etc).'''
# Define what attribute to test for "showability".
showAttr = 'show'
if elem != 'page':
showAttr = 'show%s' % elem.capitalize()
# Get the value of the show attribute as identified above.
show = getattr(self, showAttr)
if callable(show): show = show(obj.appy())
# Show value can be 'view', for example. Thanks to p_layoutType,
# convert show value to a real final boolean value.
res = show
if res == 'view': res = layoutType == 'view'
return res
def getInfo(self, obj, layoutType):
'''Gets information about this page, for p_obj, as a dict.'''
res = {}
for elem in Page.subElements:
res['show%s' % elem.capitalize()] = self.isShowable(obj, layoutType,
elem=elem)
return res
class Group: class Group:
'''Used for describing a group of widgets within a page.''' '''Used for describing a group of widgets within a page.'''
@ -313,9 +370,9 @@ class Type:
# Must the field be visible or not? # Must the field be visible or not?
self.show = show self.show = show
# When displaying/editing the whole object, on what page and phase must # When displaying/editing the whole object, on what page and phase must
# this field value appear? Default is ('main', 'main'). pageShow # this field value appear?
# indicates if the page must be shown or not. self.page = Page.get(page)
self.page, self.phase, self.pageShow = PageDescr.getPageInfo(page) self.pageName = self.page.name
# Within self.page, in what group of fields must this field value # Within self.page, in what group of fields must this field value
# appear? # appear?
self.group = Group.get(group) self.group = Group.get(group)
@ -465,14 +522,6 @@ class Type:
else: res = False else: res = False
return res return res
def showPage(self, obj):
'''Must the page where this field lies be shown ? "Show value" can be
True, False or 'view' (page is available only in "view" mode).'''
if callable(self.pageShow):
return self.pageShow(obj.appy())
else:
return self.pageShow
def formatSync(self, sync): def formatSync(self, sync):
'''Creates a dictionary indicating, for every layout type, if the field '''Creates a dictionary indicating, for every layout type, if the field
value must be retrieved synchronously or not.''' value must be retrieved synchronously or not.'''

View file

@ -73,8 +73,8 @@ class ClassDescriptor(Descriptor):
'''Gets the phases defined on fields of this class.''' '''Gets the phases defined on fields of this class.'''
res = [] res = []
for fieldName, appyType, klass in self.getOrderedAppyAttributes(): for fieldName, appyType, klass in self.getOrderedAppyAttributes():
if appyType.phase not in res: if appyType.page.phase not in res:
res.append(appyType.phase) res.append(appyType.page.phase)
return res return res
class WorkflowDescriptor(Descriptor): class WorkflowDescriptor(Descriptor):

View file

@ -37,7 +37,7 @@ class Generator(AbstractGenerator):
if the page where the field must be displayed has a boolean attribute if the page where the field must be displayed has a boolean attribute
"show" having the boolean value False.''' "show" having the boolean value False.'''
if (type(field.show) == bool) and not field.show: return True if (type(field.show) == bool) and not field.show: return True
if (type(field.pageShow) == bool) and not field.pageShow: return True if (type(field.page.show) == bool) and not field.page.show: return True
return False return False
undumpable = ('Ref', 'Action', 'File', 'Computed') undumpable = ('Ref', 'Action', 'File', 'Computed')

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -160,7 +160,7 @@
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer" <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); tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=pageName);
src string: $portal_url/skyn/edit.gif" 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> </td>
</tr> </tr>
</table> </table>
@ -180,7 +180,7 @@
<img title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer" <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); tal:attributes="onClick python: 'href: window.location=\'%s\'' % contextObj.getUrl(mode='edit', page=aPage);
src string: $portal_url/skyn/edit.gif" 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> </td>
</tr> </tr>
</table> </table>

View file

@ -23,8 +23,8 @@
tool contextObj/getTool; tool contextObj/getTool;
appFolder tool/getAppFolder; appFolder tool/getAppFolder;
appName appFolder/getId; appName appFolder/getId;
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
page request/page|python:'main'; page request/page|python:'main';
phaseInfo python: contextObj.getAppyPhases(page=page);
phase phaseInfo/name; phase phaseInfo/name;
showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type)"> showWorkflow python: tool.getAttr('showWorkflowFor' + contextObj.meta_type)">
<metal:prologue use-macro="here/skyn/page/macros/prologue"/> <metal:prologue use-macro="here/skyn/page/macros/prologue"/>

View file

@ -9,9 +9,9 @@
on a forward reference, the "nav" parameter is added to the URL for allowing to navigate 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> from one object to the next/previous on skyn/view.</tal:comment>
<a tal:define="includeShownInfo includeShownInfo | python:False; <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); 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)" 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> tal:attributes="href fullUrl" tal:content="python: (not includeShownInfo) and obj.Title() or contextObj.getReferenceLabel(fieldName, obj.appy())"></a>
</metal:objectTitle> </metal:objectTitle>
@ -41,7 +41,7 @@
</td> </td>
<tal:comment replace="nothing">Edit the element</tal:comment> <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']"> <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)"> tal:attributes="href python: obj.getUrl(mode='edit', page='main', nav=navInfo)">
<img title="label_edit" i18n:domain="plone" i18n:attributes="title" <img title="label_edit" i18n:domain="plone" i18n:attributes="title"
tal:attributes="src string: $portal_url/skyn/edit.gif"/> 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 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> an icon for creating a new linked object (at least if multiplicities allow it).</tal:comment>
<img style="cursor:pointer" tal:condition="showPlusIcon" <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:'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); formCall python: test(appyType['addConfirm'], 'askConfirm(\'script\', &quot;%s&quot;, &quot;%s&quot;)' % (formCall, addConfirmMsg), formCall);
noFormCall python: navBaseCall.replace('**v**', '%d, \'CreateWithoutForm\'' % startNumber); noFormCall python: navBaseCall.replace('**v**', '%d, \'CreateWithoutForm\'' % startNumber);

View file

@ -24,7 +24,7 @@ class GroupDescr(Descr):
self.descrId = self.labelId + '_descr' self.descrId = self.labelId + '_descr'
self.helpId = self.labelId + '_help' self.helpId = self.labelId + '_help'
# The name of the page where the group lies # The name of the page where the group lies
self.page = page self.page = page.name
# The widgets belonging to the group that the current user may see. # The widgets belonging to the group that the current user may see.
# They will be stored by m_addWidget below as a list of lists because # They will be stored by m_addWidget below as a list of lists because
# they will be rendered as a table. # they will be rendered as a table.
@ -58,34 +58,18 @@ class GroupDescr(Descr):
newRow = [newWidget] newRow = [newWidget]
groupDict['widgets'].append(newRow) groupDict['widgets'].append(newRow)
class PageDescr(Descr):
@staticmethod
def getPageInfo(pageOrName):
'''pageOrName can be:
- a string containing the name of the page
- a string containing <pageName>_<phaseName>
- a appy.gen.Page instance for a more detailed page description.
This method returns a normalized tuple containing page-related
information.'''
if isinstance(pageOrName, basestring):
res = pageOrName.rsplit('_', 1)
if len(res) == 1:
res.append('main')
res.append(True)
else:
res = [pageOrName.name, pageOrName.phase, pageOrName.show]
return res
class PhaseDescr(Descr): class PhaseDescr(Descr):
def __init__(self, name, states, obj): def __init__(self, name, states, obj):
self.name = name self.name = name
self.states = states self.states = states
self.obj = obj self.obj = obj
self.phaseStatus = None self.phaseStatus = None
self.pages = [] # The list of pages in this phase # The list of names of pages in this phase
# Below, a dict of "show" values (True, False or 'view') for every page self.pages = []
# in self.pages. # The list of hidden pages in this phase
self.pageShows = {} self.hiddenPages = []
# The dict below stores infor about every page listed in self.pages.
self.pagesInfo = {}
self.totalNbOfPhases = None self.totalNbOfPhases = None
# The following attributes allows to browse, from a given page, to the # The following attributes allows to browse, from a given page, to the
# last page of the previous phase and to the first page of the following # last page of the previous phase and to the first page of the following
@ -93,13 +77,23 @@ class PhaseDescr(Descr):
self.previousPhase = None self.previousPhase = None
self.nextPhase = None self.nextPhase = None
def addPage(self, appyType, obj): def addPage(self, appyType, obj, layoutType):
toAdd = appyType.page '''Adds page-related information in the phase.'''
if toAdd not in self.pages: # If the page is already there, we have nothing more to do.
showValue = appyType.showPage(obj) if (appyType.page.name in self.pages) or \
if showValue: (appyType.page.name in self.hiddenPages): return
self.pages.append(toAdd) # Add the page only if it must be shown.
self.pageShows[toAdd] = showValue if appyType.page.isShowable(obj, layoutType):
# The page must be added.
self.pages.append(appyType.page.name)
# Create the dict about page information and add it in self.pageInfo
pageInfo = {'page': appyType.page,
'showOnView': appyType.page.isShowable(obj, 'view'),
'showOnEdit': appyType.page.isShowable(obj, 'edit')}
pageInfo.update(appyType.page.getInfo(obj, layoutType))
self.pagesInfo[appyType.page.name] = pageInfo
else:
self.hiddenPages.append(appyType.page.name)
def computeStatus(self, allPhases): def computeStatus(self, allPhases):
'''Compute status of whole phase based on individual status of states '''Compute status of whole phase based on individual status of states