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:
parent
3cfc24fe02
commit
2aedf8c88a
|
@ -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.'''
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -646,56 +646,57 @@
|
||||||
<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';
|
||||||
<br/>
|
pageInfo python: phaseInfo['pagesInfo'][page]">
|
||||||
<tal:previousButton condition="previousPage">
|
<br/>
|
||||||
<tal:button condition="isEdit">
|
<tal:previousButton condition="python: previousPage and pageInfo['showPrevious']">
|
||||||
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
|
<tal:button condition="isEdit">
|
||||||
title="label_previous" i18n:attributes="title" i18n:domain="plone"
|
<input type="image" class="imageInput" style="cursor:pointer" name="buttonPrevious"
|
||||||
tal:attributes="src string:$portal_url/skyn/previous.png"/>
|
title="label_previous" i18n:attributes="title" i18n:domain="plone"
|
||||||
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
|
tal:attributes="src string:$portal_url/skyn/previous.png"/>
|
||||||
</tal:button>
|
<input type="hidden" name="previousPage" tal:attributes="value previousPage"/>
|
||||||
<tal:link condition="not: isEdit">
|
</tal:button>
|
||||||
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
|
<tal:link condition="not: isEdit">
|
||||||
<img tal:attributes="src string:$portal_url/skyn/previous.png"
|
<a tal:attributes="href python: contextObj.getUrl(page=previousPage)">
|
||||||
title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
|
<img tal:attributes="src string:$portal_url/skyn/previous.png"
|
||||||
</a>
|
title="label_previous" i18n:attributes="title" i18n:domain="plone"/>
|
||||||
</tal:link>
|
</a>
|
||||||
</tal:previousButton>
|
</tal:link>
|
||||||
|
</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"
|
||||||
tal:attributes="src string:$portal_url/skyn/next.png"/>
|
tal:attributes="src string:$portal_url/skyn/next.png"/>
|
||||||
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
|
<input type="hidden" name="nextPage" tal:attributes="value nextPage"/>
|
||||||
</tal:button>
|
</tal:button>
|
||||||
<tal:link condition="not: isEdit">
|
<tal:link condition="not: isEdit">
|
||||||
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
|
<a tal:attributes="href python: contextObj.getUrl(page=nextPage)">
|
||||||
<img tal:attributes="src string:$portal_url/skyn/next.png"
|
<img tal:attributes="src string:$portal_url/skyn/next.png"
|
||||||
title="label_next" i18n:attributes="title" i18n:domain="plone"/>
|
title="label_next" i18n:attributes="title" i18n:domain="plone"/>
|
||||||
</a>
|
</a>
|
||||||
</tal:link>
|
</tal:link>
|
||||||
</tal:nextButton>
|
</tal:nextButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tal:comment replace="nothing">
|
<tal:comment replace="nothing">
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -15,17 +15,17 @@
|
||||||
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
||||||
<body>
|
<body>
|
||||||
<metal:fill fill-slot="main"
|
<metal:fill fill-slot="main"
|
||||||
tal:define="contextObj python:context.getParentNode();
|
tal:define="contextObj python:context.getParentNode();
|
||||||
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
|
portal_type python:here.getPortalTypeName().lower().replace(' ', '_');
|
||||||
errors python:request.get('errors', {});
|
errors python:request.get('errors', {});
|
||||||
layoutType python:'view';
|
layoutType python:'view';
|
||||||
layout python: contextObj.getPageLayout(layoutType);
|
layout python: contextObj.getPageLayout(layoutType);
|
||||||
tool contextObj/getTool;
|
tool contextObj/getTool;
|
||||||
appFolder tool/getAppFolder;
|
appFolder tool/getAppFolder;
|
||||||
appName appFolder/getId;
|
appName appFolder/getId;
|
||||||
page request/page|python:'main';
|
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
|
||||||
phaseInfo python: contextObj.getAppyPhases(page=page);
|
page request/page|python:'main';
|
||||||
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"/>
|
||||||
<metal:show use-macro="here/skyn/page/macros/show"/>
|
<metal:show use-macro="here/skyn/page/macros/show"/>
|
||||||
|
|
|
@ -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\', "%s", "%s")' % (formCall, addConfirmMsg), formCall);
|
formCall python: test(appyType['addConfirm'], 'askConfirm(\'script\', "%s", "%s")' % (formCall, addConfirmMsg), formCall);
|
||||||
noFormCall python: navBaseCall.replace('**v**', '%d, \'CreateWithoutForm\'' % startNumber);
|
noFormCall python: navBaseCall.replace('**v**', '%d, \'CreateWithoutForm\'' % startNumber);
|
||||||
|
|
54
gen/utils.py
54
gen/utils.py
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue