[gen] More work ZPT->PX.
This commit is contained in:
parent
2e9a832463
commit
34e3a3083e
31 changed files with 3287 additions and 3067 deletions
|
@ -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] > 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
|
||||
|
|
|
@ -41,14 +41,14 @@ class Boolean(Field):
|
|||
|
||||
pxSearch = Px('''
|
||||
<x var="typedWidget='%s*bool' % widgetName">
|
||||
<label lfor=":widgetName">:_(field.labelId)"></label><br/>
|
||||
<label lfor=":widgetName">:_(field.labelId)</label><br/>
|
||||
<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"
|
||||
|
|
|
@ -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)' % \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -91,7 +91,7 @@ class Date(Field):
|
|||
monthFromName='%s_from_month' % name;
|
||||
yearFromName='%s*date' % widgetName">
|
||||
<td width="10px"> </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> </td>
|
||||
<td><label>_('search_to')</label> </td>
|
||||
<td height="20px">
|
||||
<select id=":dayToName" name=":dayToName">
|
||||
<option value="">--</option>
|
||||
|
|
|
@ -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> -
|
||||
<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 = ''
|
||||
|
||||
|
|
|
@ -36,10 +36,10 @@ class Float(Field):
|
|||
value=":inRequest and requestValue or value" type="text"/>''')
|
||||
|
||||
pxSearch = Px('''<x>
|
||||
<label>:_(field.labelId)"></label><br/>
|
||||
<label>:_(field.labelId)</label><br/>
|
||||
<!-- 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>
|
||||
|
|
|
@ -33,7 +33,7 @@ class Integer(Field):
|
|||
value=":inRequest and requestValue or value" type="text"/>''')
|
||||
|
||||
pxSearch = Px('''<x>
|
||||
<label>:_(field.labelId)"></label><br/>
|
||||
<label>:_(field.labelId)</label><br/>
|
||||
<!-- 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/>
|
||||
|
|
|
@ -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()
|
||||
|
|
103
fields/ref.py
103
fields/ref.py
|
@ -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)>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 > 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&className=%s&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&ref=%s:%s' % \
|
||||
(ztool.absolute_url(), linkedPortalType, zobj.UID(), \
|
||||
q('%s/search?className=%s&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/>
|
||||
<label lfor=":widgetName">:_(field.labelId)</label><br/>
|
||||
<!-- 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:
|
||||
|
|
|
@ -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/>
|
||||
<label lfor="widgetName">:_(field.labelId)</label><br/>
|
||||
<!-- 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)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue