[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:
parent
4a69a3beb2
commit
f31cbc4d12
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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()
|
|
||||||
refField = None
|
|
||||||
if refInfo[0]: refField = refInfo[0].getAppyType(refInfo[1])
|
|
||||||
for cType in contentTypes:
|
|
||||||
if refField:
|
|
||||||
fieldNames = refField.shownInfo
|
|
||||||
else:
|
else:
|
||||||
fieldNames = getattr(appyTool, 'resultColumnsFor%s' % cType)
|
toolFieldName = 'resultColumnsFor%s' % contentType
|
||||||
if not resSet:
|
res = getattr(self.appy(), toolFieldName)
|
||||||
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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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.'''
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -61,14 +61,16 @@
|
||||||
</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"/>
|
||||||
|
@ -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">
|
||||||
|
<td tal:define="widget column/field"
|
||||||
|
tal:attributes="id python:'field_%s' % widget['name'];
|
||||||
|
width column/width; align column/align">
|
||||||
<tal:comment replace="nothing">Title</tal:comment>
|
<tal:comment replace="nothing">Title</tal:comment>
|
||||||
<td id="field_title"
|
<tal:title condition="python: widget['name'] == 'title'">
|
||||||
tal: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,17 +107,17 @@
|
||||||
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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue