Allowed to express layouts in a more concise manner and various graphical improvements.

This commit is contained in:
Gaetan Delannay 2010-09-13 21:04:10 +02:00
parent eb52c1bb7d
commit 0b4f6e1f79
13 changed files with 187 additions and 96 deletions

View file

@ -652,6 +652,7 @@ class Generator(AbstractGenerator):
Flavour._appy_addImportRelatedFields(classDescr)
Flavour._appy_addWorkflowFields(self.flavour)
Flavour._appy_addWorkflowFields(self.podTemplate)
Flavour._appy_addWorkflowFields(self.user)
# Complete self.flavour.orderedAttributes from the attributes that we
# just added to the Flavour model class.
for fieldName in Flavour._appy_attributes:

View file

@ -139,7 +139,15 @@ class PloneInstaller:
site.invokeFactory(self.appyFolderType, self.productName,
title=self.productName)
getattr(site.portal_types, self.appyFolderType).global_allow = 0
appFolder = getattr(site, self.productName)
# 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)
# All roles defined as creators should be able to create the
# corresponding root content types in this folder.
i = -1

View file

@ -46,6 +46,7 @@ class AbstractMixin:
appyType.store(obj, value)
if created:
# Now we have a title for the object, so we derive a nice id
obj.unmarkCreationFlag()
obj._renameAfterCreation(check_auto_id=True)
if previousData:
# Keep in history potential changes on historized fields
@ -181,31 +182,45 @@ class AbstractMixin:
obj.plone_utils.addPortalMessage(msg)
return self.goto('%s/skyn/view' % obj.absolute_url(), True)
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
# may have changed since the object has been updated (ie,
# 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')
phaseInfo = self.getAppyPhases(page=currentPage)
previousPage = self.getPreviousPage(phaseInfo, currentPage)
previousPage, show = self.getPreviousPage(phaseInfo, currentPage)
if previousPage:
rq.set('page', previousPage)
return obj.skyn.edit(obj)
# Return the edit or view page?
if show != 'view':
rq.set('page', previousPage)
return obj.skyn.edit(obj)
else:
urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(),
previousPage)
return self.goto(urlBack)
else:
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):
# Go to the next page (edit mode) for this object
# Go to the next page for this object
currentPage = rq.get('page')
phaseInfo = self.getAppyPhases(page=currentPage)
nextPage = self.getNextPage(phaseInfo, currentPage)
nextPage, show = self.getNextPage(phaseInfo, currentPage)
if nextPage:
rq.set('page', nextPage)
return obj.skyn.edit(obj)
# Return the edit or view page?
if show != 'view':
rq.set('page', nextPage)
return obj.skyn.edit(obj)
else:
urlBack = '%s/skyn/view?page=%s' % (obj.absolute_url(),
nextPage)
return self.goto(urlBack)
else:
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)
def onDelete(self):
@ -575,28 +590,36 @@ class AbstractMixin:
pageIndex = phase['pages'].index(page)
if pageIndex > 0:
# 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:
if phase['previousPhase']:
# We go to the last page of previous phase
previousPhase = phase['previousPhase']
return previousPhase['pages'][-1]
res = previousPhase['pages'][-1]
show = previousPhase['pageShows'][res]
return res, show
else:
return None
return None, None
def getNextPage(self, phase, page):
'''Returns the page that follows p_page which is in p_phase.'''
pageIndex = phase['pages'].index(page)
if pageIndex < len(phase['pages'])-1:
# 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:
if phase['nextPhase']:
# We go to the first page of next phase
nextPhase = phase['nextPhase']
return nextPhase['pages'][0]
res = nextPhase['pages'][0]
show = nextPhase['pageShows'][res]
return res, show
else:
return None
return None, None
def changeRefOrder(self, fieldName, objectUid, newIndex, isDelta):
'''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]
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):
'''Must I show a state whose "show value" is p_stateShow?'''
if callable(stateShow):

View file

@ -78,7 +78,7 @@ class User(ModelClass):
firstName = String(**gm)
def showLogin(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 validatePassword(self): pass
password1 = String(format=String.PASSWORD, show=showPassword,
@ -142,7 +142,7 @@ class Flavour(ModelClass):
res.group = copy.copy(appyType.group)
res.phase = 'main'
# Set default layouts for all Flavour fields
res.layouts = None
res.layouts = res.formatLayouts(None)
res.specificReadPermission = False
res.specificWritePermission = False
res.multiplicity = (0, appyType.multiplicity[1])

View file

@ -651,8 +651,8 @@
This macro shows the range of buttons (next, previous, save,...).
</tal:comment>
<div metal:define-macro="buttons"
tal:define="previousPage python: contextObj.getPreviousPage(phaseInfo, page);
nextPage python: contextObj.getNextPage(phaseInfo, page);
tal:define="previousPage python: contextObj.getPreviousPage(phaseInfo, page)[0];
nextPage python: contextObj.getNextPage(phaseInfo, page)[0];
isEdit python: layoutType == 'edit';">
<br/>
<tal:previousButton condition="previousPage">
@ -682,7 +682,7 @@
tal:attributes="src string:$portal_url/skyn/cancel.png"/>
</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;
nav python: test(nav, '&nav=%s' % nav, '')"
title="Edit" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"

View file

@ -153,19 +153,20 @@
<table tal:define="phases contextObj/getAppyPhases|nothing;
page python: request.get('page', 'main')"
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:comment replace="nothing">The box containing phase-related information</tal:comment>
<tr>
<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)"
tal:attributes="class python: (len(phases) &gt; 1) and ('appyPhase step%s' % phase['phaseStatus']) or 'appyPhase'">
<span class="portletGroup" tal:condition="python: len(phases) &gt; 1">
<div class="portletGroup" tal:condition="python: len(phases) &gt; 1">
<a tal:attributes="href python: '%s?page=%s' % (contextObj.getUrl(), phase['pages'][0]);"
tal:condition="displayLink"
tal:content="python: tool.translate(label)"></a>
<span tal:condition="not: displayLink" tal:replace="python: tool.translate(label)"/>
</span>
</div>
<div class="portletMenu">
<table width="100%" cellpadding="0" cellspacing="0"
tal:condition="python: len(phase['pages']) &gt; 1">
<tr tal:repeat="aPage phase/pages" valign="top">
@ -184,6 +185,7 @@
</td>
</tr>
</table>
</div>
</td>
</tr>
<tal:comment replace="nothing">The down arrow pointing to the next phase (if any)</tal:comment>

View file

@ -93,7 +93,7 @@
<tal:comment replace="nothing">First row: the tabs.</tal:comment>
<tr valign="middle"><td style="border-bottom: 1px solid #ff8040">
<table cellpadding="0" cellspacing="0" style="position:relative; bottom:-1px;">
<tr valign="bottom">
<tr valign="middle">
<tal:tab repeat="widgetRow 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;
@ -134,12 +134,13 @@
This macro displays the content of a group of widgets.
It is exclusively called by macro "group" above.
</tal:comment>
<table metal:define-macro="groupContent" align="center"
tal:attributes="width python: test(widget['wide'], '100%', '')">
<table metal:define-macro="groupContent"
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>
<tr tal:condition="python: (widget['style'] != 'fieldset') and widget['hasLabel']">
<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'])"/>
<tal:help condition="widget/hasHelp">
<metal:call use-macro="portal/skyn/widgets/show/macros/help"/>

View file

@ -4,10 +4,6 @@
border-left: 1px solid #8cacbb;
border-right: 1px solid #8cacbb;
}
.portletGroup {
font-size: 85%;
padding-left: 0.7em;
}
/* Stylesheet with Internet Explorer-specific workarounds. */
* html #portal-columns {

View file

@ -6,7 +6,7 @@ textarea { width: 99%; }
#portal-breadcrumbs { display: none; }
#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%; }
.appyList { line-height: 1.1em; margin: 0 0 0.5em 1.2em; padding: 0; }
.appyBullet { margin: 0; }
@ -26,7 +26,7 @@ label { font-weight: bold; font-style: italic; }
.appyPhase {
border-style: dashed;
border-width: thin;
padding: 0 0.1em 0 1em;
padding: 0 0.6em 0 1em;
}
.appyState {
@ -68,14 +68,14 @@ label { font-weight: bold; font-style: italic; }
background-color: #cde2a7;
background-image: url(&dtml-portal_url;/skyn/done.png);
background-repeat: no-repeat;
background-position: -1px 4px;
background-position: -1px 7px;
}
.stepCurrent {
background-color: #eef3f5;
background-image: url(&dtml-portal_url;/skyn/current.png);
background-repeat: no-repeat;
background-position: -1px 4px;
background-position: -1px 7px;
}
.stepFuture {
@ -224,11 +224,13 @@ th {
font-variant: small-caps;
font-weight: bold;
font-style: normal;
margin: 0.3em 0 0.3em 0;
}
.portletSep { border-top: 1px dashed #8cacbb; }
.portletGroupItem { padding-left: 0.8em; font-style: italic; }
.portletPageItem { font-style: italic; }
.portletCurrent { font-weight: bold; }
.portletMenu { margin-bottom: 0.4em; }
div.appyGrey {
display: none;

View file

@ -63,6 +63,9 @@ class UserWrapper(AbstractWrapper):
# Perform updates on the corresponding Plone user
ploneUser = self.o.portal_membership.getMemberById(self.login)
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
# granting roles directly to the user, we will add the user to a
# Appy-created group having this role.