[gen] for Type.shownInfo and tool.resultColumns..., added the possibility to define the name, width and alignment of every column (=notion of 'column layout').

This commit is contained in:
Gaetan Delannay 2012-11-02 22:27:54 +01:00
parent 4a69a3beb2
commit f31cbc4d12
9 changed files with 95 additions and 72 deletions

View file

@ -1794,16 +1794,6 @@ class Ref(Type):
# other fields whose names are listed in the following attribute. # other fields whose names are listed in the following attribute.
self.shownInfo = list(shownInfo) self.shownInfo = list(shownInfo)
if not self.shownInfo: self.shownInfo.append('title') if not self.shownInfo: self.shownInfo.append('title')
self.shownInfoWidths = []
for i in range(len(self.shownInfo)):
if '*' in self.shownInfo[i]:
# The width is also specified
info, width = self.shownInfo[i].split('*',1)
self.shownInfo[i] = info
self.shownInfoWidths.append(width)
else:
# No width is specified
self.shownInfoWidths.append('')
# If a method is defined in this field "select", it will be used to # If a method is defined in this field "select", it will be used to
# filter the list of available tied objects. # filter the list of available tied objects.
self.select = select self.select = select

View file

@ -229,4 +229,35 @@ widePageLayouts = {
# The default layout for fields. Alternative layouts may exist and are declared # The default layout for fields. Alternative layouts may exist and are declared
# as static attributes of the concerned Type subclass. # as static attributes of the concerned Type subclass.
defaultFieldLayouts = {'edit': 'lrv-f'} defaultFieldLayouts = {'edit': 'lrv-f'}
# ------------------------------------------------------------------------------
class ColumnLayout:
'''A "column layout" dictates the way a table column must be rendered. Such
a layout is of the form: <name>[*width][,|!|`|`]
* "name" is the name of the field whose content must be shown in
column's cells;
* "width" is the width of the column. Any valid value for the "width"
attribute of the "td" HTML tag is accepted;
* , | or ! indicates column alignment: respectively, left, centered or
right.
'''
def __init__(self, layoutString):
self.layoutString = layoutString
def get(self):
'''Returns a list containing the separate elements that are within
self.layoutString.'''
consumed = self.layoutString
# Determine column alignment
align = 'left'
lastChar = consumed[-1]
if lastChar in cellDelimiters:
align = cellDelimiters[lastChar]
consumed = consumed[:-1]
# Determine name and width
if '*' in consumed:
name, width = consumed.rsplit('*', 1)
else:
name = consumed
width = ''
return name, width, align
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -406,28 +406,14 @@ class ToolMixin(BaseMixin):
self.REQUEST.SESSION['search_%s' % searchName] = uids self.REQUEST.SESSION['search_%s' % searchName] = uids
return res.__dict__ return res.__dict__
def getResultColumnsNames(self, contentType, refInfo): def getResultColumnsLayouts(self, contentType, refInfo):
contentTypes = contentType.strip(',').split(',') '''Returns the column layouts for displaying objects of
resSet = None # Temporary set for computing intersections. p_contentType.'''
res = [] # Final, sorted result. if refInfo[0]:
fieldNames = None res = refInfo[0].getAppyType(refInfo[1]).shownInfo
appyTool = self.appy() else:
refField = None toolFieldName = 'resultColumnsFor%s' % contentType
if refInfo[0]: refField = refInfo[0].getAppyType(refInfo[1]) res = getattr(self.appy(), toolFieldName)
for cType in contentTypes:
if refField:
fieldNames = refField.shownInfo
else:
fieldNames = getattr(appyTool, 'resultColumnsFor%s' % cType)
if not resSet:
resSet = set(fieldNames)
else:
resSet = resSet.intersection(fieldNames)
# By converting to set, we've lost order. Let's put things in the right
# order.
for fieldName in fieldNames:
if fieldName in resSet:
res.append(fieldName)
return res return res
def truncateValue(self, value, appyType): def truncateValue(self, value, appyType):

View file

@ -7,7 +7,7 @@ import os, os.path, sys, types, mimetypes, urllib, cgi
from appy import Object from appy import Object
import appy.gen as gen import appy.gen as gen
from appy.gen.utils import * from appy.gen.utils import *
from appy.gen.layout import Table, defaultPageLayouts from appy.gen.layout import Table, defaultPageLayouts, ColumnLayout
from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor
from appy.shared.utils import sequenceTypes, normalizeText from appy.shared.utils import sequenceTypes, normalizeText
from appy.shared.data import rtlLanguages from appy.shared.data import rtlLanguages
@ -683,15 +683,21 @@ class BaseMixin:
res['css'] = css res['css'] = css
res['js'] = js res['js'] = js
def getAppyTypesFromNames(self, fieldNames, asDict=True): def getColumnsSpecifiers(self, columnLayouts, dir):
'''Gets the Appy types named p_fieldNames.''' '''Extracts and returns, from a list of p_columnLayouts, the information
that is necessary for displaying a column in a result screen or for
a Ref field.'''
res = [] res = []
for name in fieldNames: tool = self.getTool()
appyType = self.getAppyType(name, asDict) for info in columnLayouts:
if appyType: res.append(appyType) fieldName, width, align = ColumnLayout(info).get()
align = tool.flipLanguageDirection(align, dir)
field = self.getAppyType(fieldName, asDict=True)
if not field:
self.log('Field "%s", used in a column specifier, was not ' \
'found.' % fieldName, type='warning')
else: else:
self.log('Field "%s", used as shownInfo in a Ref, ' \ res.append({'field':field, 'width':width, 'align': align})
'was not found.' % name, type='warning')
return res return res
def getAppyStates(self, phase, currentOnly=False): def getAppyStates(self, phase, currentOnly=False):

View file

@ -12,8 +12,7 @@ class Protos:
# List of attributes that can't be given to a Type constructor # List of attributes that can't be given to a Type constructor
notInit = ('id', 'type', 'pythonType', 'slaves', 'isSelect', 'hasLabel', notInit = ('id', 'type', 'pythonType', 'slaves', 'isSelect', 'hasLabel',
'hasDescr', 'hasHelp', 'required', 'filterable', 'validable', 'hasDescr', 'hasHelp', 'required', 'filterable', 'validable',
'backd', 'isBack', 'sync', 'pageName', 'shownInfoWidths', 'backd', 'isBack', 'sync', 'pageName', 'masterName')
'masterName')
@classmethod @classmethod
def get(self, appyType): def get(self, appyType):
'''Returns a prototype instance for p_appyType.''' '''Returns a prototype instance for p_appyType.'''

View file

@ -92,8 +92,7 @@ img { border: 0; vertical-align: middle}
.list { border: 1px solid grey; margin-bottom: 3px;} .list { border: 1px solid grey; margin-bottom: 3px;}
.list td, .list th { border: 1px solid grey; .list td, .list th { border: 1px solid grey;
padding-left: 5px; padding-right: 5px; padding-top: 3px;} padding-left: 5px; padding-right: 5px; padding-top: 3px;}
.list th { background-color: #d7dee4; font-style: italic; font-weight: normal; .list th { background-color: #d7dee4; font-style: italic; font-weight: normal }
text-align: left }
.grid th { font-style: italic; font-weight: normal; .grid th { font-style: italic; font-weight: normal;
border-bottom: 2px solid grey; padding: 2px 2px } border-bottom: 2px solid grey; padding: 2px 2px }
.grid td { padding-right: 5px } .grid td { padding-right: 5px }

View file

@ -61,19 +61,21 @@
</tr> </tr>
</table> </table>
<table tal:define="fieldNames python: tool.getResultColumnsNames(className, refInfo); <table tal:define="columnLayouts python: tool.getResultColumnsLayouts(className, refInfo);
widgets python: objs[0].getAppyTypesFromNames(fieldNames);" columns python: objs[0].getColumnsSpecifiers(columnLayouts, dir)"
class="list" width="100%"> class="list" width="100%">
<tal:comment replace="nothing">Headers, with filters and sort arrows</tal:comment> <tal:comment replace="nothing">Headers, with filters and sort arrows</tal:comment>
<tr> <tr>
<tal:header repeat="widget widgets"> <tal:header repeat="column columns">
<th tal:define="sortable python: tool.isSortable(widget['name'], className, 'search'); <th tal:define="widget column/field;
filterable widget/filterable|nothing;"> sortable python: tool.isSortable(widget['name'], className, 'search');
filterable widget/filterable|nothing"
tal:attributes="width column/width; align column/align">
<span tal:replace="structure python: tool.truncateText(_(widget['labelId']))"/> <span tal:replace="structure python: tool.truncateText(_(widget['labelId']))"/>
<metal:icons use-macro="context/ui/navigate/macros/sortAndFilter"/> <metal:icons use-macro="context/ui/navigate/macros/sortAndFilter"/>
<metal:details use-macro="context/ui/navigate/macros/showDetails"/> <metal:details use-macro="context/ui/navigate/macros/showDetails"/>
</th> </th>
</tal:header> </tal:header>
</tr> </tr>
<tal:comment replace="nothing">Results</tal:comment> <tal:comment replace="nothing">Results</tal:comment>
@ -81,10 +83,12 @@
<tr id="query_row" valign="top" tal:define="odd repeat/obj/odd" <tr id="query_row" valign="top" tal:define="odd repeat/obj/odd"
tal:attributes="class python:test(odd, 'even', 'odd')"> tal:attributes="class python:test(odd, 'even', 'odd')">
<tal:fields repeat="widget widgets"> <tal:fields repeat="column columns">
<tal:comment replace="nothing">Title</tal:comment> <td tal:define="widget column/field"
<td id="field_title" tal:attributes="id python:'field_%s' % widget['name'];
tal:condition="python: widget['name'] == 'title'"> width column/width; align column/align">
<tal:comment replace="nothing">Title</tal:comment>
<tal:title condition="python: widget['name'] == 'title'">
<tal:icons replace="structure obj/getIcons"/> <tal:icons replace="structure obj/getIcons"/>
<a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);" <a tal:define="navInfo python:'search.%s.%s.%d.%d' % (className, searchName, repeat['obj'].number()+startNumber, totalNumber);"
tal:content="obj/Title" tal:attributes="href python: obj.getUrl(nav=navInfo, page=obj.getDefaultViewPage())"></a> tal:content="obj/Title" tal:attributes="href python: obj.getUrl(nav=navInfo, page=obj.getDefaultViewPage())"></a>
@ -103,19 +107,19 @@
title python: _('object_delete'); title python: _('object_delete');
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/> onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
</div> </div>
</td> </tal:title>
<tal:comment replace="nothing">Any other field</tal:comment> <tal:comment replace="nothing">Any other field</tal:comment>
<td tal:condition="python: widget['name'] != 'title'" <tal:other condition="python: widget['name'] != 'title'">
tal:attributes="id python:'field_%s' % widget['name']">
<tal:field define="contextObj python:obj; <tal:field define="contextObj python:obj;
layoutType python:'cell'; layoutType python:'cell';
innerRef python:True" innerRef python:True"
condition="python: contextObj.showField(widget['name'], 'result')"> condition="python: contextObj.showField(widget['name'], 'result')">
<metal:field use-macro="context/ui/widgets/show/macros/field"/> <metal:field use-macro="context/ui/widgets/show/macros/field"/>
</tal:field> </tal:field>
</tal:other>
</td> </td>
</tal:fields> </tal:fields>
</tr> </tr>
</tal:row> </tal:row>
</table> </table>

View file

@ -174,21 +174,25 @@
<tal:comment replace="nothing">Show forward or backward reference(s)</tal:comment> <tal:comment replace="nothing">Show forward or backward reference(s)</tal:comment>
<table tal:attributes="class python:test(innerRef, '', 'list'); <table tal:attributes="class python:test(innerRef, '', 'list');
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);"> width python:test(innerRef, '100%', appyType['layouts']['view']['width']);">
<tal:widgets define="widgets python: objs[0].getAppyTypesFromNames(appyType['shownInfo'])"> <tal:infos define="columns python: objs[0].getColumnsSpecifiers(appyType['shownInfo'], dir)">
<tr tal:condition="appyType/showHeaders"> <tr tal:condition="appyType/showHeaders">
<th tal:repeat="widget widgets"> <th tal:repeat="column columns"
tal:attributes="width column/width; align column/align">
<tal:def define="widget column/field">
<span tal:content="python: _(widget['labelId'])"></span> <span tal:content="python: _(widget['labelId'])"></span>
<metal:sortIcons use-macro="app/ui/widgets/ref/macros/sortIcons" /> <metal:sortIcons use-macro="app/ui/widgets/ref/macros/sortIcons" />
<tal:sd define="className linkedPortalType"> <tal:sd define="className linkedPortalType">
<metal:details use-macro="context/ui/navigate/macros/showDetails"/> <metal:details use-macro="context/ui/navigate/macros/showDetails"/>
</tal:sd> </tal:sd>
</tal:def>
</th> </th>
</tr> </tr>
<tal:row repeat="obj objs"> <tal:row repeat="obj objs">
<tr valign="top" tal:define="odd repeat/obj/odd" <tr valign="top" tal:define="odd repeat/obj/odd"
tal:attributes="class python:test(odd, 'even', 'odd')"> tal:attributes="class python:test(odd, 'even', 'odd')">
<td tal:repeat="widget widgets" <td tal:repeat="column columns"
tal:attributes="width python: appyType['shownInfoWidths'][repeat['widget'].index]"> tal:attributes="width column/width; align column/align">
<tal:def define="widget column/field">
<tal:title condition="python: widget['name'] == 'title'"> <tal:title condition="python: widget['name'] == 'title'">
<metal:showObjectTitle use-macro="app/ui/widgets/ref/macros/objectTitle"/> <metal:showObjectTitle use-macro="app/ui/widgets/ref/macros/objectTitle"/>
<div name="subTitle" tal:content="structure obj/getSubTitle" <div name="subTitle" tal:content="structure obj/getSubTitle"
@ -204,10 +208,11 @@
<metal:field use-macro="app/ui/widgets/show/macros/field" /> <metal:field use-macro="app/ui/widgets/show/macros/field" />
</tal:field> </tal:field>
</tal:other> </tal:other>
</tal:def>
</td> </td>
</tr> </tr>
</tal:row> </tal:row>
</tal:widgets> </tal:infos>
</table> </table>
</td></tr> </td></tr>
</table> </table>

View file

@ -13,16 +13,19 @@
a.o. for master/slave relationships). a.o. for master/slave relationships).
tagCss Some additional CSS class for the main tag tagCss Some additional CSS class for the main tag
(ie, the CSS class for a slave). (ie, the CSS class for a slave).
Note: if layoutType is "cell", alignment and width are not taken into account:
we use the alignment of the column specification instead.
</tal:comment> </tal:comment>
<metal:show define-macro="layout" <metal:show define-macro="layout"
tal:define="contextMacro contextMacro| python: app.ui; tal:define="contextMacro contextMacro| python: app.ui;
tagId tagId|python:''; tagId tagId|python:'';
tagCss tagCss|python:''; tagCss tagCss|python:'';
layoutCss layout/css_class;"> layoutCss layout/css_class;
isCell python: layoutType == 'cell'">
<table tal:attributes="cellpadding layout/cellpadding; <table tal:attributes="cellpadding layout/cellpadding;
cellspacing layout/cellspacing; cellspacing layout/cellspacing;
width layout/width; width python: not isCell and layout['width'] or '';
align python: tool.flipLanguageDirection(layout['align'], dir); align python: not isCell and tool.flipLanguageDirection(layout['align'], dir) or '';
class python: tagCss and ('%s %s' % (tagCss, layoutCss)).strip() or layoutCss; class python: tagCss and ('%s %s' % (tagCss, layoutCss)).strip() or layoutCss;
style layout/style; style layout/style;
id tagId; id tagId;