[gen] Continued PX-based refactoring.

This commit is contained in:
Gaetan Delannay 2013-07-10 09:56:35 +02:00
parent 25b4edfc1d
commit 369e41b43c
15 changed files with 481 additions and 272 deletions

View file

@ -356,7 +356,7 @@ class Field:
# represents the maximum number of chars that a given input field may
# accept (corresponds to HTML "maxlength" property). "None" means
# "unlimited".
self.maxChars = maxChars
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

View file

@ -28,20 +28,20 @@ class Action(Field):
pxView = pxCell = Px('''
<form name="executeAppyAction"
var="formId='%s_%s_form' % (contextObj.UID(), name);
label=_(widget['labelId'])"
label=_(field.labelId)"
id=":formId" action=":ztool.absolute_url() + '/do'">
<input type="hidden" name="action" value="ExecuteAppyAction"/>
<input type="hidden" name="objectUid" value=":contextObj.UID()"/>
<input type="hidden" name="fieldName" value=":name"/>
<x if="widget['confirm']"><input
<x if="field.confirm"><input
type="button" class="button"
var="labelConfirm=_(widget['labelId'] + '_confirm')"
var="labelConfirm=_(field.labelId + '_confirm')"
value=":ztool.truncateValue(label)" title=":label"
style=":'background-image: url(%s/ui/buttonAction.png)' % appUrl"
onclick=":'askConfirm(%s,%s,%s)' % (q('form'), q(formId), \
q(labelConfirm))"/>
</x>
<input if="not widget['confirm']" type="submit" class="button" name="do"
<input if="not field.confirm" type="submit" class="button" name="do"
value=":ztool.truncateValue(label)" title=":label"
style=":'background-image: url(%s/ui/buttonAction.png)' % appUrl"/>
</form>''')

View file

@ -30,7 +30,7 @@ class Boolean(Field):
</x>''')
pxEdit = Px('''
<x var="isChecked=contextObj.checkboxChecked(name, rawValue)">
<x var="isChecked=field.isChecked(contextObj, rawValue)">
<input type="checkbox" name=":name + '_visible'" id=":name"
class=":masterCss" checked=":isChecked"
onclick=":'toggleCheckbox(%s, %s); updateSlaves(this)' % \
@ -41,19 +41,19 @@ class Boolean(Field):
pxSearch = Px('''
<x var="typedWidget='%s*bool' % widgetName">
<label lfor=":widgetName">:_(widget['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>
<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>
<input type="radio" value="False" name=":typedWidget" id=":valueId"/>
<label lfor=":valueId">:_('no')"></label>
</x>
<x var="valueId='%s_whatever' % name">
<input type="radio" value="" name=":typedWidget" id=":valueId"
checked="checked"/>
<label lfor=":valueId">:_('whatever')"></label>
<input type="radio" value="" name=":typedWidget" id=":valueId"
checked="checked"/>
<label lfor=":valueId">:_('whatever')</label>
</x><br/>
</x>''')
@ -96,4 +96,13 @@ class Boolean(Field):
if not self.isEmptyValue(value):
exec 'res = %s' % value
return res
def isChecked(self, obj, dbValue):
'''When rendering this field as a checkbox, must it be checked or
not?'''
rq = obj.REQUEST
# Get the value we must compare (from request or from database)
if rq.has_key(self.name):
return rq.get(self.name) in ('True', 1, '1')
return dbValue
# ------------------------------------------------------------------------------

View file

@ -16,35 +16,28 @@ class Calendar(Field):
# Month view for a calendar. Called by pxView, and directly from the UI,
# via Ajax, when the user selects another month.
pxMonthView = Px('''
<div var="fieldName=req['fieldName'];
ajaxHookId=contextObj.UID() + fieldName;
<div var="field=contextObj.getAppyType(req['fieldName']);
ajaxHookId=contextObj.UID() + field.name;
month=req['month'];
monthDayOne=DateTime('%s/01' % month);
today=DateTime('00:00');
grid=contextObj.callField(fieldName, 'getMonthGrid', month);
allEventTypes=contextObj.callField(fieldName, 'getEventTypes', \
contextObj);
preComputed=contextObj.callField(fieldName, \
'getPreComputedInfo', contextObj, monthDayOne, grid);
defaultDate=contextObj.callField(fieldName, 'getDefaultDate', \
contextObj);
grid=field.getMonthGrid(month);
allEventTypes=field.getEventTypes(contextObj);
preComputed=field.getPreComputedInfo(contextObj, monthDayOne, \
grid);
defaultDate=field.getDefaultDate(contextObj);
defaultDateMonth=defaultDate.strftime('%Y/%m');
previousMonth=contextObj.callField(fieldName, \
'getSiblingMonth', month, 'previous');
nextMonth=contextObj.callField(fieldName, 'getSiblingMonth', \
month, 'next');
widget=contextObj.getAppyType(fieldName, asDict=True);
previousMonth=field.getSiblingMonth(month, 'previous');
nextMonth=field.getSiblingMonth(month, 'next');
mayEdit=contextObj.allows(widget['writePermission']);
objUrl=contextObj/absolute_url();
startDate=contextObj.callField(fieldName, 'getStartDate', \
contextObj);
endDate=contextObj.callField(fieldName, 'getEndDate',contextObj);
otherCalendars=contextObj.callField(fieldName, \
'getOtherCalendars', contextObj, preComputed)"
objUrl=contextObj.absolute_url();
startDate=field.getStartDate(contextObj);
endDate=field.getEndDate(contextObj);
otherCalendars=field.getOtherCalendars(contextObj, preComputed)"
id=":ajaxHookId">
<script type="text/javascript">:'var %s_maxEventLength = %d;' % \
(fieldName, widget['maxEventLength'])">
(field.name, field.maxEventLength)">
</script>
<!-- Month chooser -->
@ -58,7 +51,7 @@ class Calendar(Field):
<img style="cursor:pointer" tal:condition="goBack"
src=":'%s/ui/arrowLeftSimple.png' % appUrl"
onclick=":'askMonthView(%s, %s, %s, %s)' % \
(q(ajaxHookId),q(objUrl),q(fieldName),q(previousMonth))"/>
(q(ajaxHookId),q(objUrl),q(field.name),q(previousMonth))"/>
<!-- Go back to the default date -->
<x if="goBack or goForward">
<input type="button"
@ -67,14 +60,14 @@ class Calendar(Field):
'today' or 'goto_source'"
value=":_(label)"
onclick=":'askMonthView(%s, %s, %s, %s)' % (q(ajaxHookId), \
q(objUrl), q(fieldName), q(defaultDateMonth))"
q(objUrl), q(field.name), q(defaultDateMonth))"
disabled=":defaultDate.strftime(fmt)==monthDayOne.strftime(fmt)"/>
</x>
<!-- Go to the next month -->
<img style="cursor:pointer" if="goForward"
src=":'%s/ui/arrowRightSimple.png' % appUrl"
onclick=":'askMonthView(%s, %s, %s, %s)' % (q(ajaxHookId), \
q(objUrl), q(fieldName), q(nextMonth))"/>
q(objUrl), q(field.name), q(nextMonth))"/>
<span>:_('month_%s' % monthDayOne.aMonth())</span>
<span>:month.split('/')[0]</span>
</div>
@ -82,11 +75,10 @@ class Calendar(Field):
<!-- Calendar month view -->
<table cellpadding="0" cellspacing="0" width="100%" class="list"
style="font-size: 95%"
var="rowHeight=int(widget['height']/float(len(grid)))">
var="rowHeight=int(field.height/float(len(grid)))">
<!-- 1st row: names of days -->
<tr height="22px">
<th for="dayName in contextObj.callField(fieldName, 'getNamesOfDays', \
contextObj)"
<th for="dayName in field.getNamesOfDays(contextObj)"
width="14%">:dayName</th>
</tr>
<!-- The calendar in itself -->
@ -95,16 +87,13 @@ class Calendar(Field):
<x var="tooEarly=startDate and (date &lt; startDate);
tooLate=endDate and not tooEarly and (date &gt; endDate);
inRange=not tooEarly and not tooLate;
cssClasses=contextObj.callField(fieldName, 'getCellStyle', \
contextObj, date, today)">
cssClasses=field.getCellStyle(contextObj, date, today)">
<!-- Dump an empty cell if we are out of the supported date range -->
<td if="not inRange" class=":cssClasses"></td>
<!-- Dump a normal cell if we are in range -->
<x if="inRange">
<td var="events=contextObj.callField(fieldName, 'getEventsAt', \
contextObj, date);
spansDays=contextObj.callField(fieldName, 'hasEventsAt', \
contextObj, date+1, events);
<td var="events=field.getEventsAt(contextObj, date);
spansDays=field.hasEventsAt(contextObj, date+1, events);
mayCreate=mayEdit and not events;
mayDelete=mayEdit and events"
style="date.isCurrentDay() and 'font-weight:bold' or \
@ -121,39 +110,37 @@ class Calendar(Field):
<!-- Icon for adding an event -->
<x if="mayCreate">
<img style="visibility:hidden; cursor:pointer"
var="info=contextObj.callField(fieldName, \
'getApplicableEventsTypesAt', contextObj, date, \
var="info=field.getApplicableEventsTypesAt(contextObj, date, \
allEventTypes, preComputed, True)"
if="info['eventTypes']"
src=":'%s/ui/plus.png' % appUrl"
onclick=":'openEventPopup(%s, %s, %s, null, %s, %s)' % \
(q('new'), q(fieldName), q(dayString), \
(q('new'), q(field.name), q(dayString), \
q(info['eventTypes']), q(info['message']))"/>
</x>
<!-- Icon for deleting an event -->
<img if="mayDelete" style="visibility:hidden; cursor:pointer"
src=":'%s/ui/delete.png' % appUrl"
onclick=":'openEventPopup(%s, %s, %s, %s, null, null)' % \
(q('del'), q(fieldName), q(dayString), q(str(spansDays)))"/>
(q('del'), q(field.name), q(dayString), q(str(spansDays)))"/>
<x if="events">
<!-- A single event is allowed for the moment -->
<div var="eventType=events[0]['eventType']">
<span style="color: grey">:contextObj.callField(fieldName, \
'getEventName', contextObj, eventType)"></span>
<span style="color: grey">:field.getEventName(contextObj, \
eventType)"></span>
</div>
</x>
<!-- Events from other calendars -->
<x if="otherCalendars">
<x var="otherEvents=contextObj.callField(fieldName, \
'getOtherEventsAt', contextObj, date, otherCalendars)"
<x var="otherEvents=field.getOtherEventsAt(contextObj, date, \
otherCalendars)"
if="otherEvents">
<div style=":'color: %s; font-style: italic' % event['color']"
for="event in otherEvents">:event['name']</div>
</x>
</x>
<!-- Additional info -->
<x var="info=contextObj.callField(fieldName, \
'getAdditionalInfoAt', contextObj, date, preComputed)"
<x var="info=field.getAdditionalInfoAt(contextObj,date,preComputed)"
if="info">::info</x>
</x>
</td>
@ -164,13 +151,13 @@ class Calendar(Field):
</table>
<!-- Popup for creating a calendar event -->
<div var="prefix='%s_newEvent' % fieldName;
<div var="prefix='%s_newEvent' % field.name;
popupId=prefix + 'Popup'"
id=":popupId" class="popup" align="center">
<form id="prefix + 'Form'" method="post">
<input type="hidden" name="fieldName" value=":fieldName"/>
<input type="hidden" name="fieldName" value=":field.name"/>
<input type="hidden" name="month" value=":month"/>
<input type="hidden" name="name" value=":fieldName"/>
<input type="hidden" name="name" value=":field.name"/>
<input type="hidden" name="action" value="Process"/>
<input type="hidden" name="actionType" value="createEvent"/>
<input type="hidden" name="day"/>
@ -180,8 +167,7 @@ class Calendar(Field):
<select name="eventType">
<option value="">:_('choose_a_value')"></option>
<option for="eventType in allEventTypes"
value=":eventType">:contextObj.callField(fieldName, \
'getEventName', contextObj, eventType)">
value=":eventType">:field.getEventName(contextObj, eventType)">
</option>
</select><br/><br/>
<!--Span the event on several days -->
@ -193,7 +179,7 @@ class Calendar(Field):
value=":_('object_save')"
onclick=":'triggerCalendarEvent(%s, %s, %s, %s, \
%s_maxEventLength)' % (q('new'), q(ajaxHookId), \
q(fieldName), q(objUrl), fieldName)"/>
q(field.name), q(objUrl), field.name)"/>
<input type="button"
value=":_('object_cancel')"
onclick=":'closePopup(%s)' % q(popupId)"/>
@ -201,13 +187,13 @@ class Calendar(Field):
</div>
<!-- Popup for deleting a calendar event -->
<div var="prefix='%s_delEvent' % fieldName;
<div var="prefix='%s_delEvent' % field.name;
popupId=prefix + 'Popup'"
id=":popupId" class="popup" align="center">
<form id=":prefix + 'Form'" method="post">
<input type="hidden" name="fieldName" value=":fieldName"/>
<input type="hidden" name="fieldName" value=":field.name"/>
<input type="hidden" name="month" value=":month"/>
<input type="hidden" name="name" value=":fieldName"/>
<input type="hidden" name="name" value=":field.name"/>
<input type="hidden" name="action" value="Process"/>
<input type="hidden" name="actionType" value="deleteEvent"/>
<input type="hidden" name="day"/>
@ -227,7 +213,7 @@ class Calendar(Field):
</div>
<input type="button" value=":_('yes')"
onClick=":'triggerCalendarEvent(%s, %s, %s, %s)' % \
(q('del'), q(ajaxHookId), q(fieldName), q(objUrl))"/>
(q('del'), q(ajaxHookId), q(field.name), q(objUrl))"/>
<input type="button" value=":_('no')"
onclick=":'closePopup(%s)' % q(popupId)"/>
</form>
@ -235,13 +221,11 @@ class Calendar(Field):
</div>''')
pxView = pxCell = Px('''
<x var="defaultDate=contextObj.callField(widget['name'], 'getDefaultDate',\
contextObj);
x=req.set('fieldName', widget['name']);
<x var="defaultDate=field.getDefaultDate(contextObj);
x=req.set('fieldName', field.name);
x=req.set('month', defaultDate.strftime('%Y/%m'))">
<x>:widget['pxMonthView']></x>
</x>
''')
<x>:field.pxMonthView</x>
</x>''')
pxEdit = pxSearch = ''

View file

@ -24,15 +24,15 @@ class Computed(Field):
# Ajax-called view content of a non sync Computed field.
pxViewContent = Px('''
<x var="name=req['fieldName'];
widget=contextObj.getAppyType(name, asDict=True);
field=contextObj.getAppyType(name);
value=contextObj.getFieldValue(name);
sync=True">:widget['pxView']</x>''')
sync=True">:field.pxView</x>''')
pxView = pxCell = pxEdit = Px('''
<x>
<x if="sync">
<x if="widget['plainText']">:value</x>
<x if="not widget['plainText']">::value></x>
<x if="field.plainText">:value</x>
<x if="not field.plainText">::value></x>
</x>
<x if="not sync">
<div var="ajaxHookId=contextObj.UID() + name" id="ajaxHookId">
@ -45,12 +45,9 @@ class Computed(Field):
pxSearch = Px('''
<x>
<label lfor=":widgetName">:_(widget['labelId'])"></label><br/>&nbsp;&nbsp;
<input type="text"
var="maxChars=widget['maxChars'] and widget['maxChars'] or ''"
name=":'%s*string' % widgetName"
maxlength=":maxChars" size=":widget['width']"
value=":widget['sdefault']"/>
<label lfor=":name">:field.labelId</label><br/>&nbsp;&nbsp;
<input type="text" name=":'%s*string' % name" maxlength=":field.maxChars"
size=":field.width" value=":field.sdefault"/>
</x>''')
def __init__(self, validator=None, multiplicity=(0,1), default=None,

View file

@ -24,15 +24,15 @@ class Date(Field):
pxView = pxCell = Px('''<x>:value</x>''')
pxEdit = Px('''
<x var="years=contextObj.getSelectableYears(widget['name'])">
<x var="years=field.getSelectableYears()">
<!-- Day -->
<select var="days=range(1,32)"
name=":'%s_day' % name" id=":'%s_day' % name">
<option value="">-</option>
<x for="day in days">
<option var="zDay=str(day).zfill(2)" value=":zDay"
selected="contextObj.dateValueSelected(name, 'day', day, \
rawValue)">:zDay></option></x>
selected="field.isSelected(contextObj, 'day', day, \
rawValue)">:zDay</option></x>
</select>
<!-- Month -->
@ -41,50 +41,50 @@ class Date(Field):
<option value="">-</option>
<x for="month in months">
<option var="zMonth=str(month).zfill(2)" value=":zMonth"
selected="contextObj.dateValueSelected(name, 'month', month, \
rawValue)">:zMonth</option></x>
selected="field.isSelected(contextObj, 'month', month, \
rawValue)">:zMonth</option></x>
</select>
<!-- Year -->
<select name=":'%s_year' % name" id=":'%s_year' % name">
<option value="">-</option>
<option for="year in years" value=":year"
selected="contextObj.dateValueSelected(name, 'year', year, \
rawValue)">:year</option>
selected="field.isSelected(contextObj, name, 'year', year, \
rawValue)">:year</option>
</select>
<!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']">
<x if="field.calendar">
<input type="hidden" id=":name" name=":name"/>
<img id=":'%s_img' % name" src=":'%s/ui/calendar.gif' % appUrl"/>
<script type="text/javascript">:contextObj.getCalendarInit(name, years)
</script>
<script type="text/javascript">:field.getJsInit(name, years)</script>
</x>
<!-- Hour and minutes -->
<x if="widget['format'] == 0">
<select var="hours=range(0,24)"
name=":'%s_hour' % name" id=":'%s_hour' % name">
<option value="">-</option>
<x for="hour in hours">
<option var="zHour=str(hour).zfill(2)" value=":zHour"
selected=":contextObj.dateValueSelected(name, 'hour', hour, \
rawValue)">:zHour</option></x>
</select> :
<select var="minutes=range(0,60,5)"
name=":'%s_minute' % name" id=":'%s_minute' % name">
<option value="">-</option>
<x for="minute in minutes">
<option var="zMinute=str(minute).zfill(2)" value=":zMinute"
selected=":contextObj.dateValueSelected(name, 'minute', \
minute, rawValue)">:zMinute</option></x>
</select>
<x if="field.format == 0">
<select var="hours=range(0,24)" name=":'%s_hour' % name"
id=":'%s_hour' % name">
<option value="">-</option>
<x for="hour in hours">
<option var="zHour=str(hour).zfill(2)" value=":zHour"
selected=":field.isSelected(contextObj, 'hour', hour, \
rawValue)">:zHour</option>
</x>
</select> :
<select var="minutes=range(0,60,5)" name=":'%s_minute' % name"
id=":'%s_minute' % name">
<option value="">-</option>
<x for="minute in minutes">
<option var="zMinute=str(minute).zfill(2)" value=":zMinute"
selected=":field.isSelected(contextObj, 'minute', minute,\
rawValue)">:zMinute</option></x>
</select>
</x>
</x>''')
pxSearch = Px('''
<x var="years=range(widget['startYear'], widget['endYear']+1)">
<label>:_(widget['labelId'])</label>
<x var="years=range(field.startYear, field.endYear+1)">
<label>:_(field.labelId)</label>
<table>
<!-- From -->
<tr var="fromName='%s_from' % name;
@ -106,14 +106,14 @@ class Date(Field):
</select> /
<select id=":yearFromName" name=":yearFromName">
<option value="">--</option>
<option for="value in range(widget['startYear'],widget['endYear']+1)"
<option for="value in range(field.startYear, field.endYear+1)"
value=":value">:value</option>
</select>
<!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']">
<x if="field.calendar">
<input type="hidden" id=":fromName" name=":fromName"/>
<img id=":'%s_img' % fromName" src=":'%s/ui/calendar.gif' % appUrl"/>
<script type="text/javascript">:tool.getCalendarInit(fromName, years)
<script type="text/javascript">:field.getJsInit(fromName, years)
</script>
</x>
</td>
@ -139,14 +139,14 @@ class Date(Field):
</select> /
<select id=":yearToName" name=":yearToName">
<option value="">--</option>
<option for="value in range(widget['startYear'],widget['endYear']+1)"
<option for="value in range(field.startYear, field.endYear+1)"
value=":value">:value</option>
</select>
<!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']">
<x if="widget.calendar">
<input type="hidden" id=":toName" name=":toName"/>
<img id=":'%s_img' % toName" src=":%s/ui/calendar.gif' % appUrl"/>
<script type="text/javascript">:tool.getCalendarInit(toName, years)">
<script type="text/javascript">:field.getJsInit(toName, years)">
</script>
</x>
</td>
@ -248,4 +248,30 @@ class Date(Field):
return DateTime.DateTime(value)
def getIndexType(self): return 'DateIndex'
def isSelected(self, obj, fieldPart, dateValue, dbValue):
'''When displaying this field, must the particular p_dateValue be
selected in the sub-field p_fieldPart corresponding to the date
part?'''
# Get the value we must compare (from request or from database)
rq = obj.REQUEST
partName = '%s_%s' % (self.name, fieldPart)
if rq.has_key(partName):
compValue = rq.get(partName)
if compValue.isdigit():
compValue = int(compValue)
else:
compValue = dbValue
if compValue:
compValue = getattr(compValue, fieldPart)()
# Compare the value
return compValue == dateValue
def getJsInit(self, name, years):
'''Gets the Javascript init code for displaying a calendar popup for
this field, for an input named p_name (which can be different from
self.name if, ie, it is a search field).'''
return 'Calendar.setup({inputField: "%s", button: "%s_img", ' \
'onSelect: onSelectDate, range:[%d,%d]});' % \
(name, name, years[0], years[-1])
# ------------------------------------------------------------------------------

View file

@ -16,6 +16,7 @@
# ------------------------------------------------------------------------------
import time, os.path, mimetypes
from appy import Object
from appy.fields import Field
from appy.px import Px
from appy.shared import utils as sutils
@ -24,32 +25,32 @@ from appy.shared import utils as sutils
class File(Field):
pxView = pxCell = Px('''
<x var="info=contextObj.getFileInfo(value);
empty=not info['size'];
<x var="info=field.getFileInfo(value);
empty=not info.size;
imgSrc='%s/download?name=%s' % (contextObj.absolute_url(), name)">
<x if="not empty and not widget['isImage']">
<a href=":imgSrc">:info['filename']"</a>&nbsp;&nbsp;-
<i class="discreet">'%sKb' % (info['size'] / 1024)"></i>
<x if="not empty and not field.isImage">
<a href=":imgSrc">:info.filename</a>&nbsp;&nbsp;-
<i class="discreet">'%sKb' % (info.size / 1024)"></i>
</x>
<x if="not empty and widget['isImage']"><img src=":imgSrc"/></x>
<x if="not empty and field.isImage"><img src=":imgSrc"/></x>
<x if="empty">-</x>
</x>''')
pxEdit = Px('''
<x var="info=contextObj.getFileInfo(value);
empty= not info['size'];
<x var="info=field.getFileInfo(value);
empty= not info.size;
fName=q('%s_file' % name)">
<x if="not: empty">:widget['pxView']</x><br/>
<x if="not empty">:field.pxView</x><br/>
<x if="not empty">
<!-- Keep the file unchanged. -->
<input type="radio" value="nochange"
checked=":(info['size'] != 0) and 'checked' or None"
checked=":(info.size != 0) and 'checked' or None"
name=":'%s_delete' % name" id=":'%s_nochange' % name"
onclick=":'document.getElementById(%s).disabled=true' % fName"/>
<label lfor=":'%s_nochange' % name">Keep the file unchanged</label><br/>
<!-- Delete the file. -->
<x if="not widget['required']">
<x if="not field.required">
<input type="radio" value="delete"
name=":'%s_delete' % name" id=":'%s_delete' % name"
onclick=":'document.getElementById(%s).disabled=true' % fName"/>
@ -57,14 +58,14 @@ class File(Field):
</x>
<!-- Replace with a new file. -->
<input type="radio" value=""
checked=":(info['size'] == 0) and 'checked' or None"
checked=":(info.size == 0) and 'checked' or None"
name=":'%s_delete' % name" id=":'%s_upload' % name"
onclick=":'document.getElementById(%s).disabled=false' % fName"/>
<label lfor=":'%s_upload' % name">Replace it with a new file</label><br/>
</x>
<!-- The upload field. -->
<input type="file" name=":'%s_file' % name" id=":'%s_file' % name"
size=":widget['width']"/>
size=":field.width"/>
<script var="isDisabled=empty and 'false' or 'true'"
type="text/javascript">:document.getElementById(%s).disabled=%s'%\
(q(fName), q(isDisabled))">
@ -223,4 +224,9 @@ class File(Field):
if rq: action = rq.get('%s_delete' % self.name, None)
if action == 'nochange': pass
else: setattr(obj, self.name, None)
def getFileInfo(self, fileObject):
'''Returns filename and size of p_fileObject.'''
if not fileObject: return Object(filename='', size=0)
return Object(filename=fileObject.filename, size=fileObject.size)
# ------------------------------------------------------------------------------

View file

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

View file

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

View file

@ -27,12 +27,10 @@ class List(Field):
# PX for rendering a single row.
pxRow = Px('''
<tr valign="top" style="(rowIndex==-1) and 'display: none' or ''">
<td align="center" for="fieldInfo in widget['fieldsd']">
<x var="widget=fieldInfo[1];
<td align="center" for="info in field.fields">
<x var="field=info[1];
tagCss='noStyle';
widgetName='%s*%d' % (widget['name'], rowIndex)">
<x>:widget['pxView']</x>
</x>
widgetName='%s*%d' % (field.name, rowIndex)">:field.pxView</x>
</td>
<!-- Icon for removing the row -->
<td if="layoutType=='edit'" align=":dright">
@ -48,8 +46,7 @@ class List(Field):
id=":'list_%s' % name" class="isEdit and 'grid' or 'list'">
<!-- Header -->
<tr valign="bottom">
<th for="fieldInfo in widget['fieldsd']">::_(fieldInfo[1]['labelId'])
</th>
<th for="info in field.fields">::_(info[1].labelId)</th>
<!-- Icon for adding a new row. -->
<th if="isEdit">
<img style="cursor:pointer" src=":'%s/ui/plus.png' % appUrl"
@ -59,21 +56,20 @@ class List(Field):
</tr>
<!-- Template row (edit only) -->
<x var="rowIndex=-1" if="isEdit">:widget['pxRow']</x>
<x var="rowIndex=-1" if="isEdit">:field.pxRow</x>
<tr height="7px" if="isEdit"><td></td></tr>
<!-- Rows of data -->
<x var="rows =inRequest and requestValue or value" for="row in rows">
<x var="rowIndex=loop.row.nb">:widget['pxRow']</x>
<x var="rows=inRequest and requestValue or value" for="row in rows">
<x var="rowIndex=loop.row.nb">:field.pxRow</x>
</x>
</table>''')
pxView = pxCell = Px('''<x>:widget['pxTable']</x>''')
pxView = pxCell = Px('''<x>:field.pxTable</x>''')
pxEdit = Px('''
<x>
<!-- This input makes Appy aware that this field is in the request -->
<input type="hidden" name=":name" value=""/>
<x>:widget['pxTable']</x>
<input type="hidden" name=":name" value=""/><x>:field.pxTable</x>
</x>''')
pxSearch = ''

View file

@ -33,7 +33,6 @@ class Ogone(Field):
pxView = pxCell = Px('''
<x>
<!-- var "value" is misused and contains the contact params for Ogone -->
<p>:value</p>
<!-- The form for sending the payment request to Ogone -->
<form method="post" id="form1" name="form1" var="env=value['env']"
action=":'https://secure.ogone.com/ncol/%s/orderstandard.asp'% env">

View file

@ -28,7 +28,7 @@ from appy.shared import utils as sutils
class Pod(Field):
'''A pod is a field allowing to produce a (PDF, ODT, Word, RTF...) document
from data contained in Appy class and linked objects or anything you
want to put in it. It uses appy.pod.'''
want to put in it. It is the way gen uses pod.'''
# Layout for rendering a POD field for exporting query results.
rLayouts = {'view': Table('fl', width=None)}
POD_ERROR = 'An error occurred while generating the document. Please ' \
@ -38,14 +38,14 @@ class Pod(Field):
pxView = pxCell = Px('''
<x>
<!-- Ask action -->
<x if="widget['askAction']">
<x var="doLabel='%s_askaction' % widget['labelId'];
<x if="field.askAction">
<x var="doLabel='%s_askaction' % field.labelId;
chekboxId='%s_%s_cb' % (contextObj.UID(), name)">
<input type="checkbox" name=":doLabel" id=":chekboxId"/>
<label lfor=":chekboxId" class="discreet">:_(doLabel)"></label>
</x>
</x>
<img for="podFormat in ztool.getPodInfo(contextObj, name)[1]"
<img for="podFormat in field.getToolInfo(contextObj.appy())[1]"
src=":'%s/ui/%s.png' % (appUrl, podFormat)"
onclick=":'generatePodDocument(%s, %s, %s, %s)' % \
(q(contextObj.UID()), q(name), q(podFormat), \

View file

@ -15,7 +15,7 @@
# Appy. If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------
import sys
import sys, re
from appy.fields import Field, No
from appy.px import Px
from appy.gen.layout import Table
@ -37,16 +37,15 @@ 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' % (contextObj.UID(), fieldName, \
appyType['pageName'], loop.obj.nb + startNumber, totalNumber);
navInfo=not appyType['isBack'] and navInfo or '';
<x var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), field.name, \
field.pageName, loop.obj.nb + startNumber, totalNumber);
navInfo=not field.isBack and navInfo or '';
cssClass=obj.getCssFor('title')">
<x>::obj.getSupTitle(navInfo)</x>
<a var="pageName=appyType['isBack'] and appyType['backd']['pageName'] or \
'main';
<a var="pageName=field.isBack and field.back.pageName or 'main';
fullUrl=obj.getUrl(page=pageName, nav=navInfo)"
href=":fullUrl" class=":cssClass">:(not includeShownInfo) and \
obj.Title() or contextObj.getReferenceLabel(fieldName, obj.appy())
obj.Title() or field.getReferenceLabel(obj.appy())
</a><span name="subTitle" style=":showSubTitles and 'display:inline' or \
'display:none'">::obj.getSubTitle()"</span>
</x>''')
@ -54,16 +53,16 @@ class Ref(Field):
# This PX displays icons for triggering actions on a given referenced object
# (edit, delete, etc).
pxObjectActions = Px('''
<table class="noStyle" var="isBack=appyType['isBack']">
<table class="noStyle" var="isBack=field.isBack">
<tr>
<!-- Arrows for moving objects up or down -->
<td if=":not isBack and (len(objs)&gt;1) and changeOrder and canWrite">
<x var="objectIndex=contextObj.getAppyRefIndex(fieldName, obj);
<td if="not isBack and (len(objs)&gt;1) and changeOrder and canWrite">
<x var="objectIndex=field.getIndexOf(contextObj, obj);
ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
(q(startNumber), q('ChangeRefOrder'), q('refObjectUid'),
q(obj.UID()), q('move'), q('**v**')))">
<img if="objectIndex &gt; 0" style="cursor:pointer"
src=":'%s/ui/arrowUp.png' % $appUrl" title=":_('move_up')"
src=":'%s/ui/arrowUp.png' % appUrl" title=":_('move_up')"
onclick=":ajaxBaseCall.replace('**v**', 'up')"/><img
style="cursor:pointer" if="objectIndex &lt; (totalNumber-1)"
src=":'%s/ui/arrowDown.png' % appUrl" title=":_('move_down')"
@ -75,27 +74,25 @@ class Ref(Field):
<x var="targetObj=obj">:targetObj.appy().pxTransitions</x>
</td>
<!-- Edit -->
<td if="not appyType['noForm'] and obj.mayEdit() and appyType['delete']">
<a var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, \
appyType['pageName'], loop.obj.nb + startNumber, \
totalNumber)"
<td if="not field.noForm and obj.mayEdit() and field.delete">
<a var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), field.name, \
field.pageName, loop.obj.nb + startNumber, totalNumber)"
href=":obj.getUrl(mode='edit', page='main', nav=navInfo)">
<img src=":'%s/ui/edit.png' % appUrl" title=":_('object_edit')"/>
</a>
</td>
<!-- Delete -->
<td if="not isBack and appyType['delete'] and canWrite and \
obj.mayDelete()">
<td if="not isBack and field.delete and canWrite and obj.mayDelete()">
<img style="cursor:pointer" title=":_('object_delete')"
src=":'%s/ui/delete.png' % appUrl"
onclick=":'onDeleteObject(%s)' % q(obj.UID())"/>
</td>
<!-- Unlink -->
<td if="not isBack and appyType['unlink'] and canWrite">
<td if="not isBack and field.unlink and canWrite">
<img style="cursor:pointer" title=":_('object_unlink')"
src=":'%s/ui/unlink.png' % appUrl"
onclick=":'onUnlinkObject(%s,%s,%s)' % (q(contextObj.UID()), \
q(appyType['name']), q(obj.UID()))"/>
q(field.name), q(obj.UID()))"/>
</td>
</tr>
</table>''')
@ -106,21 +103,21 @@ class Ref(Field):
<x if="showPlusIcon">
<input type="button" class="button"
var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), \
fieldName, appyType['pageName'], 0, totalNumber);
field.name, field.pageName, 0, totalNumber);
formCall='window.location=%s' % \
q('%s/do?action=Create&amp;className=%s&amp;nav=%s' % \
(folder.absolute_url(), linkedPortalType, navInfo));
formCall=not appyType['addConfirm'] and formCall or \
formCall=not field.addConfirm and formCall or \
'askConfirm(%s,%s,%s)' % (q('script'), q(formCall), \
q(addConfirmMsg));
noFormCall=navBaseCall.replace('**v**', \
'%d,%s' % (startNumber, q('CreateWithoutForm')));
noFormCall=not appyType['addConfirm'] and noFormCall or \
noFormCall=not field.addConfirm and noFormCall or \
'askConfirm(%s, %s, %s)' % (q('script'), q(noFormCall), \
q(addConfirmMsg))"
style=":'background-image: url(%s/ui/buttonAdd.png)' % appUrl"
value=":_('add_ref')"
onclick=":appyType['noForm'] and noFormCall or formCall"/>
onclick=":field.noForm and noFormCall or formCall"/>
</x>''')
# This PX displays, in a cell header from a ref table, icons for sorting the
@ -128,8 +125,8 @@ class Ref(Field):
pxSortIcons = Px('''
<x var="ajaxBaseCall=navBaseCall.replace('**v**', '%s,%s,{%s:%s,%s:%s}' % \
(q(startNumber), q('SortReference'), q('sortKey'), \
q(widget['name']), q('reverse'), q('**v**')))"
if="changeOrder and canWrite and ztool.isSortable(widget['name'], \
q(field.name), q('reverse'), q('**v**')))"
if="changeOrder and canWrite and ztool.isSortable(field.name, \
objs[0].meta_type, 'ref')">
<img style="cursor:pointer" src=":'%s/ui/sortAsc.png' % appUrl"
onclick=":ajaxBaseCall.replace('**v**', 'False')"/>
@ -140,29 +137,27 @@ class Ref(Field):
# This PX is called by a XmlHttpRequest (or directly by pxView) for
# displaying the referred objects of a reference field.
pxViewContent = Px('''
<div var="fieldName=req['fieldName'];
appyType=contextObj.getAppyType(fieldName, asDict=True);
innerRef=req.get('innerRef',False) == 'True';
ajaxHookId=contextObj.UID() + fieldName;
<div var="field=contextObj.getAppyType(req['fieldName']);
innerRef=req.get('innerRef', False) == 'True';
ajaxHookId=contextObj.UID() + field.name;
startNumber=int(req.get('%s_startNumber' % ajaxHookId, 0));
refObjects=contextObj.getAppyRefs(fieldName, startNumber);
objs=refObjects['objects'];
totalNumber=refObjects['totalNumber'];
batchSize=refObjects['batchSize'];
refObjects=field.getLinkedObjects(contextObj, startNumber);
objs=refObjects.objects;
totalNumber=refObjects.totalNumber;
batchSize=refObjects.batchSize;
folder=contextObj.getCreateFolder();
linkedPortalType=ztool.getPortalType(appyType['klass']);
canWrite=not appyType['isBack'] and \
contextObj.allows(appyType['writePermission']);
showPlusIcon=contextObj.mayAddReference(fieldName);
atMostOneRef=(appyType['multiplicity'][1] == 1) and \
linkedPortalType=ztool.getPortalType(field.klass);
canWrite=not field.isBack and \
contextObj.allows(field.writePermission);
showPlusIcon=contextObj.mayAddReference(field.name);
atMostOneRef=(field.multiplicity[1] == 1) and \
(len(objs)&lt;=1);
addConfirmMsg=appyType['addConfirm'] and \
_('%s_addConfirm' % appyType['labelId']) or '';
addConfirmMsg=field.addConfirm and \
_('%s_addConfirm' % field.labelId) or '';
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(contextObj.absolute_url()), \
q(fieldName), q(innerRef));
changeOrder=contextObj.callField(fieldName, \
'changeOrderEnabled', contextObj);
q(field.name), q(innerRef));
changeOrder=field.changeOrderEnabled(contextObj);
showSubTitles=req.get('showSubTitles', 'true') == 'true'"
id=":ajaxHookId">
@ -178,12 +173,12 @@ class Ref(Field):
<!-- If there is no object -->
<x if="not objs">
<td class="discreet">:_('no_ref')</td>
<td>:widget['pxAdd']</td>
<td>:field.pxAdd</td>
</x>
<!-- If there is an object... -->
<x if="objs">
<x for="obj in objs">
<td var="includeShownInfo=True">:widget['pxObjectTitle']</td>
<td var="includeShownInfo=True">:field.pxObjectTitle</td>
</x>
</x>
</tr>
@ -194,15 +189,15 @@ class Ref(Field):
<x if="not atMostOneRef">
<div if="not innerRef or showPlusIcon" style="margin-bottom: 4px">
(<x>:totalNumber</x>)
<x>:widget['pxAdd']</x>
<x>:field.pxAdd</x>
<!-- The search button if field is queryable -->
<input if="objs and appyType['queryable']" type="button" class="button"
<input if="objs and field.queryable" type="button" class="button"
style=":'background-image: url(%s/ui/buttonSearch.png)' % appUrl"
value=":_('search_title')"
onclick=":'window.location=%s' % \
q('%s/ui/search?className=%s&amp;ref=%s:%s' % \
(ztool.absolute_url(), linkedPortalType, contextObj.UID(), \
appyType['name']))"/>
field.name))"/>
</div>
<!-- Appy (top) navigation -->
@ -216,17 +211,15 @@ class Ref(Field):
<tr valign="bottom">
<td>
<!-- Show forward or backward reference(s) -->
<table class="not innerRef and 'list' or '';
width=innerRef and '100%' or \
appyType['layouts']['view']['width']"
var="columns=objs[0].getColumnsSpecifiers(\
appyType['shownInfo'], dir)">
<tr if="appyType['showHeaders']">
<table class="not innerRef and 'list' or ''"
width=":innerRef and '100%' or field.layouts['view']['width']"
var="columns=objs[0].getColumnsSpecifiers(field.shownInfo, dir)">
<tr if="field.showHeaders">
<th for="column in columns" width=":column['width']"
align="column['align']">
<x var="widget=column['field']">
<span>_(widget['labelId'])</span>
<x>:widget['pxSortIcons']</x>
<x var="field=column['field']">
<span>_(field.labelId)</span>
<x>:field.pxSortIcons</x>
<x var="className=linkedPortalType">:contextObj.appy(\
).pxShowDetails</x>
</x>
@ -237,18 +230,18 @@ class Ref(Field):
class=":odd and 'even' or 'odd'">
<td for="column in columns"
width=":column['width']" align=":column['align']">
<x var="widget=column['field']">
<x var="field=column['field']">
<!-- The "title" field -->
<x if="python: widget['name'] == 'title'">
<x>:widget['pxObjectTitle']</x>
<div if="obj.mayAct()">:widget['pxObjectActions']</div>
<x if="python: field.name == 'title'">
<x>:field.pxObjectTitle</x>
<div if="obj.mayAct()">:field.pxObjectActions</div>
</x>
<!-- Any other field -->
<x if="widget['name'] != 'title'">
<x if="field.name != 'title'">
<x var="contextObj=obj;
layoutType='cell';
innerRef=True"
if="obj.showField(widget['name'], layoutType='result')">
if="obj.showField(field.name, layoutType='result')">
<!-- use-macro="app/ui/widgets/show/macros/field"/-->
</x>
</x>
@ -267,37 +260,35 @@ class Ref(Field):
</div>''')
pxView = pxCell = Px('''
<x var="x=req.set('fieldName', widget['name'])">:widget['pxViewContent']
</x>''')
<x var="x=req.set('fieldName', field.name)">:field.pxViewContent</x>''')
pxEdit = Px('''
<x if="widget['link']"
<x if="field.link"
var="requestValue=req.get(name, []);
inRequest=req.has_key(name);
allObjects=contextObj.getSelectableAppyRefs(name);
refUids=[o.UID() for o in contextObj.getAppyRefs(name)['objects']];
allObjects=field.getSelectableObjects();
uids=[o.UID() for o in field.getLinkedObjects(contextObj).objects];
isBeingCreated=contextObj.isTemporary()">
<select name=":name" size="isMultiple and widget['height'] or ''"
<select name=":name" size="isMultiple and field.height or ''"
multiple="isMultiple and 'multiple' or ''">
<option value="" if="not isMultiple">:_('choose_a_value')"></option>
<x for="refObj in allObjects">
<option var="uid=contextObj.getReferenceUid(refObj)"
<option var="uid=refObj.o.UID()"
selected=":inRequest and (uid in requestValue) or \
(uid in refUids)"
value=":uid">:contextObj.getReferenceLabel(name, refObj)
</option>
(uid in uids)"
value=":uid">:field.getReferenceLabel(refObj)</option>
</x>
</select>
</x>''')
pxSearch = Px('''
<x>
<label lfor=":widgetName">:_(widget['labelId'])"></label><br/>&nbsp;&nbsp;
<label lfor=":widgetName">:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<!-- The "and" / "or" radio buttons -->
<x var="operName='o_%s' % name;
orName='%s_or' % operName;
andName='%s_and' % operName"
if="widget['multiplicity'][1] != 1">
if="field.multiplicity[1] != 1">
<input type="radio" name=":operName" id=":orName"
checked="checked" value="or"/>
<label lfor=":orName">:_('search_or')"></label>
@ -305,12 +296,12 @@ class Ref(Field):
<label lfor=":andName">:_('search_and')"></label><br/>
</x>
<!-- The list of values -->
<select name=":widgetName" size="widget['sheight']" multiple="multiple">
<select name=":widgetName" size=":field.sheight" multiple="multiple">
<x for="v in ztool.getSearchValues(name, className)">
<option var="uid=v[0];
title=ztool.getReferenceLabel(name, v[1], className)"
title=field.getReferenceLabel(v[1])"
value=":uid"
title=":title">:ztool.truncateValue(title, widget['swidth'])">
title=":title">:ztool.truncateValue(title, field.swidth)">
</option>
</x>
</select>
@ -482,6 +473,14 @@ class Ref(Field):
if someObjects: return res
return res.objects
def getLinkedObjects(self, obj, startNumber=None):
'''Gets the objects linked to p_obj via this Ref field. If p_startNumber
is None, all linked objects are returned. If p_startNumber is a
number, self.maxPerPage objects will be returned, starting at
p_startNumber.'''
return self.getValue(obj, type='zobjects', someObjects=True,
startNumber=startNumber)
def getFormattedValue(self, obj, value, showChanges=False):
return value
@ -650,6 +649,47 @@ class Ref(Field):
else:
return self.callMethod(obj, self.changeOrder)
def getSelectableObjects(self, contextObj):
'''This method returns the list of all objects that can be selected to
be linked as references to p_contextObj via p_self.'''
if not self.select:
# No select method has been defined: we must retrieve all objects
# of the referred type that the user is allowed to access.
return contextObj.appy().search(self.klass)
else:
return self.select(contextObj.appy())
xhtmlToText = re.compile('<.*?>', re.S)
def getReferenceLabel(self, refObject):
'''p_self must have link=True. I need to display, on an edit view, the
p_refObject in the listbox that will allow the user to choose which
object(s) to link through the Ref. The information to display may
only be the object title or more if self.shownInfo is used.'''
res = ''
for fieldName in self.shownInfo:
refType = refObject.o.getAppyType(fieldName)
value = getattr(refObject, fieldName)
value = refType.getFormattedValue(refObject.o, value)
if refType.type == 'String':
if refType.format == 2:
value = self.xhtmlToText.sub(' ', value)
elif type(value) in sequenceTypes:
value = ', '.join(value)
prefix = ''
if res:
prefix = ' | '
res += prefix + value
maxWidth = self.width or 30
if len(res) > maxWidth:
res = res[:maxWidth-2] + '...'
return res
def getIndexOf(self, obj, refObj):
'''Gets the position of p_refObj within this field on p_obj.'''
uids = getattr(obj.aq_base, self.name, None)
if not uids: raise IndexError()
return uids.index(refObj.UID())
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

@ -20,6 +20,7 @@ import re, random
from appy.gen.layout import Table
from appy.gen.indexer import XhtmlTextExtractor
from appy.fields import Field
from appy.px import Px
from appy.shared.data import countries
from appy.shared.xml_parser import XhtmlCleaner
from appy.shared.diff import HtmlDiff
@ -71,6 +72,118 @@ class String(Field):
URL = c('(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})?' \
'(([0-9]{1,5})?\/.*)?')
pxView = Px('''
<x var="fmt=field.format; isUrl=field.isUrl;
mayAjaxEdit=not showChanges and field.inlineEdit and \
contextObj.mayEdit(field.writePermission)">
<x if="fmt in (0, 3)">
<ul if="value and isMultiple">
<li for="sv in value"><i>::sv</i></li>
</ul>
<x if="value and not isMultiple">
<!-- A password -->
<x if="fmt == 3">********</x>
<!-- A URL -->
<a if="(fmt != 3) and isUrl" target="_blank" href=":value">:value</a>
<!-- Any other value -->
<x if="(fmt != 3) and not isUrl">::value</x>
</x>
</x>
<!-- Unformatted text -->
<x if="value and (fmt == 1)">::contextObj.formatText(value, format='html')
</x>
<!-- XHTML text -->
<x if="value and (fmt == 2)">
<div if="not mayAjaxEdit" class="xhtml">::value</div>
<div if="mayAjaxEdit" class="xhtml" contenteditable="true"
id=":'%s_%s_ck' % (contextObj.UID(), name)">::value</div>
<script if="mayAjaxEdit">:field.getJsInlineInit(contextObj)"></script>
</x>
<input type="hidden" if="masterCss" class=":masterCss" value=":rawValue"
name=":name" id=":name"/>
</x>''')
pxEdit = Px('''
<x var="fmt=field.format;
isSelect=field.isSelect;
isMaster=field.slaves;
isOneLine=fmt in (0,3,4)">
<x if="isSelect">
<select var="possibleValues=field.getPossibleValues(contextObj, \
withTranslations=True, withBlankValue=True)"
name=":name" id=":name" class=":masterCss"
multiple=":isMultiple and 'multiple' or ''"
onchange=":isMaster and 'updateSlaves(this)' or ''"
size=":isMultiple and field.height or 1">
<option for="val in possibleValues" value=":val[0]"
selected=":field.isSelected(contextObj, val[0], rawValue)"
title=":val[1]">:ztool.truncateValue(val[1], field.width)">
</option>
</select>
</x>
<x if="isOneLine and not isSelect">
<input id=":name" name=":name" size=":field.width"
maxlength=":field.maxChars"
value=":inRequest and requestValue or value"
style=":'text-transform:%s' % field.transform"
type=":(fmt == 3) and 'password' or 'text'"/>
<!-- Display a captcha if required -->
<span if="fmt == 4">:_('captcha_text', \
mapping=field.getCaptchaChallenge(req.SESSION))
</span>
</x>
<x if="fmt in (1,2)">
<textarea id=":name" name=":name" cols=":field.width"
class=":(fmt == 2) and ('rich_%s' % name) or ''"
style=":'text-transform:%s' % field.transform"
rows=":field.height">:inRequest and requestValue or value
</textarea>
<script if="fmt == 2"
type="text/javascript">:field.getJsInit(contextObj)</script>
</x>
</x>''')
pxCell = Px('''
<x var="multipleValues=value and isMultiple">
<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;
<!-- Show a simple search field for most String fields -->
<x if="not field.isSelect">
<input type="text" maxlength=":field.maxChars" size=":field.swidth"
name=":'%s*string-%s' % (widgetName, field.transform)"
style=":'text-transform:%s' % field.transform"
value=":field.sdefault"/>
</x>
<!-- Show a multi-selection box for fields whose validator defines a list
of values, with a "AND/OR" checkbox. -->
<x if="field.isSelect">
<!-- The "and" / "or" radio buttons -->
<x var="operName='o_%s' % name;
orName='%s_or' % operName;
andName='%s_and' % operName"
if="field.multiplicity[1] != 1">
<input type="radio" name=":operName" id=":orName" checked="checked"
value="or"/>
<label lfor=":orName">:_('search_or')</label>
<input type="radio" name=":operName" id=":andName" value="and"/>
<label lfor=":andName">:_('search_and')"></label><br/>
</x>
<!-- The list of values -->
<select var="preSelected=field.sdefault"
name=":widgetName" size=":field.sheight" multiple="multiple">
<option for="v in field.getPossibleValues(ztool, withTranslations=True,\
withBlankValue=False, className=className)"
selected=":v[0] in preSelected" value=":v[0]"
title=":v[1]">ztool.truncateValue(v[1], field.swidth)</option>
</select>
</x><br/>
</x>''')
# Some predefined functions that may also be used as validators
@staticmethod
def _MODULO_97(obj, value, complement=False):
@ -352,14 +465,16 @@ class String(Field):
if res in self.emptyValuesCatalogIgnored: res = ' '
return res
def getPossibleValues(self,obj,withTranslations=False,withBlankValue=False):
'''Returns the list of possible values for this field if it is a
selection field. If p_withTranslations is True,
instead of returning a list of string values, the result is a list
of tuples (s_value, s_translation). If p_withBlankValue is True, a
blank value is prepended to the list, excepted if the type is
multivalued.'''
if not self.isSelect: raise 'This field is not a selection.'
def getPossibleValues(self, obj, withTranslations=False,
withBlankValue=False, className=None):
'''Returns the list of possible values for this field (only for fields
with self.isSelect=True). If p_withTranslations is True, instead of
returning a list of string values, the result is a list of tuples
(s_value, s_translation). If p_withBlankValue is True, a blank value
is prepended to the list, excepted if the type is multivalued. If
p_className is given, p_obj is the tool and, if we need an instance
of p_className, we will need to use obj.executeQuery to find one.'''
if not self.isSelect: raise Exception('This field is not a selection.')
if isinstance(self.validator, Selection):
# We need to call self.methodName for getting the (dynamic) values.
# If methodName begins with _appy_, it is a special Appy method:
@ -381,6 +496,15 @@ class String(Field):
if methodName.startswith('tool:'):
obj = obj.getTool()
methodName = methodName[5:]
else:
# We must call on p_obj. But if we have something in
# p_className, p_obj is the tool and not an instance of
# p_className as required. So find such an instance.
if className:
brains = obj.executeQuery(className, maxResults=1,
brainsOnly=True)
if brains:
obj = brains[0].getObject()
# Do we need to call the method on the object or on the wrapper?
if methodName.startswith('_appy_'):
exec 'res = obj.%s(*args)' % methodName
@ -503,4 +627,47 @@ class String(Field):
'''Generates a password (we recycle here the captcha challenge
generator).'''
return self.getCaptchaChallenge({})['text']
def getJsInit(self, obj):
'''Gets the Javascript init code for displaying a rich editor for this
field (rich field only).'''
# Define the attributes that will initialize the ckeditor instance for
# this field.
ckAttrs = {'toolbar': 'Appy',
'format_tags': '%s' % ';'.join(self.styles)}
if self.width: ckAttrs['width'] = self.width
if self.allowImageUpload:
ckAttrs['filebrowserUploadUrl'] = '%s/upload' % obj.absolute_url()
ck = []
for k, v in ckAttrs.iteritems():
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))
def getJsInlineInit(self, obj):
'''Gets the Javascript init code for enabling inline edition of this
field (rich text only).'''
uid = obj.UID()
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)}, "\
"null, evalInnerScripts);}}});"% \
(uid, self.name, uid, self.name, obj.absolute_url(), self.name)
def isSelected(self, obj, vocabValue, dbValue):
'''When displaying a selection box (only for fields with a validator
being a list), must the _vocabValue appear as selected?'''
rq = obj.REQUEST
# Get the value we must compare (from request or from database)
if rq.has_key(self.name):
compValue = rq.get(self.name)
else:
compValue = dbValue
# Compare the value
if type(compValue) in sutils.sequenceTypes:
return vocabValue in compValue
return vocabValue == compValue
# ------------------------------------------------------------------------------

View file

@ -1393,24 +1393,10 @@ class BaseMixin:
def getPossibleValues(self, name, withTranslations, withBlankValue,
className=None):
'''Gets the possible values for field named p_name. This field must be a
String with isSelection()=True. If p_withTranslations is True,
instead of returning a list of string values, the result is a list
of tuples (s_value, s_translation). If p_withBlankValue is True, a
blank value is prepended to the list. If no p_className is defined,
the field is supposed to belong to self's class.'''
appyType = self.getAppyType(name, className=className)
if className:
# We need an instance of className, but self can be an instance of
# another class. So here we will search such an instance.
brains = self.executeQuery(className, maxResults=1, brainsOnly=True)
if brains:
obj = brains[0].getObject()
else:
obj = self
else:
obj = self
return appyType.getPossibleValues(obj, withTranslations, withBlankValue)
'''See docstring of String.getPossibleValues.'''
field = self.getAppyType(name, className=className)
return field.getPossibleValues(self, withTranslations, withBlankValue,
className=className)
def getCaptchaChallenge(self, name):
return self.getAppyType(name).getCaptchaChallenge(self.REQUEST.SESSION)