[gen] Ergonomic improvements: added a breadcrumb, more compact design.

This commit is contained in:
Gaetan Delannay 2012-11-29 20:45:21 +01:00
parent 3ec1270fc2
commit 387fbaea7c
11 changed files with 137 additions and 99 deletions

View file

@ -1172,9 +1172,8 @@ class String(Type):
# Default width, height and maxChars vary according to String format
if width == None:
if format == String.TEXT: self.width = 60
# This width corresponds to the standard width of an Appy page,
# minus the portlet.
if format == String.XHTML: self.width = 750
# This width corresponds to the standard width of an Appy page.
if format == String.XHTML: self.width = 870
else: self.width = 30
if height == None:
if format == String.TEXT: self.height = 5

View file

@ -217,13 +217,13 @@ class Table(LayoutElement):
# Some base layouts to use, for fields and pages -------------------------------
# The default layouts for pages
defaultPageLayouts = {
'view': Table('n!-w|-b|', align="center"),
'view': Table('w|-b|', align="center"),
'edit': Table('w|-b|', width=None)}
# A layout for pages, containing the page summary.
summaryPageLayouts = {'view': Table('s-n!-w|-b|', align="center"),
summaryPageLayouts = {'view': Table('s-w|-b|', align="center"),
'edit': Table('w|-b|', width=None)}
widePageLayouts = {
'view': Table('n!-w|-b|', align="center"),
'view': Table('w|-b|', align="center"),
'edit': Table('w|-b|')
}
# The default layout for fields. Alternative layouts may exist and are declared

View file

@ -245,7 +245,10 @@ class ToolMixin(BaseMixin):
elems = sortMethod(elems)
return [importParams['headers'], elems]
def showPortlet(self, context):
def showPortlet(self, context, layoutType):
'''When must the portlet be shown?'''
# Not on 'edit' pages.
if layoutType == 'edit': return
if context.id == 'ui': context = context.getParentNode()
res = True
if hasattr(context.aq_base, 'appy'):
@ -424,15 +427,27 @@ class ToolMixin(BaseMixin):
return '<acronym title="%s">%s</acronym>' % \
(text, uText[:width].encode('utf-8') + '...')
def getPublishedObject(self):
def getLayoutType(self):
'''Guess the current layout type, according to actual URL.'''
actualUrl = self.REQUEST['ACTUAL_URL']
res = ''
if actualUrl.endswith('/ui/view'):
res = 'view'
elif actualUrl.endswith('/ui/edit') or actualUrl.endswith('/do'):
res = 'edit'
return res
def getPublishedObject(self, layoutType):
'''Gets the currently published object, if its meta_class is among
application classes.'''
req = self.REQUEST
# If we are querying object, there is no published object (the truth is:
# the tool is the currently published object but we don't want to
# consider it this way).
if not req['ACTUAL_URL'].endswith('/ui/view'): return
# In some situations (ie, we are querying objects), the published object
# according to Zope is the tool, but we don't want to consider it that
# way.
if layoutType not in ('edit', 'view'): return
obj = self.REQUEST['PUBLISHED']
# If URL is a /do, published object is the "do" method.
if type(obj) == types.MethodType: obj = obj.im_self
else:
parent = obj.getParentNode()
if parent.id == 'ui': obj = parent.getParentNode()
if obj.meta_type in self.getProductConfig().attributes: return obj

View file

@ -822,6 +822,11 @@ class BaseMixin:
break
return phase
else:
# Return an empty list if we have a single, link-free page within
# a single phase.
if (len(res) == 1) and (len(res[0]['pages']) == 1) and \
not res[0]['pagesInfo'][res[0]['pages'][0]].get('links'):
return None
return res
def getIcons(self):
@ -1434,7 +1439,16 @@ class BaseMixin:
# Not-Managers can't navigate back to the tool
if (parent.id == 'config') and not self.getUser().has_role('Manager'):
return False
if parent.meta_type != 'Folder': return parent
if parent.meta_type not in ('Folder', 'Temporary Folder'): return parent
def getBreadCrumb(self):
'''Gets breadcrumb info about this object and its parents.'''
res = [{'url': self.absolute_url(),
'title': self.getFieldValue('title', layoutType='view')}]
parent = self.getParent()
if parent:
res = parent.getBreadCrumb() + res
return res
def index_html(self):
'''Redirects to /ui.'''

View file

@ -57,6 +57,9 @@ img { border: 0; vertical-align: middle}
.userStripText { font-size: 110%; padding: 0 0.3em 0 0.3em; color: white }
.userStrip a { color: #e7e7e7 }
.userStrip a:visited { color: #e7e7e7 }
.navigate { border-bottom: 1px solid #5F7983;
background-color: #dbdde1; font-weight: bold }
.navigate td { padding: 4px 9px }
.login { margin-top: 2px; margin-bottom: 2px; color: black;}
.buttons { margin-left: 4px;}
.fakeButton { border: 1px solid #D7DEE4; background-color: #fde8e0;
@ -73,7 +76,7 @@ img { border: 0; vertical-align: middle}
.lostPassword a { font-size: 90%; color: white; padding-left: 1em;}
.portlet { width: 150px; border-right: 1px solid #5F7983;
background-color: #ededed}
.portletContent { margin: 9px; }
.portletContent { margin: 4px 9px }
.portletTitle { font-weight: bold; font-size: 110%; margin-bottom: 4px;}
.portletCurrent { font-weight: bold; }
.portletSep { border-top: 1px solid #5F7983; margin-top: 2px;}
@ -113,21 +116,20 @@ img { border: 0; vertical-align: middle}
text-align: center; color: white; }
.odd { background-color: #f9f9f9; }
.even { background-color: #f4f4f4; }
.summary {margin-bottom: 5px;}
.summary { margin-bottom: 5px; background-color: #e9e9e9;
border: 1px dashed grey }
.objectTitle { font-size: 11pt; border-bottom: 3px solid grey;
font-weight: bold;}
.by { padding-top: 5px; color: grey; font-size: 97% }
.underTitle { background-color: #e9e9e9 }
.objectNavigate { margin-top: 3px;}
.by { padding: 5px; color: grey; font-size: 97% }
.underline { border-bottom: 1px dotted grey;}
.state { font-weight: bold; border-bottom: 1px dashed grey;}
.historyLabel { font-variant: small-caps; font-weight: bold;}
.history td { border-top: 1px solid grey;}
.history th { font-style: italic; text-align; left;}
.history td { border-top: 1px solid grey; padding: 0 5px 0 5px }
.history th { font-style: italic; text-align: left; padding: 0 5px 0 5px }
.topSpace { margin-top: 15px;}
.bottomSpace { margin-bottom: 15px;}
.discreet { color: grey}
.pageLink { padding-left: 6px; font-style: italic}
.pageLink { padding-left: 8px }
.footer { font-size: 95% }
.footer td { background-color: #CBCBC9; border-top: 1px solid grey;
padding: 0.4em 1em 0.5em }

View file

@ -1,10 +1,8 @@
<tal:main define="tool context/getTool">
<html metal:use-macro="context/ui/template/macros/main">
<metal:fill fill-slot="content"
tal:define="contextObj context/getParentNode;
dummy python: contextObj.allows('Modify portal content', raiseError=True);
tal:define="dummy python: contextObj.allows('Modify portal content', raiseError=True);
errors request/errors | python:{};
layoutType python:'edit';
layout python: contextObj.getPageLayout(layoutType);
cssJs python: {};
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType=layoutType);

View file

@ -46,13 +46,12 @@
</table>
</div>
<div metal:define-macro="objectNavigate" tal:condition="request/nav|nothing">
<tal:comment replace="nothing">
<tal:comment replace="nothing">
Buttons for going to next/previous elements if this one is among bunch of referenced or searched objects.
currentNumber starts with 1.
</tal:comment>
<table class="objectNavigate"
tal:define="navInfo tool/getNavigationInfo;
</tal:comment>
<metal:nav define-macro="objectNavigate" tal:condition="request/nav|nothing">
<div tal:define="navInfo tool/getNavigationInfo;
currentNumber navInfo/currentNumber;
totalNumber navInfo/totalNumber;
firstUrl navInfo/firstUrl;
@ -61,36 +60,56 @@
lastUrl navInfo/lastUrl;
sourceUrl navInfo/sourceUrl;
backText navInfo/backText">
<tr valign="middle">
<tal:comment replace="nothing">Go to the source URL (search or referred object)</tal:comment>
<td tal:condition="sourceUrl"><a tal:attributes="href sourceUrl"><img
<a tal:condition="sourceUrl" tal:attributes="href sourceUrl"><img
tal:define="gotoSource python: _('goto_source');
goBack python: backText and ('%s - %s' % (backText, gotoSource)) or gotoSource"
tal:attributes="src string: $appUrl/ui/gotoSource.png; title goBack"/></a></td>
tal:attributes="src string: $appUrl/ui/gotoSource.png; title goBack"/></a>
<tal:comment replace="nothing">Go to the first page</tal:comment>
<td tal:condition="firstUrl"><a tal:attributes="href firstUrl"><img
<a tal:condition="firstUrl" tal:attributes="href firstUrl"><img
tal:attributes="src string: $appUrl/ui/arrowLeftDouble.png;
title python: _('goto_first')"/></a></td>
title python: _('goto_first')"/></a>
<tal:comment replace="nothing">Go to the previous page</tal:comment>
<td tal:condition="previousUrl"><a tal:attributes="href previousUrl"><img
<a tal:condition="previousUrl" tal:attributes="href previousUrl"><img
tal:attributes="src string: $appUrl/ui/arrowLeftSimple.png;
title python: _('goto_previous')"/></a></td>
title python: _('goto_previous')"/></a>
<tal:comment replace="nothing">Explain which element is currently shown</tal:comment>
<td class="discreet">&nbsp;
<span class="discreet">&nbsp;
<span tal:replace="python: currentNumber"/>&nbsp;<b>//</b>
<span tal:replace="python: totalNumber"/>&nbsp;&nbsp;
</td>
</span>
<tal:comment replace="nothing">Go to the next page</tal:comment>
<td tal:condition="python: nextUrl"><a tal:attributes="href nextUrl"><img
<a tal:condition="python: nextUrl" tal:attributes="href nextUrl"><img
tal:attributes="src string: $appUrl/ui/arrowRightSimple.png;
title python: _('goto_next')"/></a></td>
title python: _('goto_next')"/></a>
<tal:comment replace="nothing">Go to the last page</tal:comment>
<td tal:condition="lastUrl"><a tal:attributes="href lastUrl"><img
<a tal:condition="lastUrl" tal:attributes="href lastUrl"><img
tal:attributes="src string: $appUrl/ui/arrowRightDouble.png;
title python: _('goto_last')"/></a></td>
title python: _('goto_last')"/></a>
</div>
</metal:nav>
<table metal:define-macro="navigationStrip" tal:condition="python: contextObj"
width="100%" class="navigate">
<tr>
<td tal:define="breadcrumb contextObj/getBreadCrumb">
<tal:bc repeat="bc breadcrumb">
<tal:elem define="nb repeat/bc/number">
<tal:sep condition="python: nb != 1">
<img tal:attributes="src string: $appUrl/ui/to.png"/>
</tal:sep>
<tal:comment replace="nothing">Display only the title of the current object</tal:comment>
<span tal:condition="python: nb == len(breadcrumb)" tal:content="bc/title"></span>
<tal:comment replace="nothing">Display a link for parent objects</tal:comment>
<a tal:condition="python: nb != len(breadcrumb)" tal:attributes="href bc/url" tal:content="bc/title"></a>
</tal:elem>
</tal:bc>
</td>
<td align="right">
<metal:nav use-macro="app/ui/navigate/macros/objectNavigate"/>
</td>
</tr>
</table>
</div>
</table>
<tal:comment replace="nothing">
This macro displays up/down arrows in a table header column for sorting a given column.

View file

@ -183,14 +183,8 @@
<tal:comment replace="nothing">Information that is common to all tabs (object title, state, etc)</tal:comment>
<table width="100%" class="summary">
<tr>
<tal:comment replace="nothing">Title</tal:comment>
<td colspan="2" class="objectTitle">
<tal:icons replace="structure contextObj/getIcons"/>
<span tal:content="python: contextObj.getFieldValue('title', layoutType='view')"></span>
</td>
</tr>
<tr class="underTitle">
<td colspan="2" class="by">
<tal:icons replace="structure contextObj/getIcons"/>
<tal:comment replace="nothing">Creator and last modification date</tal:comment>
<tal:comment replace="nothing">Plus/minus icon for accessing history</tal:comment>
<tal:accessHistory condition="hasHistory">
@ -225,7 +219,7 @@
</td>
</tr>
<tal:comment replace="nothing">Object history</tal:comment>
<tr tal:condition="hasHistory" class="underTitle">
<tr tal:condition="hasHistory">
<td colspan="2">
<span id="appyHistory"
tal:attributes="style python:test(historyExpanded, 'display:block', 'display:none')">

View file

@ -50,25 +50,19 @@
appUrl app/absolute_url;
currentSearch req/search|nothing;
currentClass req/className|nothing;
contextObj tool/getPublishedObject;
rootClasses tool/getRootClasses">
rootClasses tool/getRootClasses;
phases python: contextObj and contextObj.getAppyPhases() or None">
<div class="portletContent" tal:condition="python: contextObj and contextObj.mayNavigate()">
<div class="portletTitle" tal:define="parent contextObj/getParent">
<span tal:replace="contextObj/Title"></span>
<a tal:condition="python: parent" tal:attributes="href parent/absolute_url">
<img tal:attributes="src string: $appUrl/ui/gotoSource.png"/>
</a>
</div>
<tal:phases condition="python: contextObj and phases and contextObj.mayNavigate()">
<metal:phases use-macro="app/ui/portlet/macros/phases"/>
</div>
</tal:phases>
<tal:comment replace="nothing">One section for every searchable root class.</tal:comment>
<tal:section repeat="rootClass python: [rc for rc in rootClasses if tool.userMaySearch(rc)]">
<tal:comment replace="nothing">A separator if required</tal:comment>
<div class="portletSep" tal:define="nb repeat/rootClass/number"
tal:condition="python: (nb == 1 and contextObj) or (nb != 1)"></div>
tal:condition="python: (nb != 1) or ((nb == 1) and phases)"></div>
<div class="portletContent" tal:define="searchInfo python: tool.getGroupedSearches(rootClass)">
<tal:comment replace="nothing">Section title (link triggers the default search), with action icons</tal:comment>
@ -119,9 +113,8 @@
This macro displays, within the portlet, the navigation tree for the
currently shown object, made of phases and contained pages.
</tal:comment>
<table metal:define-macro="phases"
tal:define="phases contextObj/getAppyPhases;
singlePhase python: len(phases) == 1;
<table metal:define-macro="phases" class="portletContent"
tal:define="singlePhase python: phases and (len(phases) == 1);
page python: req.get('page', 'main')">
<tal:phase repeat="phase phases">
<tal:comment replace="nothing">The box containing phase-related information</tal:comment>

View file

@ -10,6 +10,8 @@
req python: request;
resp req/RESPONSE;
lang tool/getUserLanguage;
layoutType tool/getLayoutType;
contextObj python: tool.getPublishedObject(layoutType);
dir python: tool.getLanguageDirection(lang);
dleft python: (dir == 'ltr') and 'left' or 'right';
dright python: (dir == 'ltr') and 'right' or 'left';
@ -35,7 +37,7 @@
<tr class="top" metal:define-slot="top">
<td tal:define="bannerName python: (dir == 'ltr') and 'banner' or 'bannerrtl'"
tal:attributes="style python: 'background-image: url(%s/ui/%s.jpg)' % (appUrl, bannerName)">
<table width="100%">
<table width="100%" style="margin-top: 4px">
<tr valign="top" tal:define="pages tool/getMainPages">
<tal:comment replace="nothing">Links to main pages</tal:comment>
<td tal:condition="pages"><a tal:repeat="page pages" class="pageLink"
@ -183,12 +185,16 @@
</table>
</td>
</tr>
<tal:comment replace="nothing">The navigation strip</tal:comment>
<tr tal:condition="python: contextObj and (layoutType == 'view')">
<td><metal:navigate use-macro="app/ui/navigate/macros/navigationStrip"/></td>
</tr>
<tr>
<td>
<table width="100%" cellpadding="0" cellspacing="0">
<tr valign="top">
<tal:comment replace="nothing">Portlet</tal:comment>
<td tal:condition="python: tool.showPortlet(context)" class="portlet">
<td tal:condition="python: tool.showPortlet(context, layoutType)" class="portlet">
<metal:portlet use-macro="app/ui/portlet/macros/portlet"/>
</td>
<tal:comment replace="nothing">Page content</tal:comment>

View file

@ -1,10 +1,8 @@
<tal:main define="tool context/config">
<html metal:use-macro="context/ui/template/macros/main">
<metal:fill fill-slot="content"
tal:define="contextObj python: context.getParentNode();
dummy python: contextObj.allows('View', raiseError=True);
tal:define="dummy python: contextObj.allows('View', raiseError=True);
errors python: req.get('errors', {});
layoutType python:'view';
layout python: contextObj.getPageLayout(layoutType);
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
phase phaseInfo/name;