[gen] More work ZPT->PX.

This commit is contained in:
Gaetan Delannay 2013-08-21 12:35:30 +02:00
parent 2e9a832463
commit 34e3a3083e
31 changed files with 3287 additions and 3067 deletions

View file

@ -19,6 +19,8 @@ import copy, types, re
from appy.gen.layout import Table, defaultFieldLayouts
from appy.gen import utils as gutils
from appy.shared import utils as sutils
from appy.px import Px
from appy import Object
# ------------------------------------------------------------------------------
nullValues = (None, '', [])
@ -108,18 +110,167 @@ class Page:
return res
def getInfo(self, obj, layoutType):
'''Gets information about this page, for p_obj, as a dict.'''
res = {}
'''Gets information about this page, for p_obj, as an object.'''
res = Object()
for elem in Page.subElements:
res['show%s' % elem.capitalize()] = self.isShowable(obj, layoutType,
elem=elem)
setattr(res, 'show%s' % elem.capitalize(), \
self.isShowable(obj, layoutType, elem=elem))
return res
# ------------------------------------------------------------------------------
# Phase. Pages are grouped into phases.
# ------------------------------------------------------------------------------
class Phase:
'''A group of pages.'''
pxView = Px('''
<tr var="singlePage=len(phase.pages) == 1">
<td var="label='%s_phase_%s' % (zobj.meta_type, phase.name)">
<!-- The title of the phase -->
<div class="portletGroup"
if="not singlePhase and not singlePage">::_(label)</div>
<!-- The page(s) within the phase -->
<x for="aPage in phase.pages">
<!-- First line: page name and icons -->
<div if="not (singlePhase and singlePage)"
class=":aPage==page and 'portletCurrent portletPage' or \
'portletPage'">
<a href=":zobj.getUrl(page=aPage)">::_('%s_page_%s' % \
(zobj.meta_type, aPage))</a>
<x var="locked=zobj.isLocked(user, aPage);
editable=mayEdit and phase.pagesInfo[aPage].showOnEdit">
<a if="editable and not locked"
href=":zobj.getUrl(mode='edit', page=aPage)">
<img src=":url('edit')" title=":_('object_edit')"/></a>
<a if="editable and locked">
<img style="cursor: help"
var="lockDate=tool.formatDate(locked[1]);
lockMap={'user':ztool.getUserName(locked[0]), \
'date':lockDate};
lockMsg=_('page_locked', mapping=lockMap)"
src=":url('locked')" title=":lockMsg"/></a>
<a if="editable and locked and user.has_role('Manager')">
<img class="clickable" title=":_('page_unlock')" src=":url('unlock')"
onclick=":'onUnlockPage(%s,%s)' % \
(q(zobj.UID()), q(aPage))"/></a>
</x>
</div>
<!-- Next lines: links -->
<x var="links=phase.pagesInfo[aPage].links" if="links">
<div for="link in links"><a href=":link.url">:link.title</a></div>
</x>
</x>
</td>
</tr>''')
def __init__(self, name, obj):
self.name = name
self.obj = obj
# The list of names of pages in this phase
self.pages = []
# The list of hidden pages in this phase
self.hiddenPages = []
# The dict below stores info about every page listed in self.pages.
self.pagesInfo = {}
self.totalNbOfPhases = None
# 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
# phase if allowed by phase state.
self.previousPhase = None
self.nextPhase = None
def addPageLinks(self, field, obj):
'''If p_field is a navigable Ref, we must add, within self.pagesInfo,
objects linked to p_obj through this ReF as links.'''
if field.page.name in self.hiddenPages: return
infos = []
for ztied in field.getValue(obj, type='zobjects'):
infos.append(Object(title=ztied.title, url=ztied.absolute_url()))
self.pagesInfo[field.page.name].links = infos
def addPage(self, field, obj, layoutType):
'''Adds page-related information in the phase.'''
# If the page is already there, we have nothing more to do.
if (field.page.name in self.pages) or \
(field.page.name in self.hiddenPages): return
# Add the page only if it must be shown.
isShowableOnView = field.page.isShowable(obj, 'view')
isShowableOnEdit = field.page.isShowable(obj, 'edit')
if isShowableOnView or isShowableOnEdit:
# The page must be added.
self.pages.append(field.page.name)
# Create the dict about page information and add it in self.pageInfo
pageInfo = Object(page=field.page, showOnView=isShowableOnView,
showOnEdit=isShowableOnEdit, links=None)
pageInfo.update(field.page.getInfo(obj, layoutType))
self.pagesInfo[field.page.name] = pageInfo
else:
self.hiddenPages.append(field.page.name)
def computeNextPrevious(self, allPhases):
'''This method also fills fields "previousPhase" and "nextPhase"
if relevant, based on list of p_allPhases.'''
# Identify previous and next phases
for phase in allPhases:
if phase.name == self.name:
i = allPhases.index(phase)
if i > 0:
self.previousPhase = allPhases[i-1]
if i < (len(allPhases)-1):
self.nextPhase = allPhases[i+1]
def getPreviousPage(self, page):
'''Returns the page that precedes p_page in this phase.'''
try:
pageIndex = self.pages.index(page)
except ValueError:
# The current page is probably not visible anymore. Return the
# first available page in current phase.
res = self.pages[0]
return res, self.pagesInfo[res]
if pageIndex > 0:
# We stay on the same phase, previous page
res = self.pages[pageIndex-1]
return res, self.pagesInfo[res]
else:
if self.previousPhase:
# We go to the last page of previous phase
previousPhase = self.previousPhase
res = previousPhase.pages[-1]
return res, previousPhase.pagesInfo[res]
else:
return None, None
def getNextPage(self, page):
'''Returns the page that follows p_page in this phase.'''
try:
pageIndex = self.pages.index(page)
except ValueError:
# The current page is probably not visible anymore. Return the
# first available page in current phase.
res = self.pages[0]
return res, self.pagesInfo[res]
if pageIndex < (len(self.pages)-1):
# We stay on the same phase, next page
res = self.pages[pageIndex+1]
return res, self.pagesInfo[res]
else:
if self.nextPhase:
# We go to the first page of next phase
nextPhase = self.nextPhase
res = nextPhase.pages[0]
return res, nextPhase.pagesInfo[res]
else:
return None, None
# ------------------------------------------------------------------------------
# Group. Fields can be grouped.
# ------------------------------------------------------------------------------
class Group:
'''Used for describing a group of widgets within a page.'''
'''Used for describing a group of fields within a page.'''
def __init__(self, name, columns=['100%'], wide=True, style='section2',
hasLabel=True, hasDescr=False, hasHelp=False,
hasHeaders=False, group=None, colspan=1, align='center',
@ -258,27 +409,26 @@ class Group:
self.group.generateLabels(messages, classDescr, walkedGroups,
forSearch=forSearch)
def insertInto(self, widgets, groupDescrs, page, metaType, forSearch=False):
'''Inserts the GroupDescr instance corresponding to this Group instance
into p_widgets, the recursive structure used for displaying all
widgets in a given p_page (or all searches), and returns this
GroupDescr instance.'''
# First, create the corresponding GroupDescr if not already in
# p_groupDescrs.
if self.name not in groupDescrs:
groupDescr = groupDescrs[self.name] = gutils.GroupDescr(\
self, page, metaType, forSearch=forSearch).get()
# Insert the group at the higher level (ie, directly in p_widgets)
def insertInto(self, fields, uiGroups, page, metaType, forSearch=False):
'''Inserts the UiGroup instance corresponding to this Group instance
into p_fields, the recursive structure used for displaying all
fields in a given p_page (or all searches), and returns this
UiGroup instance.'''
# First, create the corresponding UiGroup if not already in p_uiGroups.
if self.name not in uiGroups:
uiGroup = uiGroups[self.name] = UiGroup(self, page, metaType,
forSearch=forSearch)
# Insert the group at the higher level (ie, directly in p_fields)
# if the group is not itself in a group.
if not self.group:
widgets.append(groupDescr)
fields.append(uiGroup)
else:
outerGroupDescr = self.group.insertInto(widgets, groupDescrs,
page, metaType, forSearch=forSearch)
gutils.GroupDescr.addWidget(outerGroupDescr, groupDescr)
outerGroup = self.group.insertInto(fields, uiGroups, page,
metaType,forSearch=forSearch)
outerGroup.addField(uiGroup)
else:
groupDescr = groupDescrs[self.name]
return groupDescr
uiGroup = uiGroups[self.name]
return uiGroup
class Column:
'''Used for describing a column within a Group like defined above.'''
@ -286,6 +436,188 @@ class Column:
self.width = width
self.align = align
class UiGroup:
'''On-the-fly-generated data structure that groups all fields sharing the
same appy.fields.Group instance, that some logged user can see.'''
# PX that renders a help icon for a group.
pxHelp = Px('''<acronym title="obj.translate('help', field=field)"><img
src=":url('help')"/></acronym>''')
# PX that renders the content of a group.
pxContent = Px('''
<table var="cellgap=field.cellgap" width=":field.wide"
align=":ztool.flipLanguageDirection(field.align, dir)"
id=":tagId" name=":tagName" class=":groupCss"
cellspacing=":field.cellspacing" cellpadding=":field.cellpadding">
<!-- Display the title of the group if not rendered a fieldset. -->
<tr if="(field.style != 'fieldset') and field.hasLabel">
<td colspan=":len(field.columnsWidths)" class=":field.style"
align=":dleft">
<x>::_(field.labelId)</x><x if="field.hasHelp">:field.pxHelp</x>
</td>
</tr>
<tr if="(field.style != 'fieldset') and field.hasDescr">
<td colspan=":len(field.columnsWidths)"
class="discreet">::_(field.descrId)</td>
</tr>
<!-- The column headers -->
<tr>
<th for="colNb in range(len(field.columnsWidths))"
align="ztool.flipLanguageDirection(field.columnsAligns[colNb], dir)"
width=":field.columnsWidths[colNb]">::field.hasHeaders and \
_('%s_col%d' % (field.labelId, (colNb+1))) or ''</th>
</tr>
<!-- The rows of widgets -->
<tr valign=":field.valign" for="row in field.fields">
<td for="field in row"
colspan="field.colspan"
style=":not loop.field.last and ('padding-right:%s'% cellgap) or ''">
<x if="field">
<x if="field.type == 'group'">:field.pxView</x>
<x if="field.type != 'group'">:field.pxRender</x>
</x>
</td>
</tr>
</table>''')
# PX that renders a group of fields.
pxView = Px('''
<x var="tagCss=field.master and ('slave_%s_%s' % \
(field.masterName, '_'.join(field.masterValue))) or '';
widgetCss=field.css_class;
groupCss=tagCss and ('%s %s' % (tagCss, widgetCss)) or widgetCss;
tagName=field.master and 'slave' or '';
tagId='%s_%s' % (zobj.UID(), field.name)">
<!-- Render the group as a fieldset if required -->
<fieldset if="field.style == 'fieldset'">
<legend if="field.hasLabel">
<i>::_(field.labelId)></i><x if="field.hasHelp">:field.pxHelp</x>
</legend>
<div if="field.hasDescr" class="discreet">::_(field.descrId)</div>
<x>:field.pxContent</x>
</fieldset>
<!-- Render the group as a section if required -->
<x if="field.style not in ('fieldset', 'tabs')">:field.pxContent</x>
<!-- Render the group as tabs if required -->
<x if="field.style == 'tabs'" var2="lenFields=len(field.fields)">
<table width=":field.wide" class=":groupCss" id=":tagId" name=":tagName">
<!-- First row: the tabs. -->
<tr valign="middle"><td style="border-bottom: 1px solid #ff8040">
<table style="position:relative; bottom:-2px"
cellpadding="0" cellspacing="0">
<tr valign="bottom">
<x for="row in field.fields"
var2="rowNb=loop.row.nb;
tabId='tab_%s_%d_%d' % (field.name, rowNb, lenFields)">
<td><img src=":url('tabLeft')" id=":'%s_left' % tabId"/></td>
<td style=":url('tabBg', bg=True)" id=":tabId">
<a onclick=":'showTab(%s)' % q('%s_%d_%d' % (field.name, rowNb, \
lenFields))"
class="clickable">:_('%s_col%d' % (field.labelId, rowNb))</a>
</td>
<td><img id=":'%s_right' % tabId" src=":url('tabRight')"/></td>
</x>
</tr>
</table>
</td></tr>
<!-- Other rows: the fields -->
<tr for="row in field.fields"
id=":'tabcontent_%s_%d_%d' % (field.name, loop.row.nb, lenFields)"
style=":loop.row.nb==0 and 'display:table-row' or 'display:none')">
<td var="field=row[0]">
<x if="field.type == 'group'">:field.pxView</x>
<x if="field.type != 'group'">:field.pxRender</x>
</td>
</tr>
</table>
<script type="text/javascript">:'initTab(%s,%s)' % \
(q('tab_%s' % field.name), q('%s_1_%d' % (field.name, lenFields)))">
</script>
</x>
</x>''')
# PX that renders a group of searches.
pxViewSearches = Px('''
<x var="expanded=req.get(field.labelId, 'collapsed') == 'expanded'">
<!-- Group name, prefixed by the expand/collapse icon -->
<div class="portletGroup">
<img class="clickable" style="margin-right: 3px" align=":dleft"
id=":'%s_img' % field.labelId"
src=":expanded and url('collapse.gif') or url('expand.gif')"
onclick=":'toggleCookie(%s)' % q(field.labelId)"/>
<x if="not field.translated">:_(field.labelId)</x>
<x if="field.translated">:field.translated</x>
</div>
<!-- Group content -->
<div var="display=expanded and 'display:block' or 'display:none'"
id=":field.labelId" style=":'padding-left: 10px; %s' % display">
<x for="searches in field.widgets">
<x for="elem in searches">
<!-- An inner group within this group -->
<x if="elem.type == 'group'"
var2="field=elem">:field.pxViewSearches</x>
<!-- A search -->
<x if="elem.type != 'group'" var2="search=elem">:search.pxView</x>
</x>
</x>
</div>
</x>''')
def __init__(self, group, page, metaType, forSearch=False):
self.type = 'group'
# All p_group attributes become self attributes.
for name, value in group.__dict__.iteritems():
if not name.startswith('_'):
setattr(self, name, value)
self.columnsWidths = [col.width for col in group.columns]
self.columnsAligns = [col.align for col in group.columns]
# Names of i18n labels
labelName = self.name
prefix = metaType
if group.label:
if isinstance(group.label, basestring): prefix = group.label
else: # It is a tuple (metaType, name)
if group.label[1]: labelName = group.label[1]
if group.label[0]: prefix = group.label[0]
if forSearch: gp = 'searchgroup'
else: gp = 'group'
self.labelId = '%s_%s_%s' % (prefix, gp, labelName)
self.descrId = self.labelId + '_descr'
self.helpId = self.labelId + '_help'
# The name of the page where the group lies
self.page = page.name
# The fields belonging to the group that the current user may see.
# They will be stored by m_addField below as a list of lists because
# they will be rendered as a table.
self.fields = [[]]
# PX to user for rendering this group.
self.px = forSearch and self.pxViewSearches or self.pxView
def addField(self, field):
'''Adds p_field into self.fields. We try first to add p_field into the
last row. If it is not possible, we create a new row.'''
# Get the last row
lastRow = self.fields[-1]
numberOfColumns = len(self.columnsWidths)
# Compute the number of columns already filled in the last row.
filledColumns = 0
for rowField in lastRow: filledColumns += rowField.colspan
freeColumns = numberOfColumns - filledColumns
if freeColumns >= field.colspan:
# We can add the widget in the last row.
lastRow.append(field)
else:
if freeColumns:
# Terminate the current row by appending empty cells
for i in range(freeColumns): lastRow.append('')
# Create a new row
self.fields.append([field])
# ------------------------------------------------------------------------------
# Abstract base class for every field.
# ------------------------------------------------------------------------------
@ -298,6 +630,67 @@ class Field:
jsFiles = {}
dLayouts = 'lrv-d-f'
# Render a field. Optiona vars:
# * fieldName can be given as different as field.name for fields included
# in List fields: in this case, fieldName includes the row
# index.
# * showChanges If True, a variant of the field showing successive changes
# made to it is shown.
pxRender = Px('''
<x var="showChanges=showChanges|False;
layout=field.layouts[layoutType];
name=fieldName|field.name;
sync=field.sync[layoutType];
outerValue=value|None;
rawValue=zobj.getFieldValue(name, onlyIfSync=True, \
layoutType=layoutType, \
outerValue=outerValue);
value=zobj.getFormattedFieldValue(name, rawValue, showChanges);
requestValue=zobj.getRequestFieldValue(name);
inRequest=req.has_key(name);
errors=errors|();
inError=name in errors;
isMultiple=(field.multiplicity[1] == None) or \
(field.multiplicity[1] &gt; 1);
masterCss=field.slaves and ('master_%s' % name) or '';
slaveCss=field.master and ('slave_%s_%s' % \
(field.masterName, '_'.join(field.masterValue))) or '';
tagCss=tagCss|'';
tagCss=('%s %s' % (slaveCss, tagCss)).strip();
tagId='%s_%s' % (zobj.UID(), name);
tagName=field.master and 'slave' or '';
layoutTarget=field">:tool.pxLayoutedObject</x>''')
# Displays a field label.
pxLabel = Px('''<label if="field.hasLabel and (field.type != 'Action')"
lfor="field.name">::zobj.translate('label', field=field)</label>''')
# Displays a field description.
pxDescription = Px('''<span if="field.hasDescr"
class="discreet">::zobj.translate('descr', field=field)</span>''')
# Displays a field help.
pxHelp = Px('''<acronym title="zobj.translate('help', field=field)"><img
src=":url('help')"/></acronym>''')
# Displays validation-error-related info about a field.
pxValidation = Px('''<x><acronym if="inError" title=":errors[name]"><img
src=":url('warning')"/></acronym><img if="not inError"
src=":url('warning_no.gif')"/></x>''')
# Displays the fact that a field is required.
pxRequired = Px('''<img src=":url('required.gif')"/>''')
# Button for showing changes to the field.
pxChanges = Px('''<x if=":zobj.hasHistory(name)"><img class="clickable"
if="not showChanges" src=":url('changes')" title="_('changes_show')"
onclick=":'askField(%s,%s,%s,%s)' % \
(q(tagId), q(zobj.absolute_url()), q('view'), q('True'))"/><img
class="clickable" if="showChanges" src=":url('changesNo')"
onclick=":'askField(%s,%s,%s,%s)' % \
(q(tagId), q(zobj.absolute_url(), q('view'), q('True'))"
title=":_('changes_hide')"/></x>''')
def __init__(self, validator, multiplicity, default, show, page, group,
layouts, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan,
@ -359,7 +752,7 @@ class Field:
self.maxChars = maxChars or ''
# If the widget is in a group with multiple columns, the following
# attribute specifies on how many columns to span the widget.
self.colspan = colspan
self.colspan = colspan or 1
# The list of slaves of this field, if it is a master
self.slaves = []
# The behaviour of this field may depend on another, "master" field
@ -401,7 +794,7 @@ class Field:
# default value will be present.
self.sdefault = sdefault
# Colspan for rendering the search widget corresponding to this field.
self.scolspan = scolspan
self.scolspan = scolspan or 1
# Width and height for the search widget
self.swidth = swidth or width
self.sheight = sheight or height

View file

@ -41,14 +41,14 @@ class Boolean(Field):
pxSearch = Px('''
<x var="typedWidget='%s*bool' % widgetName">
<label lfor=":widgetName">:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<label lfor=":widgetName">:_(field.labelId)</label><br/>&nbsp;&nbsp;
<x var="valueId='%s_yes' % name">
<input type="radio" value="True" name=":typedWidget" id=":valueId"/>
<label lfor=":valueId">:_('yes')</label>
</x>
<x var="valueId='%s_no' % name">
<input type="radio" value="False" name=":typedWidget" id=":valueId"/>
<label lfor=":valueId">:_('no')"></label>
<label lfor=":valueId">:_('no')</label>
</x>
<x var="valueId='%s_whatever' % name">
<input type="radio" value="" name=":typedWidget" id=":valueId"

View file

@ -35,8 +35,8 @@ class Calendar(Field):
otherCalendars=field.getOtherCalendars(zobj, preComputed)"
id=":ajaxHookId">
<script type="text/javascript">:'var %s_maxEventLength = %d;' % \
(field.name, field.maxEventLength)"></script>
<script type="text/javascript">:'var %s_maxEventLength = %d' % \
(field.name, field.maxEventLength)</script>
<!-- Month chooser -->
<div style="margin-bottom: 5px"
@ -100,7 +100,7 @@ class Calendar(Field):
onmouseout="mayEdit and 'this.getElementsByTagName(\
%s)[0].style.visibility=%s' % (q('img'), q('hidden')) or ''">
<span>:day</span>
<span if="day == 1">:_('month_%s_short' % date.aMonth())"></span>
<span if="day == 1">:_('month_%s_short' % date.aMonth())</span>
<!-- Icon for adding an event -->
<x if="mayCreate">
<img class="clickable" style="visibility:hidden"
@ -115,11 +115,10 @@ class Calendar(Field):
<img if="mayDelete" class="clickable" style="visibility:hidden"
src=":url('delete')"
onclick=":'openEventPopup(%s, %s, %s, %s, null, null)' % \
(q('del'), q(field.name), q(dayString), q(str(spansDays)))"/>
(q('del'), q(field.name), q(dayString), q(spansDays))"/>
<!-- A single event is allowed for the moment -->
<div if="events" var2="eventType=events[0]['eventType']">
<span style="color: grey">:field.getEventName(zobj, \
eventType)"></span>
<span style="color: grey">:field.getEventName(zobj, eventType)</span>
</div>
<!-- Events from other calendars -->
<x if="otherCalendars"
@ -149,16 +148,15 @@ class Calendar(Field):
<input type="hidden" name="day"/>
<!-- Choose an event type -->
<div align="center" style="margin-bottom: 3px">:_('which_event')"></div>
<div align="center" style="margin-bottom: 3px">:_('which_event')</div>
<select name="eventType">
<option value="">:_('choose_a_value')"></option>
<option value="">:_('choose_a_value')</option>
<option for="eventType in allEventTypes"
value=":eventType">:field.getEventName(zobj, eventType)">
</option>
value=":eventType">:field.getEventName(zobj,eventType)</option>
</select><br/><br/>
<!--Span the event on several days -->
<div align="center" class="discreet" style="margin-bottom: 3px">
<span>:_('event_span')"></span>
<span>:_('event_span')</span>
<input type="text" size="3" name="eventSpan"/>
</div>
<input type="button"
@ -184,8 +182,7 @@ class Calendar(Field):
<input type="hidden" name="actionType" value="deleteEvent"/>
<input type="hidden" name="day"/>
<div align="center" style="margin-bottom: 5px">_('delete_confirm')">
</div>
<div align="center" style="margin-bottom: 5px">_('delete_confirm')</div>
<!-- Delete successive events ? -->
<div class="discreet" style="margin-bottom: 10px"
@ -195,7 +192,7 @@ class Calendar(Field):
onClick=":'toggleCheckbox(%s, %s)' % \
(q('%s_cb' % prefix), q('%s_hd' % prefix))"/>
<input type="hidden" id=":prefix + '_hd'" name="deleteNext"/>
<span>:_('del_next_events')"></span>
<span>:_('del_next_events')</span>
</div>
<input type="button" value=":_('yes')"
onClick=":'triggerCalendarEvent(%s, %s, %s, %s)' % \

View file

@ -23,17 +23,13 @@ class Computed(Field):
# Ajax-called view content of a non sync Computed field.
pxViewContent = Px('''
<x var="name=req['fieldName'];
field=zobj.getAppyType(name);
value=zobj.getFieldValue(name);
sync=True">:field.pxView</x>''')
<x var="value=zobj.getFieldValue(name); sync=True">:field.pxView</x>''')
pxView = pxCell = pxEdit = Px('''<x>
<x if="sync">
<x if="field.plainText">:value</x><x if="not field.plainText">::value></x>
<x if="field.plainText">:value</x><x if="not field.plainText">::value</x>
</x>
<div if="not sync">
var2="ajaxHookId=zobj.UID() + name" id="ajaxHookId">
<div if="not sync" var2="ajaxHookId=zobj.UID() + name" id="ajaxHookId">
<script type="text/javascript">:'askComputedField(%s, %s, %s)' % \
(q(ajaxHookId), q(zobj.absolute_url()), q(name))">
</script>

View file

@ -91,7 +91,7 @@ class Date(Field):
monthFromName='%s_from_month' % name;
yearFromName='%s*date' % widgetName">
<td width="10px">&nbsp;</td>
<td><label>:_('search_from')"></label></td>
<td><label>:_('search_from')</label></td>
<td>
<select id=":dayFromName" name=":dayFromName">
<option value="">--</option>
@ -124,7 +124,7 @@ class Date(Field):
monthToName='%s_to_month' % name;
yearToName='%s_to_year' % name">
<td></td>
<td><label>_('search_to')"></label>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td><label>_('search_to')</label>&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td height="20px">
<select id=":dayToName" name=":dayToName">
<option value="">--</option>

View file

@ -30,7 +30,7 @@ class File(Field):
imgSrc='%s/download?name=%s' % (zobj.absolute_url(), name)">
<x if="not empty and not field.isImage">
<a href=":imgSrc">:info.filename</a>&nbsp;&nbsp;-
<i class="discreet">'%sKb' % (info.size / 1024)"></i>
<i class="discreet">:'%sKb' % (info.size / 1024)</i>
</x>
<x if="not empty and field.isImage"><img src=":imgSrc"/></x>
<x if="empty">-</x>
@ -67,10 +67,8 @@ class File(Field):
<input type="file" name=":'%s_file' % name" id=":'%s_file' % name"
size=":field.width"/>
<script var="isDisabled=empty and 'false' or 'true'"
type="text/javascript">:document.getElementById(%s).disabled=%s'%\
(q(fName), q(isDisabled))">
</script>
</x>''')
type="text/javascript">:'document.getElementById(%s).disabled=%s'%\
(q(fName), q(isDisabled))</script></x>''')
pxSearch = ''

View file

@ -36,10 +36,10 @@ class Float(Field):
value=":inRequest and requestValue or value" type="text"/>''')
pxSearch = Px('''<x>
<label>:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<label>:_(field.labelId)</label><br/>&nbsp;&nbsp;
<!-- From -->
<x var="fromName='%s*float' % widgetName">
<label lfor=":fromName">:_('search_from')"></label>
<label lfor=":fromName">:_('search_from')</label>
<input type="text" name=":fromName" maxlength=":field.maxChars"
value=":field.sdefault[0]" size=":field.swidth"/>
</x>

View file

@ -33,7 +33,7 @@ class Integer(Field):
value=":inRequest and requestValue or value" type="text"/>''')
pxSearch = Px('''<x>
<label>:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<label>:_(field.labelId)</label><br/>&nbsp;&nbsp;
<!-- From -->
<x var="fromName='%s*int' % widgetName">
<label lfor=":fromName">:_('search_from')</label>
@ -42,7 +42,7 @@ class Integer(Field):
</x>
<!-- To -->
<x var="toName='%s_to' % name">
<label lfor=":toName">:_('search_to')"></label>
<label lfor=":toName">:_('search_to')</label>
<input type="text" name=":toName" maxlength=":field.maxChars"
value=":field.sdefault[1]" size=":field.swidth"/>
</x><br/>

View file

@ -112,7 +112,7 @@ class Ogone(Field):
res.update(self.callMethod(obj, self.orderMethod))
# Add user-related information
res['CN'] = str(tool.getUserName(normalized=True))
user = obj.appy().appyUser
user = obj.appy().user
res['EMAIL'] = user.email or user.login
# Add standard back URLs
siteUrl = tool.getSiteUrl()

View file

@ -37,8 +37,9 @@ class Ref(Field):
# the URL for allowing to navigate from one object to the next/previous on
# ui/view.
pxObjectTitle = Px('''
<x var="navInfo='ref.%s.%s:%s.%d.%d' % (zobj.UID(), field.name, \
field.pageName, loop.ztied.nb + startNumber, totalNumber);
<x var="includeShownInfo=includeShownInfo|False;
navInfo='ref.%s.%s:%s.%d.%d' % (zobj.UID(), field.name, \
field.pageName, loop.ztied.nb + 1 + startNumber, totalNumber);
navInfo=not field.isBack and navInfo or '';
cssClass=ztied.getCssFor('title')">
<x>::ztied.getSupTitle(navInfo)</x>
@ -47,7 +48,7 @@ class Ref(Field):
href=":fullUrl" class=":cssClass">:(not includeShownInfo) and \
ztied.Title() or field.getReferenceLabel(ztied.appy())
</a><span name="subTitle" style=":showSubTitles and 'display:inline' or \
'display:none'">::ztied.getSubTitle()"</span>
'display:none'">::ztied.getSubTitle()</span>
</x>''')
# This PX displays icons for triggering actions on a given referenced object
@ -59,7 +60,7 @@ class Ref(Field):
<td if="not isBack and (len(zobjects)&gt;1) and changeOrder and canWrite"
var2="objectIndex=field.getIndexOf(zobj, ztied);
ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
(q(startNumber), q('ChangeRefOrder'), q('refObjectUid'),
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
q(ztied.UID()), q('move'), q('**v**')))">
<img if="objectIndex &gt; 0" class="clickable" src=":url('arrowUp')"
title=":_('move_up')"
@ -100,7 +101,7 @@ class Ref(Field):
field.name, field.pageName, 0, totalNumber);
formCall='goto(%s)' % \
q('%s/do?action=Create&amp;className=%s&amp;nav=%s' % \
(folder.absolute_url(), linkedPortalType, navInfo));
(folder.absolute_url(), tiedClassName, navInfo));
formCall=not field.addConfirm and formCall or \
'askConfirm(%s,%s,%s)' % (q('script'), q(formCall), \
q(addConfirmMsg));
@ -115,22 +116,20 @@ class Ref(Field):
# This PX displays, in a cell header from a ref table, icons for sorting the
# ref field according to the field that corresponds to this column.
pxSortIcons = Px('''
<x if="changeOrder and canWrite and ztool.isSortable(field.name, \
zobjects[0].meta_type, 'ref')"
<x if="changeOrder and canWrite and ztool.isSortable(refField.name, \
tiedClassName, 'ref')"
var2="ajaxBaseCall=navBaseCall.replace('**v**', '%s,%s,{%s:%s,%s:%s}'% \
(q(startNumber), q('SortReference'), q('sortKey'), \
q(field.name), q('reverse'), q('**v**')))">
(q(startNumber), q('sort'), q('sortKey'), q(refField.name), \
q('reverse'), q('**v**')))">
<img class="clickable" src=":url('sortAsc')"
onclick=":ajaxBaseCall.replace('**v**', 'False')"/>
<img class="clickable" src=":url('sortDesc')"
onclick=":ajaxBaseCall.replace('**v**', 'True')"/>
</x>''')
# This PX is called by a XmlHttpRequest (or directly by pxView) for
# displaying the referred objects of a reference field.
pxViewContent = Px('''
<div var="field=zobj.getAppyType(req['fieldName']);
innerRef=req.get('innerRef', False) == 'True';
# PX that displays referred objects through this field.
pxView = pxCell = Px('''
<div var="innerRef=req.get('innerRef', False) == 'True';
ajaxHookId=zobj.UID() + field.name;
startNumber=int(req.get('%s_startNumber' % ajaxHookId, 0));
info=field.getLinkedObjects(zobj, startNumber);
@ -139,7 +138,7 @@ class Ref(Field):
batchSize=info.batchSize;
batchNumber=len(zobjects);
folder=zobj.getCreateFolder();
linkedPortalType=ztool.getPortalType(field.klass);
tiedClassName=ztool.getPortalType(field.klass);
canWrite=not field.isBack and zobj.allows(field.writePermission);
showPlusIcon=zobj.mayAddReference(field.name);
atMostOneRef=(field.multiplicity[1] == 1) and \
@ -182,13 +181,13 @@ class Ref(Field):
<input if="zobjects and field.queryable" type="button" class="button"
style=":url('buttonSearch', bg=True)" value=":_('search_title')"
onclick=":'goto(%s)' % \
q('%s/ui/search?className=%s&amp;ref=%s:%s' % \
(ztool.absolute_url(), linkedPortalType, zobj.UID(), \
q('%s/search?className=%s&amp;ref=%s:%s' % \
(ztool.absolute_url(), tiedClassName, zobj.UID(), \
field.name))"/>
</div>
<!-- Appy (top) navigation -->
<x>:obj.pxAppyNavigate</x>
<!-- (Top) navigation -->
<x>:tool.pxNavigate</x>
<!-- No object is present -->
<p class="discreet" if="not zobjects">:_('no_ref')</p>
@ -198,35 +197,34 @@ class Ref(Field):
<tr valign="bottom">
<td>
<!-- Show forward or backward reference(s) -->
<table class="not innerRef and 'list' or ''"
<table class=":not innerRef and 'list' or ''"
width=":innerRef and '100%' or field.layouts['view']['width']"
var="columns=zobjects[0].getColumnsSpecifiers(\
var="columns=ztool.getColumnsSpecifiers(tiedClassName, \
field.shownInfo, dir)">
<tr if="field.showHeaders">
<th for="column in columns" width=":column['width']"
align="column['align']"
var2="field=column['field']">
<span>:_(field.labelId)</span>
<th for="column in columns" width=":column.width"
align="column.align" var2="refField=column.field">
<span>:_(refField.labelId)</span>
<x>:field.pxSortIcons</x>
<x var="className=linkedPortalType">:obj.pxShowDetails</x>
<x var="className=tiedClassName">:tool.pxShowDetails</x>
</th>
</tr>
<tr for="ztied in zobjects" valign="top"
class=":loop.ztied.odd and 'even' or 'odd'">
<td for="column in columns"
width=":column['width']" align=":column['align']"
var2="field=column['field']">
width=":column.width" align=":column.align"
var2="refField=column.field">
<!-- The "title" field -->
<x if="python: field.name == 'title'">
<x if="refField.name == 'title'">
<x>:field.pxObjectTitle</x>
<div if="ztied.mayAct()">:field.pxObjectActions</div>
</x>
<!-- Any other field -->
<x if="field.name != 'title'">
<x if="refField.name != 'title'">
<x var="zobj=ztied; obj=ztied.appy(); layoutType='cell';
innerRef=True"
innerRef=True; field=refField"
if="zobj.showField(field.name, \
layoutType='result')">:field.pxView</x>
layoutType='result')">:field.pxRender</x>
</x>
</td>
</tr>
@ -235,14 +233,11 @@ class Ref(Field):
</tr>
</table>
<!-- Appy (bottom) navigation -->
<x>:obj.pxAppyNavigate</x>
<!-- (Bottom) navigation -->
<x>:tool.pxNavigate</x>
</x>
</div>''')
pxView = pxCell = Px('''
<x var="x=req.set('fieldName', field.name)">:field.pxViewContent</x>''')
pxEdit = Px('''
<select if="field.link"
var2="requestValue=req.get(name, []);
@ -253,7 +248,7 @@ class Ref(Field):
isBeingCreated=zobj.isTemporary()"
name=":name" size="isMultiple and field.height or ''"
multiple="isMultiple and 'multiple' or ''">
<option value="" if="not isMultiple">:_('choose_a_value')"></option>
<option value="" if="not isMultiple">:_('choose_a_value')</option>
<option for="ztied in zobjects" var2="uid=ztied.o.UID()"
selected=":inRequest and (uid in requestValue) or \
(uid in uids)"
@ -261,7 +256,7 @@ class Ref(Field):
</select>''')
pxSearch = Px('''<x>
<label lfor=":widgetName">:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<label lfor=":widgetName">:_(field.labelId)</label><br/>&nbsp;&nbsp;
<!-- The "and" / "or" radio buttons -->
<x if="field.multiplicity[1] != 1"
var2="operName='o_%s' % name;
@ -269,15 +264,15 @@ class Ref(Field):
andName='%s_and' % operName">
<input type="radio" name=":operName" id=":orName" checked="checked"
value="or"/>
<label lfor=":orName">:_('search_or')"></label>
<label lfor=":orName">:_('search_or')</label>
<input type="radio" name=":operName" id=":andName" value="and"/>
<label lfor=":andName">:_('search_and')"></label><br/>
<label lfor=":andName">:_('search_and')</label><br/>
</x>
<!-- The list of values -->
<select name=":widgetName" size=":field.sheight" multiple="multiple">
<option for="v in ztool.getSearchValues(name, className)"
var2="uid=v[0]; title=field.getReferenceLabel(v[1])" value=":uid"
title=":title">:ztool.truncateValue(title,field.swidth)"></option>
title=":title">:ztool.truncateValue(title,field.swidth)</option>
</select>
</x>''')
@ -603,7 +598,7 @@ class Ref(Field):
addPermission = '%s: Add %s' % (tool.getAppName(),
tool.getPortalType(self.klass))
folder = obj.getCreateFolder()
if not obj.getUser().has_permission(addPermission, folder):
if not tool.getUser().has_permission(addPermission, folder):
return No('no_add_perm')
return True
@ -623,6 +618,19 @@ class Ref(Field):
else:
return self.callMethod(obj, self.changeOrder)
def doChangeOrder(self, obj):
'''Moves a referred object up or down.'''
rq = obj.REQUEST
# Move the item up (-1), down (+1) ?
move = (rq['move'] == 'down') and 1 or -1
# The UID of the referred object to move
uid = rq['refObjectUid']
uids = getattr(obj.aq_base, self.name)
oldIndex = uids.index(uid)
uids.remove(uid)
newIndex = oldIndex + move
uids.insert(newIndex, uid)
def getSelectableObjects(self, obj):
'''This method returns the list of all objects that can be selected to
be linked as references to p_obj via p_self.'''
@ -647,7 +655,7 @@ class Ref(Field):
if refType.type == 'String':
if refType.format == 2:
value = self.xhtmlToText.sub(' ', value)
elif type(value) in sequenceTypes:
elif type(value) in sutils.sequenceTypes:
value = ', '.join(value)
prefix = ''
if res:
@ -664,6 +672,13 @@ class Ref(Field):
if not uids: raise IndexError()
return uids.index(refObj.UID())
def sort(self, obj):
'''Called when the user wants to sort the content of this field.'''
rq = obj.REQUEST
sortKey = rq.get('sortKey')
reverse = rq.get('reverse') == 'True'
obj.appy().sort(self.name, sortKey=sortKey, reverse=reverse)
def autoref(klass, field):
'''klass.field is a Ref to p_klass. This kind of auto-reference can't be
declared in the "normal" way, like this:

View file

@ -96,7 +96,7 @@ class String(Field):
<div if="not mayAjaxEdit" class="xhtml">::value</div>
<div if="mayAjaxEdit" class="xhtml" contenteditable="true"
id=":'%s_%s_ck' % (zobj.UID(), name)">::value</div>
<script if="mayAjaxEdit">:field.getJsInlineInit(zobj)"></script>
<script if="mayAjaxEdit">::field.getJsInlineInit(zobj)"></script>
</x>
<input type="hidden" if="masterCss" class=":masterCss" value=":rawValue"
name=":name" id=":name"/>
@ -116,8 +116,7 @@ class String(Field):
size=":isMultiple and field.height or 1">
<option for="val in possibleValues" value=":val[0]"
selected=":field.isSelected(zobj, val[0], rawValue)"
title=":val[1]">:ztool.truncateValue(val[1], field.width)">
</option>
title=":val[1]">:ztool.truncateValue(val[1],field.width)</option>
</select>
<x if="isOneLine and not isSelect">
<input id=":name" name=":name" size=":field.width"
@ -137,18 +136,18 @@ class String(Field):
rows=":field.height">:inRequest and requestValue or value
</textarea>
<script if="fmt == 2"
type="text/javascript">:field.getJsInit(zobj)</script>
type="text/javascript">::field.getJsInit(zobj)</script>
</x>
</x>''')
pxCell = Px('''
<x var="multipleValues=value and isMultiple">
<x if="multipleValues">:', '.join(value)"></x>
<x if="multipleValues">:', '.join(value)</x>
<x if="not multipleValues">:field.pxView</x>
</x>''')
pxSearch = Px('''<x>
<label lfor="widgetName">:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<label lfor="widgetName">:_(field.labelId)</label><br/>&nbsp;&nbsp;
<!-- Show a simple search field for most String fields -->
<input if="not field.isSelect" type="text" maxlength=":field.maxChars"
size=":field.swidth" value=":field.sdefault"
@ -166,7 +165,7 @@ class String(Field):
value="or"/>
<label lfor=":orName">:_('search_or')</label>
<input type="radio" name=":operName" id=":andName" value="and"/>
<label lfor=":andName">:_('search_and')"></label><br/>
<label lfor=":andName">:_('search_and')</label><br/>
</x>
<!-- The list of values -->
<select var="preSelected=field.sdefault"
@ -656,7 +655,7 @@ class String(Field):
if isinstance(v, int): sv = str(v)
else: sv = '"%s"' % v
ck.append('%s: %s' % (k, sv))
return 'CKEDITOR.replace("%s", {%s})' % (name, ', '.join(ck))
return 'CKEDITOR.replace("%s", {%s})' % (self.name, ', '.join(ck))
def getJsInlineInit(self, obj):
'''Gets the Javascript init code for enabling inline edition of this
@ -665,8 +664,8 @@ class String(Field):
return "CKEDITOR.disableAutoInline = true;\n" \
"CKEDITOR.inline('%s_%s_ck', {on: {blur: " \
"function( event ) { var data = event.editor.getData(); " \
"askAjaxChunk('%s_%s','POST','%s','page','saveField', "\
"{'fieldName':'%s', 'fieldContent': encodeURIComponent(data)}, "\
"askAjaxChunk('%s_%s','POST','%s','%s:pxSave', " \
"{'fieldContent': encodeURIComponent(data)}, " \
"null, evalInnerScripts);}}});"% \
(uid, self.name, uid, self.name, obj.absolute_url(), self.name)