Allowed to express layouts in a more concise manner and various graphical improvements.
This commit is contained in:
parent
eb52c1bb7d
commit
0b4f6e1f79
104
gen/__init__.py
104
gen/__init__.py
|
@ -31,7 +31,8 @@ class Group:
|
||||||
'''Used for describing a group of widgets within a page.'''
|
'''Used for describing a group of widgets within a page.'''
|
||||||
def __init__(self, name, columns=['100%'], wide=True, style='fieldset',
|
def __init__(self, name, columns=['100%'], wide=True, style='fieldset',
|
||||||
hasLabel=True, hasDescr=False, hasHelp=False,
|
hasLabel=True, hasDescr=False, hasHelp=False,
|
||||||
hasHeaders=False, group=None, colspan=1, valign='top'):
|
hasHeaders=False, group=None, colspan=1, align='center',
|
||||||
|
valign='top'):
|
||||||
self.name = name
|
self.name = name
|
||||||
# In its simpler form, field "columns" below can hold a list or tuple
|
# In its simpler form, field "columns" below can hold a list or tuple
|
||||||
# of column widths expressed as strings, that will be given as is in
|
# of column widths expressed as strings, that will be given as is in
|
||||||
|
@ -66,6 +67,7 @@ class Group:
|
||||||
# If the group is rendered into another group, we can specify the number
|
# If the group is rendered into another group, we can specify the number
|
||||||
# of columns that this group will span.
|
# of columns that this group will span.
|
||||||
self.colspan = colspan
|
self.colspan = colspan
|
||||||
|
self.align = align
|
||||||
self.valign = valign
|
self.valign = valign
|
||||||
if style == 'tabs':
|
if style == 'tabs':
|
||||||
# Group content will be rendered as tabs. In this case, some
|
# Group content will be rendered as tabs. In this case, some
|
||||||
|
@ -351,26 +353,7 @@ class Type:
|
||||||
self.type = self.__class__.__name__
|
self.type = self.__class__.__name__
|
||||||
self.pythonType = None # The True corresponding Python type
|
self.pythonType = None # The True corresponding Python type
|
||||||
# Get the layouts. Consult layout.py for more info about layouts.
|
# Get the layouts. Consult layout.py for more info about layouts.
|
||||||
areDefaultLayouts = False
|
self.layouts = self.formatLayouts(layouts)
|
||||||
if not layouts:
|
|
||||||
# Get the default layouts as defined by the subclass
|
|
||||||
areDefaultLayouts = True
|
|
||||||
layouts = self.getDefaultLayouts()
|
|
||||||
if not layouts:
|
|
||||||
# Get the global default layouts
|
|
||||||
layouts = copy.deepcopy(defaultFieldLayouts)
|
|
||||||
else:
|
|
||||||
layouts = copy.deepcopy(layouts)
|
|
||||||
# We make copies of layouts, because every layout can be different,
|
|
||||||
# even if the user decides to reuse one from one field to another.
|
|
||||||
# This is because we modify every layout for adding
|
|
||||||
# master/slave-related info, focus-related info, etc, which can be
|
|
||||||
# different from one field to the other.
|
|
||||||
# Express the layouts in a standardized way.
|
|
||||||
self.layouts = self.formatLayouts(layouts, areDefaultLayouts)
|
|
||||||
self.hasLabel = self.hasLayoutElement('l')
|
|
||||||
self.hasDescr = self.hasLayoutElement('d')
|
|
||||||
self.hasHelp = self.hasLayoutElement('h')
|
|
||||||
# Can we filter this field?
|
# Can we filter this field?
|
||||||
self.filterable = False
|
self.filterable = False
|
||||||
# Can this field have values that can be edited and validated?
|
# Can this field have values that can be edited and validated?
|
||||||
|
@ -451,21 +434,57 @@ class Type:
|
||||||
else:
|
else:
|
||||||
res = self.show
|
res = self.show
|
||||||
# Take into account possible values 'view' and 'edit' for 'show' param.
|
# Take into account possible values 'view' and 'edit' for 'show' param.
|
||||||
if (res == 'view' and isEdit) or (res == 'edit' and not isEdit):
|
if res == 'view':
|
||||||
res = False
|
if isEdit: res = False
|
||||||
|
else: res = True
|
||||||
|
elif res == 'edit':
|
||||||
|
if isEdit: res = True
|
||||||
|
else: res = False
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def formatLayouts(self, layouts, areDefault):
|
def showPage(self, obj):
|
||||||
'''Standardizes the given dict of p_layouts. p_areDefault is True if
|
'''Must the page where this field lies be shown ? "Show value" can be
|
||||||
p_layouts are the global default layouts or a subclass-specific set
|
True, False or 'view' (page is available only in "view" mode).'''
|
||||||
of default layouts.'''
|
if callable(self.pageShow):
|
||||||
# Create a Table instance for every simple layout string.
|
return self.pageShow(obj.appy())
|
||||||
|
else:
|
||||||
|
return self.pageShow
|
||||||
|
|
||||||
|
def formatLayouts(self, layouts):
|
||||||
|
'''Standardizes the given p_layouts. .'''
|
||||||
|
# First, get the layouts as a dictionary, if p_layouts is None or
|
||||||
|
# expressed as a simple string.
|
||||||
|
areDefault = False
|
||||||
|
if not layouts:
|
||||||
|
# Get the default layouts as defined by the subclass
|
||||||
|
areDefault = True
|
||||||
|
layouts = self.getDefaultLayouts()
|
||||||
|
if not layouts:
|
||||||
|
# Get the global default layouts
|
||||||
|
layouts = copy.deepcopy(defaultFieldLayouts)
|
||||||
|
else:
|
||||||
|
if isinstance(layouts, basestring) or isinstance(layouts, Table):
|
||||||
|
# The user specified a single layoutString (the "edit" one)
|
||||||
|
layouts = {'edit': layouts}
|
||||||
|
else:
|
||||||
|
layouts = copy.deepcopy(layouts)
|
||||||
|
# Here, we make a copy of the layouts, because every layout can
|
||||||
|
# be different, even if the user decides to reuse one from one
|
||||||
|
# field to another. This is because we modify every layout for
|
||||||
|
# adding master/slave-related info, focus-related info, etc,
|
||||||
|
# which can be different from one field to the other.
|
||||||
|
# We have now a dict of layouts in p_layouts. Ensure now that a Table
|
||||||
|
# instance is created for every layout (=value from the dict). Indeed,
|
||||||
|
# a layout could have been expressed as a simple layout string.
|
||||||
for layoutType in layouts.iterkeys():
|
for layoutType in layouts.iterkeys():
|
||||||
if isinstance(layouts[layoutType], basestring):
|
if isinstance(layouts[layoutType], basestring):
|
||||||
layouts[layoutType] = Table(layouts[layoutType])
|
layouts[layoutType] = Table(layouts[layoutType])
|
||||||
# Create the "cell" layout if not specified.
|
# Create the "view" layout from the "edit" layout if not specified
|
||||||
|
if 'view' not in layouts:
|
||||||
|
layouts['view'] = Table(other=layouts['edit'], derivedType='view')
|
||||||
|
# Create the "cell" layout from the 'view' layout if not specified.
|
||||||
if 'cell' not in layouts:
|
if 'cell' not in layouts:
|
||||||
layouts['cell'] = Table('f')
|
layouts['cell'] = Table(other=layouts['view'], derivedType='cell')
|
||||||
# Put the required CSS classes in the layouts
|
# Put the required CSS classes in the layouts
|
||||||
layouts['cell'].addCssClasses('no-style-table')
|
layouts['cell'].addCssClasses('no-style-table')
|
||||||
if self.master:
|
if self.master:
|
||||||
|
@ -473,7 +492,12 @@ class Type:
|
||||||
# allowing to show/hide, in Javascript, its widget according to
|
# allowing to show/hide, in Javascript, its widget according to
|
||||||
# master value.
|
# master value.
|
||||||
classes = 'slave_%s' % self.master.id
|
classes = 'slave_%s' % self.master.id
|
||||||
classes += ' slaveValue_%s_%s' % (self.master.id, self.masterValue)
|
if type(self.masterValue) not in sequenceTypes:
|
||||||
|
masterValues = [self.masterValue]
|
||||||
|
else:
|
||||||
|
masterValues = self.masterValue
|
||||||
|
for masterValue in masterValues:
|
||||||
|
classes += ' slaveValue_%s_%s' % (self.master.id, masterValue)
|
||||||
layouts['view'].addCssClasses(classes)
|
layouts['view'].addCssClasses(classes)
|
||||||
layouts['edit'].addCssClasses(classes)
|
layouts['edit'].addCssClasses(classes)
|
||||||
if self.focus:
|
if self.focus:
|
||||||
|
@ -489,17 +513,21 @@ class Type:
|
||||||
if not self.required:
|
if not self.required:
|
||||||
for layoutType in layouts.iterkeys():
|
for layoutType in layouts.iterkeys():
|
||||||
layouts[layoutType].removeElement('r')
|
layouts[layoutType].removeElement('r')
|
||||||
|
# Derive some boolean values from the layouts.
|
||||||
|
self.hasLabel = self.hasLayoutElement('l', layouts)
|
||||||
|
self.hasDescr = self.hasLayoutElement('d', layouts)
|
||||||
|
self.hasHelp = self.hasLayoutElement('h', layouts)
|
||||||
# Store Table instance's dicts instead of instances: this way, they can
|
# Store Table instance's dicts instead of instances: this way, they can
|
||||||
# be manipulated in ZPTs.
|
# be manipulated in ZPTs.
|
||||||
for layoutType in layouts.iterkeys():
|
for layoutType in layouts.iterkeys():
|
||||||
layouts[layoutType] = layouts[layoutType].get()
|
layouts[layoutType] = layouts[layoutType].get()
|
||||||
return layouts
|
return layouts
|
||||||
|
|
||||||
def hasLayoutElement(self, element):
|
def hasLayoutElement(self, element, layouts):
|
||||||
'''This method returns True if the given layout p_element can be found
|
'''This method returns True if the given layout p_element can be found
|
||||||
at least once among the various p_layouts defined for this field.'''
|
at least once among the various p_layouts defined for this field.'''
|
||||||
for layout in self.layouts.itervalues():
|
for layout in layouts.itervalues():
|
||||||
if element in layout['layoutString']: return True
|
if element in layout.layoutString: return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getDefaultLayouts(self):
|
def getDefaultLayouts(self):
|
||||||
|
@ -1161,14 +1189,16 @@ class Ref(Type):
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
|
||||||
def isShowable(self, obj, layoutType):
|
def isShowable(self, obj, layoutType):
|
||||||
res = Type.isShowable(self, obj, layout)
|
res = Type.isShowable(self, obj, layoutType)
|
||||||
if not res: return res
|
if not res: return res
|
||||||
|
# We add here specific Ref rules for preventing to show the field under
|
||||||
|
# some inappropriate circumstances.
|
||||||
if (layoutType == 'edit') and self.add: return False
|
if (layoutType == 'edit') and self.add: return False
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
if layoutType == 'edit': return False
|
if layoutType == 'edit': return False
|
||||||
else:
|
else:
|
||||||
return obj.getBRefs(self.relationship)
|
return obj.getBRefs(self.relationship)
|
||||||
return True
|
return res
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
if self.isBack:
|
if self.isBack:
|
||||||
|
@ -1622,6 +1652,6 @@ class Config:
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Special field "type" is mandatory for every class. If one class does not
|
# Special field "type" is mandatory for every class. If one class does not
|
||||||
# define it, we will add a copy of the instance defined below.
|
# define it, we will add a copy of the instance defined below.
|
||||||
title = String(multiplicity=(1,1), indexed=True, show='edit')
|
title = String(multiplicity=(1,1), show='edit')
|
||||||
title.init('title', None, 'appy')
|
title.init('title', None, 'appy')
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -118,21 +118,47 @@ class Row(LayoutElement):
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Table(LayoutElement):
|
class Table(LayoutElement):
|
||||||
'''Represents a table where to dispose graphical elements.'''
|
'''Represents a table where to dispose graphical elements.'''
|
||||||
def __init__(self, layoutString, style=None, css_class='', cellpadding=0,
|
simpleParams = ('style', 'css_class', 'cellpadding', 'cellspacing', 'width',
|
||||||
cellspacing=0, width='100%', align='left'):
|
'align')
|
||||||
self.style = style
|
derivedRepls = {'view': 'hrv', 'cell': 'l'}
|
||||||
self.css_class = css_class
|
def __init__(self, layoutString=None, style=None, css_class='',
|
||||||
self.cellpadding = cellpadding
|
cellpadding=0, cellspacing=0, width='100%', align='left',
|
||||||
self.cellspacing = cellspacing
|
other=None, derivedType=None):
|
||||||
self.width = width
|
if other:
|
||||||
self.align = align
|
# We need to create a Table instance from another Table instance,
|
||||||
|
# given in p_other. In this case, we ignore previous params.
|
||||||
|
if derivedType != None:
|
||||||
|
# We will not simply clone p_other. If p_derivedType is:
|
||||||
|
# - "view", p_derivedFrom is an "edit" layout, and we must
|
||||||
|
# create the corresponding "view" layout;
|
||||||
|
# - "cell", p_derivedFrom is a "view" layout, and we must
|
||||||
|
# create the corresponding "cell" layout;
|
||||||
|
self.layoutString = Table.deriveLayout(other.layoutString,
|
||||||
|
derivedType)
|
||||||
|
else:
|
||||||
|
self.layoutString = layoutString
|
||||||
|
source = 'other.'
|
||||||
|
else:
|
||||||
|
source = ''
|
||||||
|
self.layoutString = layoutString
|
||||||
|
# Initialise simple params, either from the true params, either from
|
||||||
|
# the p_other Table instance.
|
||||||
|
for param in Table.simpleParams:
|
||||||
|
exec 'self.%s = %s%s' % (param, source, param)
|
||||||
# The following attribute will store a special Row instance used for
|
# The following attribute will store a special Row instance used for
|
||||||
# defining column properties.
|
# defining column properties.
|
||||||
self.headerRow = None
|
self.headerRow = None
|
||||||
# The content rows are stored hereafter.
|
# The content rows will be stored hereafter.
|
||||||
self.rows = []
|
self.rows = []
|
||||||
self.layoutString = layoutString
|
self.decodeRows(self.layoutString)
|
||||||
self.decodeRows(layoutString)
|
|
||||||
|
@staticmethod
|
||||||
|
def deriveLayout(layout, derivedType):
|
||||||
|
'''Returns a layout derived from p_layout.'''
|
||||||
|
res = layout
|
||||||
|
for letter in Table.derivedRepls[derivedType]:
|
||||||
|
res = res.replace(letter, '')
|
||||||
|
return res
|
||||||
|
|
||||||
def addCssClasses(self, css_class):
|
def addCssClasses(self, css_class):
|
||||||
'''Adds a single or a group of p_css_class.'''
|
'''Adds a single or a group of p_css_class.'''
|
||||||
|
@ -186,5 +212,5 @@ class Table(LayoutElement):
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
defaultPageLayouts = {
|
defaultPageLayouts = {
|
||||||
'view': Table('m;-s|-n!-w|-b|'), 'edit': Table('m;-w|-b|')}
|
'view': Table('m;-s|-n!-w|-b|'), 'edit': Table('m;-w|-b|')}
|
||||||
defaultFieldLayouts = {'view': 'l;f!', 'edit': 'lrv;f!'}
|
defaultFieldLayouts = {'edit': 'lrv;f!'}
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -652,6 +652,7 @@ class Generator(AbstractGenerator):
|
||||||
Flavour._appy_addImportRelatedFields(classDescr)
|
Flavour._appy_addImportRelatedFields(classDescr)
|
||||||
Flavour._appy_addWorkflowFields(self.flavour)
|
Flavour._appy_addWorkflowFields(self.flavour)
|
||||||
Flavour._appy_addWorkflowFields(self.podTemplate)
|
Flavour._appy_addWorkflowFields(self.podTemplate)
|
||||||
|
Flavour._appy_addWorkflowFields(self.user)
|
||||||
# Complete self.flavour.orderedAttributes from the attributes that we
|
# Complete self.flavour.orderedAttributes from the attributes that we
|
||||||
# just added to the Flavour model class.
|
# just added to the Flavour model class.
|
||||||
for fieldName in Flavour._appy_attributes:
|
for fieldName in Flavour._appy_attributes:
|
||||||
|
|
|
@ -139,6 +139,14 @@ class PloneInstaller:
|
||||||
site.invokeFactory(self.appyFolderType, self.productName,
|
site.invokeFactory(self.appyFolderType, self.productName,
|
||||||
title=self.productName)
|
title=self.productName)
|
||||||
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
getattr(site.portal_types, self.appyFolderType).global_allow = 0
|
||||||
|
# Manager has been granted Add permissions for all root classes.
|
||||||
|
# This may not be desired, so remove this.
|
||||||
|
appFolder = getattr(site, self.productName)
|
||||||
|
for className in self.config.rootClasses:
|
||||||
|
permission = self.getAddPermission(className)
|
||||||
|
print 'Permission is', permission
|
||||||
|
appFolder.manage_permission(permission, (), acquire=0)
|
||||||
|
else:
|
||||||
appFolder = getattr(site, self.productName)
|
appFolder = getattr(site, self.productName)
|
||||||
# All roles defined as creators should be able to create the
|
# All roles defined as creators should be able to create the
|
||||||
# corresponding root content types in this folder.
|
# corresponding root content types in this folder.
|
||||||
|
|
|
@ -46,6 +46,7 @@ class AbstractMixin:
|
||||||
appyType.store(obj, value)
|
appyType.store(obj, value)
|
||||||
if created:
|
if created:
|
||||||
# Now we have a title for the object, so we derive a nice id
|
# Now we have a title for the object, so we derive a nice id
|
||||||
|
obj.unmarkCreationFlag()
|
||||||
obj._renameAfterCreation(check_auto_id=True)
|
obj._renameAfterCreation(check_auto_id=True)
|
||||||
if previousData:
|
if previousData:
|
||||||
# Keep in history potential changes on historized fields
|
# Keep in history potential changes on historized fields
|
||||||
|
@ -181,31 +182,45 @@ class AbstractMixin:
|
||||||
obj.plone_utils.addPortalMessage(msg)
|
obj.plone_utils.addPortalMessage(msg)
|
||||||
return self.goto('%s/skyn/view' % obj.absolute_url(), True)
|
return self.goto('%s/skyn/view' % obj.absolute_url(), True)
|
||||||
if rq.get('buttonPrevious.x', None):
|
if rq.get('buttonPrevious.x', None):
|
||||||
# Go to the previous page (edit mode) for this object.
|
# Go to the previous page for this object.
|
||||||
# We recompute the list of phases and pages because things
|
# We recompute the list of phases and pages because things
|
||||||
# may have changed since the object has been updated (ie,
|
# may have changed since the object has been updated (ie,
|
||||||
# additional pages may be shown or hidden now, so the next and
|
# additional pages may be shown or hidden now, so the next and
|
||||||
# previous pages may have changed).
|
# 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')
|
currentPage = rq.get('page')
|
||||||
phaseInfo = self.getAppyPhases(page=currentPage)
|
phaseInfo = self.getAppyPhases(page=currentPage)
|
||||||
previousPage = self.getPreviousPage(phaseInfo, currentPage)
|
previousPage, show = self.getPreviousPage(phaseInfo, currentPage)
|
||||||
if previousPage:
|
if previousPage:
|
||||||
|
# Return the edit or view page?
|
||||||
|
if show != 'view':
|
||||||
rq.set('page', previousPage)
|
rq.set('page', previousPage)
|
||||||
return obj.skyn.edit(obj)
|
return obj.skyn.edit(obj)
|
||||||
|
else:
|
||||||
|
urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(),
|
||||||
|
previousPage)
|
||||||
|
return self.goto(urlBack)
|
||||||
else:
|
else:
|
||||||
obj.plone_utils.addPortalMessage(msg)
|
obj.plone_utils.addPortalMessage(msg)
|
||||||
return self.goto('%s/skyn/view' % obj.absolute_url(), True)
|
return self.goto('%s/skyn/view' % obj.absolute_url())
|
||||||
if rq.get('buttonNext.x', None):
|
if rq.get('buttonNext.x', None):
|
||||||
# Go to the next page (edit mode) for this object
|
# Go to the next page for this object
|
||||||
currentPage = rq.get('page')
|
currentPage = rq.get('page')
|
||||||
phaseInfo = self.getAppyPhases(page=currentPage)
|
phaseInfo = self.getAppyPhases(page=currentPage)
|
||||||
nextPage = self.getNextPage(phaseInfo, currentPage)
|
nextPage, show = self.getNextPage(phaseInfo, currentPage)
|
||||||
if nextPage:
|
if nextPage:
|
||||||
|
# Return the edit or view page?
|
||||||
|
if show != 'view':
|
||||||
rq.set('page', nextPage)
|
rq.set('page', nextPage)
|
||||||
return obj.skyn.edit(obj)
|
return obj.skyn.edit(obj)
|
||||||
|
else:
|
||||||
|
urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(),
|
||||||
|
nextPage)
|
||||||
|
return self.goto(urlBack)
|
||||||
else:
|
else:
|
||||||
obj.plone_utils.addPortalMessage(msg)
|
obj.plone_utils.addPortalMessage(msg)
|
||||||
return self.goto('%s/skyn/view' % obj.absolute_url(), True)
|
return self.goto('%s/skyn/view' % obj.absolute_url())
|
||||||
return obj.skyn.edit(obj)
|
return obj.skyn.edit(obj)
|
||||||
|
|
||||||
def onDelete(self):
|
def onDelete(self):
|
||||||
|
@ -575,28 +590,36 @@ class AbstractMixin:
|
||||||
pageIndex = phase['pages'].index(page)
|
pageIndex = phase['pages'].index(page)
|
||||||
if pageIndex > 0:
|
if pageIndex > 0:
|
||||||
# We stay on the same phase, previous page
|
# We stay on the same phase, previous page
|
||||||
return phase['pages'][pageIndex-1]
|
res = phase['pages'][pageIndex-1]
|
||||||
|
show = phase['pageShows'][res]
|
||||||
|
return res, show
|
||||||
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']
|
||||||
return previousPhase['pages'][-1]
|
res = previousPhase['pages'][-1]
|
||||||
|
show = previousPhase['pageShows'][res]
|
||||||
|
return res, show
|
||||||
else:
|
else:
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
def getNextPage(self, phase, page):
|
def getNextPage(self, phase, page):
|
||||||
'''Returns the page that follows p_page which is in p_phase.'''
|
'''Returns the page that follows p_page which is in p_phase.'''
|
||||||
pageIndex = phase['pages'].index(page)
|
pageIndex = phase['pages'].index(page)
|
||||||
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
|
||||||
return phase['pages'][pageIndex+1]
|
res = phase['pages'][pageIndex+1]
|
||||||
|
show = phase['pageShows'][res]
|
||||||
|
return res, show
|
||||||
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']
|
||||||
return nextPhase['pages'][0]
|
res = nextPhase['pages'][0]
|
||||||
|
show = nextPhase['pageShows'][res]
|
||||||
|
return res, show
|
||||||
else:
|
else:
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta):
|
def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta):
|
||||||
'''This method changes the position of object with uid p_objectUid in
|
'''This method changes the position of object with uid p_objectUid in
|
||||||
|
@ -873,12 +896,6 @@ class AbstractMixin:
|
||||||
res = [o.appy() for o in objs]
|
res = [o.appy() for o in objs]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def _appy_showPage(self, page, pageShow):
|
|
||||||
'''Must I show p_page?'''
|
|
||||||
if callable(pageShow):
|
|
||||||
return pageShow(self.appy())
|
|
||||||
else: return pageShow
|
|
||||||
|
|
||||||
def _appy_showState(self, workflow, stateShow):
|
def _appy_showState(self, workflow, stateShow):
|
||||||
'''Must I show a state whose "show value" is p_stateShow?'''
|
'''Must I show a state whose "show value" is p_stateShow?'''
|
||||||
if callable(stateShow):
|
if callable(stateShow):
|
||||||
|
|
|
@ -78,7 +78,7 @@ class User(ModelClass):
|
||||||
firstName = String(**gm)
|
firstName = String(**gm)
|
||||||
def showLogin(self): pass
|
def showLogin(self): pass
|
||||||
def validateLogin(self): pass
|
def validateLogin(self): pass
|
||||||
login = String(show=showLogin, validator=validateLogin, **gm)
|
login = String(show=showLogin, validator=validateLogin, indexed=True, **gm)
|
||||||
def showPassword(self): pass
|
def showPassword(self): pass
|
||||||
def validatePassword(self): pass
|
def validatePassword(self): pass
|
||||||
password1 = String(format=String.PASSWORD, show=showPassword,
|
password1 = String(format=String.PASSWORD, show=showPassword,
|
||||||
|
@ -142,7 +142,7 @@ class Flavour(ModelClass):
|
||||||
res.group = copy.copy(appyType.group)
|
res.group = copy.copy(appyType.group)
|
||||||
res.phase = 'main'
|
res.phase = 'main'
|
||||||
# Set default layouts for all Flavour fields
|
# Set default layouts for all Flavour fields
|
||||||
res.layouts = None
|
res.layouts = res.formatLayouts(None)
|
||||||
res.specificReadPermission = False
|
res.specificReadPermission = False
|
||||||
res.specificWritePermission = False
|
res.specificWritePermission = False
|
||||||
res.multiplicity = (0, appyType.multiplicity[1])
|
res.multiplicity = (0, appyType.multiplicity[1])
|
||||||
|
|
|
@ -651,8 +651,8 @@
|
||||||
This macro shows the range of buttons (next, previous, save,...).
|
This macro shows the range of buttons (next, previous, save,...).
|
||||||
</tal:comment>
|
</tal:comment>
|
||||||
<div metal:define-macro="buttons"
|
<div metal:define-macro="buttons"
|
||||||
tal:define="previousPage python: contextObj.getPreviousPage(phaseInfo, page);
|
tal:define="previousPage python: contextObj.getPreviousPage(phaseInfo, page)[0];
|
||||||
nextPage python: contextObj.getNextPage(phaseInfo, page);
|
nextPage python: contextObj.getNextPage(phaseInfo, page)[0];
|
||||||
isEdit python: layoutType == 'edit';">
|
isEdit python: layoutType == 'edit';">
|
||||||
<br/>
|
<br/>
|
||||||
<tal:previousButton condition="previousPage">
|
<tal:previousButton condition="previousPage">
|
||||||
|
@ -682,7 +682,7 @@
|
||||||
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="not:isEdit">
|
<tal:editLink condition="python: not isEdit and (phaseInfo['pageShows'][page] != 'view')">
|
||||||
<img tal:define="nav request/nav|nothing;
|
<img tal:define="nav request/nav|nothing;
|
||||||
nav python: test(nav, '&nav=%s' % nav, '')"
|
nav python: test(nav, '&nav=%s' % nav, '')"
|
||||||
title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
||||||
|
|
|
@ -153,19 +153,20 @@
|
||||||
<table tal:define="phases contextObj/getAppyPhases|nothing;
|
<table tal:define="phases contextObj/getAppyPhases|nothing;
|
||||||
page python: request.get('page', 'main')"
|
page python: request.get('page', 'main')"
|
||||||
tal:condition="python: phases and not ((len(phases)==1) and len(phases[0]['pages'])==1)"
|
tal:condition="python: phases and not ((len(phases)==1) and len(phases[0]['pages'])==1)"
|
||||||
cellspacing="1" cellpadding="0" width="100%">
|
cellspacing="1" cellpadding="2" width="100%">
|
||||||
<tal:phase repeat="phase phases">
|
<tal:phase repeat="phase phases">
|
||||||
<tal:comment replace="nothing">The box containing phase-related information</tal:comment>
|
<tal:comment replace="nothing">The box containing phase-related information</tal:comment>
|
||||||
<tr>
|
<tr>
|
||||||
<td tal:define="label python:'%s_phase_%s' % (contextObj.meta_type, phase['name']);
|
<td tal:define="label python:'%s_phase_%s' % (contextObj.meta_type, phase['name']);
|
||||||
displayLink python: (phase['phaseStatus'] != 'Future') and ('/portal_factory' not in contextObj.absolute_url()) and (len(phase['pages']) == 1)"
|
displayLink python: (phase['phaseStatus'] != 'Future') and ('/portal_factory' not in contextObj.absolute_url()) and (len(phase['pages']) == 1)"
|
||||||
tal:attributes="class python: (len(phases) > 1) and ('appyPhase step%s' % phase['phaseStatus']) or 'appyPhase'">
|
tal:attributes="class python: (len(phases) > 1) and ('appyPhase step%s' % phase['phaseStatus']) or 'appyPhase'">
|
||||||
<span class="portletGroup" tal:condition="python: len(phases) > 1">
|
<div class="portletGroup" tal:condition="python: len(phases) > 1">
|
||||||
<a tal:attributes="href python: '%s?page=%s' % (contextObj.getUrl(), phase['pages'][0]);"
|
<a tal:attributes="href python: '%s?page=%s' % (contextObj.getUrl(), phase['pages'][0]);"
|
||||||
tal:condition="displayLink"
|
tal:condition="displayLink"
|
||||||
tal:content="python: tool.translate(label)"></a>
|
tal:content="python: tool.translate(label)"></a>
|
||||||
<span tal:condition="not: displayLink" tal:replace="python: tool.translate(label)"/>
|
<span tal:condition="not: displayLink" tal:replace="python: tool.translate(label)"/>
|
||||||
</span>
|
</div>
|
||||||
|
<div class="portletMenu">
|
||||||
<table width="100%" cellpadding="0" cellspacing="0"
|
<table width="100%" cellpadding="0" cellspacing="0"
|
||||||
tal:condition="python: len(phase['pages']) > 1">
|
tal:condition="python: len(phase['pages']) > 1">
|
||||||
<tr tal:repeat="aPage phase/pages" valign="top">
|
<tr tal:repeat="aPage phase/pages" valign="top">
|
||||||
|
@ -184,6 +185,7 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tal:comment replace="nothing">The down arrow pointing to the next phase (if any)</tal:comment>
|
<tal:comment replace="nothing">The down arrow pointing to the next phase (if any)</tal:comment>
|
||||||
|
|
|
@ -93,7 +93,7 @@
|
||||||
<tal:comment replace="nothing">First row: the tabs.</tal:comment>
|
<tal:comment replace="nothing">First row: the tabs.</tal:comment>
|
||||||
<tr valign="middle"><td style="border-bottom: 1px solid #ff8040">
|
<tr valign="middle"><td style="border-bottom: 1px solid #ff8040">
|
||||||
<table cellpadding="0" cellspacing="0" style="position:relative; bottom:-1px;">
|
<table cellpadding="0" cellspacing="0" style="position:relative; bottom:-1px;">
|
||||||
<tr valign="bottom">
|
<tr valign="middle">
|
||||||
<tal:tab repeat="widgetRow widget/widgets">
|
<tal:tab repeat="widgetRow widget/widgets">
|
||||||
<tal:id define="tabId python:'tab_%s_%d_%d' % (widget['name'], repeat['widgetRow'].number(), len(widget['widgets']))">
|
<tal:id define="tabId python:'tab_%s_%d_%d' % (widget['name'], repeat['widgetRow'].number(), len(widget['widgets']))">
|
||||||
<td><img tal:attributes="src string: $portal_url/skyn/tabLeft.png;
|
<td><img tal:attributes="src string: $portal_url/skyn/tabLeft.png;
|
||||||
|
@ -134,12 +134,13 @@
|
||||||
This macro displays the content of a group of widgets.
|
This macro displays the content of a group of widgets.
|
||||||
It is exclusively called by macro "group" above.
|
It is exclusively called by macro "group" above.
|
||||||
</tal:comment>
|
</tal:comment>
|
||||||
<table metal:define-macro="groupContent" align="center"
|
<table metal:define-macro="groupContent"
|
||||||
tal:attributes="width python: test(widget['wide'], '100%', '')">
|
tal:attributes="width python: test(widget['wide'], '100%', '');
|
||||||
|
align widget/align">
|
||||||
<tal:comment replace="nothing">Display the title of the group if it is not rendered a fieldset.</tal:comment>
|
<tal:comment replace="nothing">Display the title of the group if it is not rendered a fieldset.</tal:comment>
|
||||||
<tr tal:condition="python: (widget['style'] != 'fieldset') and widget['hasLabel']">
|
<tr tal:condition="python: (widget['style'] != 'fieldset') and widget['hasLabel']">
|
||||||
<td tal:attributes="colspan python: len(widget['columnsWidths']);
|
<td tal:attributes="colspan python: len(widget['columnsWidths']);
|
||||||
class widget/style">
|
class widget/style" align="left">
|
||||||
<span tal:replace="structure python: contextObj.translate(widget['labelId'])"/>
|
<span tal:replace="structure python: contextObj.translate(widget['labelId'])"/>
|
||||||
<tal:help condition="widget/hasHelp">
|
<tal:help condition="widget/hasHelp">
|
||||||
<metal:call use-macro="portal/skyn/widgets/show/macros/help"/>
|
<metal:call use-macro="portal/skyn/widgets/show/macros/help"/>
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
border-left: 1px solid #8cacbb;
|
border-left: 1px solid #8cacbb;
|
||||||
border-right: 1px solid #8cacbb;
|
border-right: 1px solid #8cacbb;
|
||||||
}
|
}
|
||||||
.portletGroup {
|
|
||||||
font-size: 85%;
|
|
||||||
padding-left: 0.7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stylesheet with Internet Explorer-specific workarounds. */
|
/* Stylesheet with Internet Explorer-specific workarounds. */
|
||||||
* html #portal-columns {
|
* html #portal-columns {
|
||||||
|
|
|
@ -6,7 +6,7 @@ textarea { width: 99%; }
|
||||||
|
|
||||||
#portal-breadcrumbs { display: none; }
|
#portal-breadcrumbs { display: none; }
|
||||||
#importedElem { color: grey; font-style: italic; }
|
#importedElem { color: grey; font-style: italic; }
|
||||||
label { font-weight: bold; font-style: italic; }
|
label { font-weight: bold; font-style: italic; line-height: 1.4em;}
|
||||||
.discreet { font-size: 94%; }
|
.discreet { font-size: 94%; }
|
||||||
.appyList { line-height: 1.1em; margin: 0 0 0.5em 1.2em; padding: 0; }
|
.appyList { line-height: 1.1em; margin: 0 0 0.5em 1.2em; padding: 0; }
|
||||||
.appyBullet { margin: 0; }
|
.appyBullet { margin: 0; }
|
||||||
|
@ -26,7 +26,7 @@ label { font-weight: bold; font-style: italic; }
|
||||||
.appyPhase {
|
.appyPhase {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
border-width: thin;
|
border-width: thin;
|
||||||
padding: 0 0.1em 0 1em;
|
padding: 0 0.6em 0 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.appyState {
|
.appyState {
|
||||||
|
@ -68,14 +68,14 @@ label { font-weight: bold; font-style: italic; }
|
||||||
background-color: #cde2a7;
|
background-color: #cde2a7;
|
||||||
background-image: url(&dtml-portal_url;/skyn/done.png);
|
background-image: url(&dtml-portal_url;/skyn/done.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: -1px 4px;
|
background-position: -1px 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepCurrent {
|
.stepCurrent {
|
||||||
background-color: #eef3f5;
|
background-color: #eef3f5;
|
||||||
background-image: url(&dtml-portal_url;/skyn/current.png);
|
background-image: url(&dtml-portal_url;/skyn/current.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: -1px 4px;
|
background-position: -1px 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stepFuture {
|
.stepFuture {
|
||||||
|
@ -224,11 +224,13 @@ th {
|
||||||
font-variant: small-caps;
|
font-variant: small-caps;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
margin: 0.3em 0 0.3em 0;
|
||||||
}
|
}
|
||||||
.portletSep { border-top: 1px dashed #8cacbb; }
|
.portletSep { border-top: 1px dashed #8cacbb; }
|
||||||
.portletGroupItem { padding-left: 0.8em; font-style: italic; }
|
.portletGroupItem { padding-left: 0.8em; font-style: italic; }
|
||||||
.portletPageItem { font-style: italic; }
|
.portletPageItem { font-style: italic; }
|
||||||
.portletCurrent { font-weight: bold; }
|
.portletCurrent { font-weight: bold; }
|
||||||
|
.portletMenu { margin-bottom: 0.4em; }
|
||||||
|
|
||||||
div.appyGrey {
|
div.appyGrey {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -63,6 +63,9 @@ class UserWrapper(AbstractWrapper):
|
||||||
# Perform updates on the corresponding Plone user
|
# Perform updates on the corresponding Plone user
|
||||||
ploneUser = self.o.portal_membership.getMemberById(self.login)
|
ploneUser = self.o.portal_membership.getMemberById(self.login)
|
||||||
ploneUser.setMemberProperties({'fullname': self.title})
|
ploneUser.setMemberProperties({'fullname': self.title})
|
||||||
|
# This object must be owned by its Plone user
|
||||||
|
if 'Owner' not in self.o.get_local_roles_for_userid(self.login):
|
||||||
|
self.o.manage_addLocalRoles(self.login, ('Owner',))
|
||||||
# Change group membership according to self.roles. Indeed, instead of
|
# Change group membership according to self.roles. Indeed, instead of
|
||||||
# granting roles directly to the user, we will add the user to a
|
# granting roles directly to the user, we will add the user to a
|
||||||
# Appy-created group having this role.
|
# Appy-created group having this role.
|
||||||
|
|
|
@ -83,6 +83,9 @@ class PhaseDescr(Descr):
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.phaseStatus = None
|
self.phaseStatus = None
|
||||||
self.pages = [] # The list of pages in this phase
|
self.pages = [] # The list of pages in this phase
|
||||||
|
# Below, a dict of "show" values (True, False or 'view') for every page
|
||||||
|
# in self.pages.
|
||||||
|
self.pageShows = {}
|
||||||
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
|
||||||
|
@ -92,9 +95,11 @@ class PhaseDescr(Descr):
|
||||||
|
|
||||||
def addPage(self, appyType, obj):
|
def addPage(self, appyType, obj):
|
||||||
toAdd = appyType.page
|
toAdd = appyType.page
|
||||||
if (toAdd not in self.pages) and \
|
if toAdd not in self.pages:
|
||||||
obj._appy_showPage(appyType.page, appyType.pageShow):
|
showValue = appyType.showPage(obj)
|
||||||
|
if showValue:
|
||||||
self.pages.append(toAdd)
|
self.pages.append(toAdd)
|
||||||
|
self.pageShows[toAdd] = showValue
|
||||||
|
|
||||||
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