[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 # represents the maximum number of chars that a given input field may
# accept (corresponds to HTML "maxlength" property). "None" means # accept (corresponds to HTML "maxlength" property). "None" means
# "unlimited". # "unlimited".
self.maxChars = maxChars self.maxChars = maxChars or ''
# If the widget is in a group with multiple columns, the following # If the widget is in a group with multiple columns, the following
# attribute specifies on how many columns to span the widget. # attribute specifies on how many columns to span the widget.
self.colspan = colspan self.colspan = colspan

View file

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

View file

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

View file

@ -24,15 +24,15 @@ class Computed(Field):
# Ajax-called view content of a non sync Computed field. # Ajax-called view content of a non sync Computed field.
pxViewContent = Px(''' pxViewContent = Px('''
<x var="name=req['fieldName']; <x var="name=req['fieldName'];
widget=contextObj.getAppyType(name, asDict=True); field=contextObj.getAppyType(name);
value=contextObj.getFieldValue(name); value=contextObj.getFieldValue(name);
sync=True">:widget['pxView']</x>''') sync=True">:field.pxView</x>''')
pxView = pxCell = pxEdit = Px(''' pxView = pxCell = pxEdit = Px('''
<x> <x>
<x if="sync"> <x if="sync">
<x if="widget['plainText']">:value</x> <x if="field.plainText">:value</x>
<x if="not widget['plainText']">::value></x> <x if="not field.plainText">::value></x>
</x> </x>
<x if="not sync"> <x if="not sync">
<div var="ajaxHookId=contextObj.UID() + name" id="ajaxHookId"> <div var="ajaxHookId=contextObj.UID() + name" id="ajaxHookId">
@ -45,12 +45,9 @@ class Computed(Field):
pxSearch = Px(''' pxSearch = Px('''
<x> <x>
<label lfor=":widgetName">:_(widget['labelId'])"></label><br/>&nbsp;&nbsp; <label lfor=":name">:field.labelId</label><br/>&nbsp;&nbsp;
<input type="text" <input type="text" name=":'%s*string' % name" maxlength=":field.maxChars"
var="maxChars=widget['maxChars'] and widget['maxChars'] or ''" size=":field.width" value=":field.sdefault"/>
name=":'%s*string' % widgetName"
maxlength=":maxChars" size=":widget['width']"
value=":widget['sdefault']"/>
</x>''') </x>''')
def __init__(self, validator=None, multiplicity=(0,1), default=None, 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>''') pxView = pxCell = Px('''<x>:value</x>''')
pxEdit = Px(''' pxEdit = Px('''
<x var="years=contextObj.getSelectableYears(widget['name'])"> <x var="years=field.getSelectableYears()">
<!-- Day --> <!-- Day -->
<select var="days=range(1,32)" <select var="days=range(1,32)"
name=":'%s_day' % name" id=":'%s_day' % name"> name=":'%s_day' % name" id=":'%s_day' % name">
<option value="">-</option> <option value="">-</option>
<x for="day in days"> <x for="day in days">
<option var="zDay=str(day).zfill(2)" value=":zDay" <option var="zDay=str(day).zfill(2)" value=":zDay"
selected="contextObj.dateValueSelected(name, 'day', day, \ selected="field.isSelected(contextObj, 'day', day, \
rawValue)">:zDay></option></x> rawValue)">:zDay</option></x>
</select> </select>
<!-- Month --> <!-- Month -->
@ -41,7 +41,7 @@ class Date(Field):
<option value="">-</option> <option value="">-</option>
<x for="month in months"> <x for="month in months">
<option var="zMonth=str(month).zfill(2)" value=":zMonth" <option var="zMonth=str(month).zfill(2)" value=":zMonth"
selected="contextObj.dateValueSelected(name, 'month', month, \ selected="field.isSelected(contextObj, 'month', month, \
rawValue)">:zMonth</option></x> rawValue)">:zMonth</option></x>
</select> </select>
@ -49,42 +49,42 @@ class Date(Field):
<select name=":'%s_year' % name" id=":'%s_year' % name"> <select name=":'%s_year' % name" id=":'%s_year' % name">
<option value="">-</option> <option value="">-</option>
<option for="year in years" value=":year" <option for="year in years" value=":year"
selected="contextObj.dateValueSelected(name, 'year', year, \ selected="field.isSelected(contextObj, name, 'year', year, \
rawValue)">:year</option> rawValue)">:year</option>
</select> </select>
<!-- The icon for displaying the calendar popup --> <!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']"> <x if="field.calendar">
<input type="hidden" id=":name" name=":name"/> <input type="hidden" id=":name" name=":name"/>
<img id=":'%s_img' % name" src=":'%s/ui/calendar.gif' % appUrl"/> <img id=":'%s_img' % name" src=":'%s/ui/calendar.gif' % appUrl"/>
<script type="text/javascript">:contextObj.getCalendarInit(name, years) <script type="text/javascript">:field.getJsInit(name, years)</script>
</script>
</x> </x>
<!-- Hour and minutes --> <!-- Hour and minutes -->
<x if="widget['format'] == 0"> <x if="field.format == 0">
<select var="hours=range(0,24)" <select var="hours=range(0,24)" name=":'%s_hour' % name"
name=":'%s_hour' % name" id=":'%s_hour' % name"> id=":'%s_hour' % name">
<option value="">-</option> <option value="">-</option>
<x for="hour in hours"> <x for="hour in hours">
<option var="zHour=str(hour).zfill(2)" value=":zHour" <option var="zHour=str(hour).zfill(2)" value=":zHour"
selected=":contextObj.dateValueSelected(name, 'hour', hour, \ selected=":field.isSelected(contextObj, 'hour', hour, \
rawValue)">:zHour</option></x> rawValue)">:zHour</option>
</x>
</select> : </select> :
<select var="minutes=range(0,60,5)" <select var="minutes=range(0,60,5)" name=":'%s_minute' % name"
name=":'%s_minute' % name" id=":'%s_minute' % name"> id=":'%s_minute' % name">
<option value="">-</option> <option value="">-</option>
<x for="minute in minutes"> <x for="minute in minutes">
<option var="zMinute=str(minute).zfill(2)" value=":zMinute" <option var="zMinute=str(minute).zfill(2)" value=":zMinute"
selected=":contextObj.dateValueSelected(name, 'minute', \ selected=":field.isSelected(contextObj, 'minute', minute,\
minute, rawValue)">:zMinute</option></x> rawValue)">:zMinute</option></x>
</select> </select>
</x> </x>
</x>''') </x>''')
pxSearch = Px(''' pxSearch = Px('''
<x var="years=range(widget['startYear'], widget['endYear']+1)"> <x var="years=range(field.startYear, field.endYear+1)">
<label>:_(widget['labelId'])</label> <label>:_(field.labelId)</label>
<table> <table>
<!-- From --> <!-- From -->
<tr var="fromName='%s_from' % name; <tr var="fromName='%s_from' % name;
@ -106,14 +106,14 @@ class Date(Field):
</select> / </select> /
<select id=":yearFromName" name=":yearFromName"> <select id=":yearFromName" name=":yearFromName">
<option value="">--</option> <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> value=":value">:value</option>
</select> </select>
<!-- The icon for displaying the calendar popup --> <!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']"> <x if="field.calendar">
<input type="hidden" id=":fromName" name=":fromName"/> <input type="hidden" id=":fromName" name=":fromName"/>
<img id=":'%s_img' % fromName" src=":'%s/ui/calendar.gif' % appUrl"/> <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> </script>
</x> </x>
</td> </td>
@ -139,14 +139,14 @@ class Date(Field):
</select> / </select> /
<select id=":yearToName" name=":yearToName"> <select id=":yearToName" name=":yearToName">
<option value="">--</option> <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> value=":value">:value</option>
</select> </select>
<!-- The icon for displaying the calendar popup --> <!-- The icon for displaying the calendar popup -->
<x if="widget['calendar']"> <x if="widget.calendar">
<input type="hidden" id=":toName" name=":toName"/> <input type="hidden" id=":toName" name=":toName"/>
<img id=":'%s_img' % toName" src=":%s/ui/calendar.gif' % appUrl"/> <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> </script>
</x> </x>
</td> </td>
@ -248,4 +248,30 @@ class Date(Field):
return DateTime.DateTime(value) return DateTime.DateTime(value)
def getIndexType(self): return 'DateIndex' 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 import time, os.path, mimetypes
from appy import Object
from appy.fields import Field from appy.fields import Field
from appy.px import Px from appy.px import Px
from appy.shared import utils as sutils from appy.shared import utils as sutils
@ -24,32 +25,32 @@ from appy.shared import utils as sutils
class File(Field): class File(Field):
pxView = pxCell = Px(''' pxView = pxCell = Px('''
<x var="info=contextObj.getFileInfo(value); <x var="info=field.getFileInfo(value);
empty=not info['size']; empty=not info.size;
imgSrc='%s/download?name=%s' % (contextObj.absolute_url(), name)"> imgSrc='%s/download?name=%s' % (contextObj.absolute_url(), name)">
<x if="not empty and not widget['isImage']"> <x if="not empty and not field.isImage">
<a href=":imgSrc">:info['filename']"</a>&nbsp;&nbsp;- <a href=":imgSrc">:info.filename</a>&nbsp;&nbsp;-
<i class="discreet">'%sKb' % (info['size'] / 1024)"></i> <i class="discreet">'%sKb' % (info.size / 1024)"></i>
</x> </x>
<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 if="empty">-</x>
</x>''') </x>''')
pxEdit = Px(''' pxEdit = Px('''
<x var="info=contextObj.getFileInfo(value); <x var="info=field.getFileInfo(value);
empty= not info['size']; empty= not info.size;
fName=q('%s_file' % name)"> 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"> <x if="not empty">
<!-- Keep the file unchanged. --> <!-- Keep the file unchanged. -->
<input type="radio" value="nochange" <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" name=":'%s_delete' % name" id=":'%s_nochange' % name"
onclick=":'document.getElementById(%s).disabled=true' % fName"/> onclick=":'document.getElementById(%s).disabled=true' % fName"/>
<label lfor=":'%s_nochange' % name">Keep the file unchanged</label><br/> <label lfor=":'%s_nochange' % name">Keep the file unchanged</label><br/>
<!-- Delete the file. --> <!-- Delete the file. -->
<x if="not widget['required']"> <x if="not field.required">
<input type="radio" value="delete" <input type="radio" value="delete"
name=":'%s_delete' % name" id=":'%s_delete' % name" name=":'%s_delete' % name" id=":'%s_delete' % name"
onclick=":'document.getElementById(%s).disabled=true' % fName"/> onclick=":'document.getElementById(%s).disabled=true' % fName"/>
@ -57,14 +58,14 @@ class File(Field):
</x> </x>
<!-- Replace with a new file. --> <!-- Replace with a new file. -->
<input type="radio" value="" <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" name=":'%s_delete' % name" id=":'%s_upload' % name"
onclick=":'document.getElementById(%s).disabled=false' % fName"/> onclick=":'document.getElementById(%s).disabled=false' % fName"/>
<label lfor=":'%s_upload' % name">Replace it with a new file</label><br/> <label lfor=":'%s_upload' % name">Replace it with a new file</label><br/>
</x> </x>
<!-- The upload field. --> <!-- The upload field. -->
<input type="file" name=":'%s_file' % name" id=":'%s_file' % name" <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'" <script var="isDisabled=empty and 'false' or 'true'"
type="text/javascript">:document.getElementById(%s).disabled=%s'%\ type="text/javascript">:document.getElementById(%s).disabled=%s'%\
(q(fName), q(isDisabled))"> (q(fName), q(isDisabled))">
@ -223,4 +224,9 @@ class File(Field):
if rq: action = rq.get('%s_delete' % self.name, None) if rq: action = rq.get('%s_delete' % self.name, None)
if action == 'nochange': pass if action == 'nochange': pass
else: setattr(obj, self.name, None) 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>''') </x>''')
pxEdit = Px(''' pxEdit = Px('''
<input var="maxChars=widget['maxChars'] and widget['maxChars'] or ''" <input id=":name" name=":name" size=":field.width"
id=":name" name=":name" size=":widget['width']" maxlength=":maxChars" maxlength=":field.maxChars"
value=":inRequest and requestValue or value" type="text"/>''') value=":inRequest and requestValue or value" type="text"/>''')
pxSearch = Px(''' pxSearch = Px('''
<x var="maxChars=widget['maxChars'] and widget['maxChars'] or ''"> <x>
<label>:_(widget['labelId'])"></label><br/>&nbsp;&nbsp; <label>:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<!-- From --> <!-- From -->
<x var="fromName='%s*float' % widgetName"> <x var="fromName='%s*float' % widgetName">
<label lfor=":fromName">:_('search_from')"></label> <label lfor=":fromName">:_('search_from')"></label>
<input type="text" name=":fromName" maxlength=":maxChars" <input type="text" name=":fromName" maxlength=":field.maxChars"
value=":widget['sdefault'][0]" size=":widget['swidth]"/> value=":field.sdefault[0]" size=":field.swidth"/>
</x> </x>
<!-- To --> <!-- To -->
<x var="toName='%s_to' % name"> <x var="toName='%s_to' % name">
<label lfor=":toName">:_('search_to')</label> <label lfor=":toName">:_('search_to')</label>
<input type="text" name=":toName" maxlength=":maxChars" <input type="text" name=":toName" maxlength=":field.maxChars"
value=":widget['sdefault'][1]" size="widget['swidth']"/> value=":field.sdefault[1]" size="field.swidth"/>
</x><br/> </x><br/>
</x>''') </x>''')

View file

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

View file

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

View file

@ -33,7 +33,6 @@ class Ogone(Field):
pxView = pxCell = Px(''' pxView = pxCell = Px('''
<x> <x>
<!-- var "value" is misused and contains the contact params for Ogone --> <!-- var "value" is misused and contains the contact params for Ogone -->
<p>:value</p>
<!-- The form for sending the payment request to Ogone --> <!-- The form for sending the payment request to Ogone -->
<form method="post" id="form1" name="form1" var="env=value['env']" <form method="post" id="form1" name="form1" var="env=value['env']"
action=":'https://secure.ogone.com/ncol/%s/orderstandard.asp'% 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): class Pod(Field):
'''A pod is a field allowing to produce a (PDF, ODT, Word, RTF...) document '''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 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. # Layout for rendering a POD field for exporting query results.
rLayouts = {'view': Table('fl', width=None)} rLayouts = {'view': Table('fl', width=None)}
POD_ERROR = 'An error occurred while generating the document. Please ' \ POD_ERROR = 'An error occurred while generating the document. Please ' \
@ -38,14 +38,14 @@ class Pod(Field):
pxView = pxCell = Px(''' pxView = pxCell = Px('''
<x> <x>
<!-- Ask action --> <!-- Ask action -->
<x if="widget['askAction']"> <x if="field.askAction">
<x var="doLabel='%s_askaction' % widget['labelId']; <x var="doLabel='%s_askaction' % field.labelId;
chekboxId='%s_%s_cb' % (contextObj.UID(), name)"> chekboxId='%s_%s_cb' % (contextObj.UID(), name)">
<input type="checkbox" name=":doLabel" id=":chekboxId"/> <input type="checkbox" name=":doLabel" id=":chekboxId"/>
<label lfor=":chekboxId" class="discreet">:_(doLabel)"></label> <label lfor=":chekboxId" class="discreet">:_(doLabel)"></label>
</x> </x>
</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)" src=":'%s/ui/%s.png' % (appUrl, podFormat)"
onclick=":'generatePodDocument(%s, %s, %s, %s)' % \ onclick=":'generatePodDocument(%s, %s, %s, %s)' % \
(q(contextObj.UID()), q(name), q(podFormat), \ (q(contextObj.UID()), q(name), q(podFormat), \

View file

@ -15,7 +15,7 @@
# Appy. If not, see <http://www.gnu.org/licenses/>. # Appy. If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import sys import sys, re
from appy.fields import Field, No from appy.fields import Field, No
from appy.px import Px from appy.px import Px
from appy.gen.layout import Table 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 # the URL for allowing to navigate from one object to the next/previous on
# ui/view. # ui/view.
pxObjectTitle = Px(''' pxObjectTitle = Px('''
<x var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, \ <x var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), field.name, \
appyType['pageName'], loop.obj.nb + startNumber, totalNumber); field.pageName, loop.obj.nb + startNumber, totalNumber);
navInfo=not appyType['isBack'] and navInfo or ''; navInfo=not field.isBack and navInfo or '';
cssClass=obj.getCssFor('title')"> cssClass=obj.getCssFor('title')">
<x>::obj.getSupTitle(navInfo)</x> <x>::obj.getSupTitle(navInfo)</x>
<a var="pageName=appyType['isBack'] and appyType['backd']['pageName'] or \ <a var="pageName=field.isBack and field.back.pageName or 'main';
'main';
fullUrl=obj.getUrl(page=pageName, nav=navInfo)" fullUrl=obj.getUrl(page=pageName, nav=navInfo)"
href=":fullUrl" class=":cssClass">:(not includeShownInfo) and \ 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 \ </a><span name="subTitle" style=":showSubTitles and 'display:inline' or \
'display:none'">::obj.getSubTitle()"</span> 'display:none'">::obj.getSubTitle()"</span>
</x>''') </x>''')
@ -54,16 +53,16 @@ class Ref(Field):
# This PX displays icons for triggering actions on a given referenced object # This PX displays icons for triggering actions on a given referenced object
# (edit, delete, etc). # (edit, delete, etc).
pxObjectActions = Px(''' pxObjectActions = Px('''
<table class="noStyle" var="isBack=appyType['isBack']"> <table class="noStyle" var="isBack=field.isBack">
<tr> <tr>
<!-- Arrows for moving objects up or down --> <!-- Arrows for moving objects up or down -->
<td if=":not isBack and (len(objs)&gt;1) and changeOrder and canWrite"> <td if="not isBack and (len(objs)&gt;1) and changeOrder and canWrite">
<x var="objectIndex=contextObj.getAppyRefIndex(fieldName, obj); <x var="objectIndex=field.getIndexOf(contextObj, obj);
ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\ ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
(q(startNumber), q('ChangeRefOrder'), q('refObjectUid'), (q(startNumber), q('ChangeRefOrder'), q('refObjectUid'),
q(obj.UID()), q('move'), q('**v**')))"> q(obj.UID()), q('move'), q('**v**')))">
<img if="objectIndex &gt; 0" style="cursor:pointer" <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 onclick=":ajaxBaseCall.replace('**v**', 'up')"/><img
style="cursor:pointer" if="objectIndex &lt; (totalNumber-1)" style="cursor:pointer" if="objectIndex &lt; (totalNumber-1)"
src=":'%s/ui/arrowDown.png' % appUrl" title=":_('move_down')" src=":'%s/ui/arrowDown.png' % appUrl" title=":_('move_down')"
@ -75,27 +74,25 @@ class Ref(Field):
<x var="targetObj=obj">:targetObj.appy().pxTransitions</x> <x var="targetObj=obj">:targetObj.appy().pxTransitions</x>
</td> </td>
<!-- Edit --> <!-- Edit -->
<td if="not appyType['noForm'] and obj.mayEdit() and appyType['delete']"> <td if="not field.noForm and obj.mayEdit() and field.delete">
<a var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), fieldName, \ <a var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), field.name, \
appyType['pageName'], loop.obj.nb + startNumber, \ field.pageName, loop.obj.nb + startNumber, totalNumber)"
totalNumber)"
href=":obj.getUrl(mode='edit', page='main', nav=navInfo)"> href=":obj.getUrl(mode='edit', page='main', nav=navInfo)">
<img src=":'%s/ui/edit.png' % appUrl" title=":_('object_edit')"/> <img src=":'%s/ui/edit.png' % appUrl" title=":_('object_edit')"/>
</a> </a>
</td> </td>
<!-- Delete --> <!-- Delete -->
<td if="not isBack and appyType['delete'] and canWrite and \ <td if="not isBack and field.delete and canWrite and obj.mayDelete()">
obj.mayDelete()">
<img style="cursor:pointer" title=":_('object_delete')" <img style="cursor:pointer" title=":_('object_delete')"
src=":'%s/ui/delete.png' % appUrl" src=":'%s/ui/delete.png' % appUrl"
onclick=":'onDeleteObject(%s)' % q(obj.UID())"/> onclick=":'onDeleteObject(%s)' % q(obj.UID())"/>
</td> </td>
<!-- Unlink --> <!-- 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')" <img style="cursor:pointer" title=":_('object_unlink')"
src=":'%s/ui/unlink.png' % appUrl" src=":'%s/ui/unlink.png' % appUrl"
onclick=":'onUnlinkObject(%s,%s,%s)' % (q(contextObj.UID()), \ onclick=":'onUnlinkObject(%s,%s,%s)' % (q(contextObj.UID()), \
q(appyType['name']), q(obj.UID()))"/> q(field.name), q(obj.UID()))"/>
</td> </td>
</tr> </tr>
</table>''') </table>''')
@ -106,21 +103,21 @@ class Ref(Field):
<x if="showPlusIcon"> <x if="showPlusIcon">
<input type="button" class="button" <input type="button" class="button"
var="navInfo='ref.%s.%s:%s.%d.%d' % (contextObj.UID(), \ 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' % \ formCall='window.location=%s' % \
q('%s/do?action=Create&amp;className=%s&amp;nav=%s' % \ q('%s/do?action=Create&amp;className=%s&amp;nav=%s' % \
(folder.absolute_url(), linkedPortalType, navInfo)); (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), \ 'askConfirm(%s,%s,%s)' % (q('script'), q(formCall), \
q(addConfirmMsg)); q(addConfirmMsg));
noFormCall=navBaseCall.replace('**v**', \ noFormCall=navBaseCall.replace('**v**', \
'%d,%s' % (startNumber, q('CreateWithoutForm'))); '%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), \ 'askConfirm(%s, %s, %s)' % (q('script'), q(noFormCall), \
q(addConfirmMsg))" q(addConfirmMsg))"
style=":'background-image: url(%s/ui/buttonAdd.png)' % appUrl" style=":'background-image: url(%s/ui/buttonAdd.png)' % appUrl"
value=":_('add_ref')" value=":_('add_ref')"
onclick=":appyType['noForm'] and noFormCall or formCall"/> onclick=":field.noForm and noFormCall or formCall"/>
</x>''') </x>''')
# This PX displays, in a cell header from a ref table, icons for sorting the # This PX displays, in a cell header from a ref table, icons for sorting the
@ -128,8 +125,8 @@ class Ref(Field):
pxSortIcons = Px(''' pxSortIcons = Px('''
<x var="ajaxBaseCall=navBaseCall.replace('**v**', '%s,%s,{%s:%s,%s:%s}' % \ <x var="ajaxBaseCall=navBaseCall.replace('**v**', '%s,%s,{%s:%s,%s:%s}' % \
(q(startNumber), q('SortReference'), q('sortKey'), \ (q(startNumber), q('SortReference'), q('sortKey'), \
q(widget['name']), q('reverse'), q('**v**')))" q(field.name), q('reverse'), q('**v**')))"
if="changeOrder and canWrite and ztool.isSortable(widget['name'], \ if="changeOrder and canWrite and ztool.isSortable(field.name, \
objs[0].meta_type, 'ref')"> objs[0].meta_type, 'ref')">
<img style="cursor:pointer" src=":'%s/ui/sortAsc.png' % appUrl" <img style="cursor:pointer" src=":'%s/ui/sortAsc.png' % appUrl"
onclick=":ajaxBaseCall.replace('**v**', 'False')"/> onclick=":ajaxBaseCall.replace('**v**', 'False')"/>
@ -140,29 +137,27 @@ class Ref(Field):
# This PX is called by a XmlHttpRequest (or directly by pxView) for # This PX is called by a XmlHttpRequest (or directly by pxView) for
# displaying the referred objects of a reference field. # displaying the referred objects of a reference field.
pxViewContent = Px(''' pxViewContent = Px('''
<div var="fieldName=req['fieldName']; <div var="field=contextObj.getAppyType(req['fieldName']);
appyType=contextObj.getAppyType(fieldName, asDict=True);
innerRef=req.get('innerRef', False) == 'True'; innerRef=req.get('innerRef', False) == 'True';
ajaxHookId=contextObj.UID() + fieldName; ajaxHookId=contextObj.UID() + field.name;
startNumber=int(req.get('%s_startNumber' % ajaxHookId, 0)); startNumber=int(req.get('%s_startNumber' % ajaxHookId, 0));
refObjects=contextObj.getAppyRefs(fieldName, startNumber); refObjects=field.getLinkedObjects(contextObj, startNumber);
objs=refObjects['objects']; objs=refObjects.objects;
totalNumber=refObjects['totalNumber']; totalNumber=refObjects.totalNumber;
batchSize=refObjects['batchSize']; batchSize=refObjects.batchSize;
folder=contextObj.getCreateFolder(); folder=contextObj.getCreateFolder();
linkedPortalType=ztool.getPortalType(appyType['klass']); linkedPortalType=ztool.getPortalType(field.klass);
canWrite=not appyType['isBack'] and \ canWrite=not field.isBack and \
contextObj.allows(appyType['writePermission']); contextObj.allows(field.writePermission);
showPlusIcon=contextObj.mayAddReference(fieldName); showPlusIcon=contextObj.mayAddReference(field.name);
atMostOneRef=(appyType['multiplicity'][1] == 1) and \ atMostOneRef=(field.multiplicity[1] == 1) and \
(len(objs)&lt;=1); (len(objs)&lt;=1);
addConfirmMsg=appyType['addConfirm'] and \ addConfirmMsg=field.addConfirm and \
_('%s_addConfirm' % appyType['labelId']) or ''; _('%s_addConfirm' % field.labelId) or '';
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \ navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(contextObj.absolute_url()), \ (q(ajaxHookId), q(contextObj.absolute_url()), \
q(fieldName), q(innerRef)); q(field.name), q(innerRef));
changeOrder=contextObj.callField(fieldName, \ changeOrder=field.changeOrderEnabled(contextObj);
'changeOrderEnabled', contextObj);
showSubTitles=req.get('showSubTitles', 'true') == 'true'" showSubTitles=req.get('showSubTitles', 'true') == 'true'"
id=":ajaxHookId"> id=":ajaxHookId">
@ -178,12 +173,12 @@ class Ref(Field):
<!-- If there is no object --> <!-- If there is no object -->
<x if="not objs"> <x if="not objs">
<td class="discreet">:_('no_ref')</td> <td class="discreet">:_('no_ref')</td>
<td>:widget['pxAdd']</td> <td>:field.pxAdd</td>
</x> </x>
<!-- If there is an object... --> <!-- If there is an object... -->
<x if="objs"> <x if="objs">
<x for="obj in objs"> <x for="obj in objs">
<td var="includeShownInfo=True">:widget['pxObjectTitle']</td> <td var="includeShownInfo=True">:field.pxObjectTitle</td>
</x> </x>
</x> </x>
</tr> </tr>
@ -194,15 +189,15 @@ class Ref(Field):
<x if="not atMostOneRef"> <x if="not atMostOneRef">
<div if="not innerRef or showPlusIcon" style="margin-bottom: 4px"> <div if="not innerRef or showPlusIcon" style="margin-bottom: 4px">
(<x>:totalNumber</x>) (<x>:totalNumber</x>)
<x>:widget['pxAdd']</x> <x>:field.pxAdd</x>
<!-- The search button if field is queryable --> <!-- 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" style=":'background-image: url(%s/ui/buttonSearch.png)' % appUrl"
value=":_('search_title')" value=":_('search_title')"
onclick=":'window.location=%s' % \ onclick=":'window.location=%s' % \
q('%s/ui/search?className=%s&amp;ref=%s:%s' % \ q('%s/ui/search?className=%s&amp;ref=%s:%s' % \
(ztool.absolute_url(), linkedPortalType, contextObj.UID(), \ (ztool.absolute_url(), linkedPortalType, contextObj.UID(), \
appyType['name']))"/> field.name))"/>
</div> </div>
<!-- Appy (top) navigation --> <!-- Appy (top) navigation -->
@ -216,17 +211,15 @@ class Ref(Field):
<tr valign="bottom"> <tr valign="bottom">
<td> <td>
<!-- Show forward or backward reference(s) --> <!-- 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 \ width=":innerRef and '100%' or field.layouts['view']['width']"
appyType['layouts']['view']['width']" var="columns=objs[0].getColumnsSpecifiers(field.shownInfo, dir)">
var="columns=objs[0].getColumnsSpecifiers(\ <tr if="field.showHeaders">
appyType['shownInfo'], dir)">
<tr if="appyType['showHeaders']">
<th for="column in columns" width=":column['width']" <th for="column in columns" width=":column['width']"
align="column['align']"> align="column['align']">
<x var="widget=column['field']"> <x var="field=column['field']">
<span>_(widget['labelId'])</span> <span>_(field.labelId)</span>
<x>:widget['pxSortIcons']</x> <x>:field.pxSortIcons</x>
<x var="className=linkedPortalType">:contextObj.appy(\ <x var="className=linkedPortalType">:contextObj.appy(\
).pxShowDetails</x> ).pxShowDetails</x>
</x> </x>
@ -237,18 +230,18 @@ class Ref(Field):
class=":odd and 'even' or 'odd'"> class=":odd and 'even' or 'odd'">
<td for="column in columns" <td for="column in columns"
width=":column['width']" align=":column['align']"> width=":column['width']" align=":column['align']">
<x var="widget=column['field']"> <x var="field=column['field']">
<!-- The "title" field --> <!-- The "title" field -->
<x if="python: widget['name'] == 'title'"> <x if="python: field.name == 'title'">
<x>:widget['pxObjectTitle']</x> <x>:field.pxObjectTitle</x>
<div if="obj.mayAct()">:widget['pxObjectActions']</div> <div if="obj.mayAct()">:field.pxObjectActions</div>
</x> </x>
<!-- Any other field --> <!-- Any other field -->
<x if="widget['name'] != 'title'"> <x if="field.name != 'title'">
<x var="contextObj=obj; <x var="contextObj=obj;
layoutType='cell'; layoutType='cell';
innerRef=True" innerRef=True"
if="obj.showField(widget['name'], layoutType='result')"> if="obj.showField(field.name, layoutType='result')">
<!-- use-macro="app/ui/widgets/show/macros/field"/--> <!-- use-macro="app/ui/widgets/show/macros/field"/-->
</x> </x>
</x> </x>
@ -267,37 +260,35 @@ class Ref(Field):
</div>''') </div>''')
pxView = pxCell = Px(''' pxView = pxCell = Px('''
<x var="x=req.set('fieldName', widget['name'])">:widget['pxViewContent'] <x var="x=req.set('fieldName', field.name)">:field.pxViewContent</x>''')
</x>''')
pxEdit = Px(''' pxEdit = Px('''
<x if="widget['link']" <x if="field.link"
var="requestValue=req.get(name, []); var="requestValue=req.get(name, []);
inRequest=req.has_key(name); inRequest=req.has_key(name);
allObjects=contextObj.getSelectableAppyRefs(name); allObjects=field.getSelectableObjects();
refUids=[o.UID() for o in contextObj.getAppyRefs(name)['objects']]; uids=[o.UID() for o in field.getLinkedObjects(contextObj).objects];
isBeingCreated=contextObj.isTemporary()"> 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 ''"> multiple="isMultiple and 'multiple' or ''">
<option value="" if="not isMultiple">:_('choose_a_value')"></option> <option value="" if="not isMultiple">:_('choose_a_value')"></option>
<x for="refObj in allObjects"> <x for="refObj in allObjects">
<option var="uid=contextObj.getReferenceUid(refObj)" <option var="uid=refObj.o.UID()"
selected=":inRequest and (uid in requestValue) or \ selected=":inRequest and (uid in requestValue) or \
(uid in refUids)" (uid in uids)"
value=":uid">:contextObj.getReferenceLabel(name, refObj) value=":uid">:field.getReferenceLabel(refObj)</option>
</option>
</x> </x>
</select> </select>
</x>''') </x>''')
pxSearch = Px(''' pxSearch = Px('''
<x> <x>
<label lfor=":widgetName">:_(widget['labelId'])"></label><br/>&nbsp;&nbsp; <label lfor=":widgetName">:_(field.labelId)"></label><br/>&nbsp;&nbsp;
<!-- The "and" / "or" radio buttons --> <!-- The "and" / "or" radio buttons -->
<x var="operName='o_%s' % name; <x var="operName='o_%s' % name;
orName='%s_or' % operName; orName='%s_or' % operName;
andName='%s_and' % operName" andName='%s_and' % operName"
if="widget['multiplicity'][1] != 1"> if="field.multiplicity[1] != 1">
<input type="radio" name=":operName" id=":orName" <input type="radio" name=":operName" id=":orName"
checked="checked" value="or"/> checked="checked" value="or"/>
<label lfor=":orName">:_('search_or')"></label> <label lfor=":orName">:_('search_or')"></label>
@ -305,12 +296,12 @@ class Ref(Field):
<label lfor=":andName">:_('search_and')"></label><br/> <label lfor=":andName">:_('search_and')"></label><br/>
</x> </x>
<!-- The list of values --> <!-- 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)"> <x for="v in ztool.getSearchValues(name, className)">
<option var="uid=v[0]; <option var="uid=v[0];
title=ztool.getReferenceLabel(name, v[1], className)" title=field.getReferenceLabel(v[1])"
value=":uid" value=":uid"
title=":title">:ztool.truncateValue(title, widget['swidth'])"> title=":title">:ztool.truncateValue(title, field.swidth)">
</option> </option>
</x> </x>
</select> </select>
@ -482,6 +473,14 @@ class Ref(Field):
if someObjects: return res if someObjects: return res
return res.objects 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): def getFormattedValue(self, obj, value, showChanges=False):
return value return value
@ -650,6 +649,47 @@ class Ref(Field):
else: else:
return self.callMethod(obj, self.changeOrder) 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): def autoref(klass, field):
'''klass.field is a Ref to p_klass. This kind of auto-reference can't be '''klass.field is a Ref to p_klass. This kind of auto-reference can't be
declared in the "normal" way, like this: 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.layout import Table
from appy.gen.indexer import XhtmlTextExtractor from appy.gen.indexer import XhtmlTextExtractor
from appy.fields import Field from appy.fields import Field
from appy.px import Px
from appy.shared.data import countries from appy.shared.data import countries
from appy.shared.xml_parser import XhtmlCleaner from appy.shared.xml_parser import XhtmlCleaner
from appy.shared.diff import HtmlDiff 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})?' \ URL = c('(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})?' \
'(([0-9]{1,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 # Some predefined functions that may also be used as validators
@staticmethod @staticmethod
def _MODULO_97(obj, value, complement=False): def _MODULO_97(obj, value, complement=False):
@ -352,14 +465,16 @@ class String(Field):
if res in self.emptyValuesCatalogIgnored: res = ' ' if res in self.emptyValuesCatalogIgnored: res = ' '
return res return res
def getPossibleValues(self,obj,withTranslations=False,withBlankValue=False): def getPossibleValues(self, obj, withTranslations=False,
'''Returns the list of possible values for this field if it is a withBlankValue=False, className=None):
selection field. If p_withTranslations is True, '''Returns the list of possible values for this field (only for fields
instead of returning a list of string values, the result is a list with self.isSelect=True). If p_withTranslations is True, instead of
of tuples (s_value, s_translation). If p_withBlankValue is True, a returning a list of string values, the result is a list of tuples
blank value is prepended to the list, excepted if the type is (s_value, s_translation). If p_withBlankValue is True, a blank value
multivalued.''' is prepended to the list, excepted if the type is multivalued. If
if not self.isSelect: raise 'This field is not a selection.' 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): if isinstance(self.validator, Selection):
# We need to call self.methodName for getting the (dynamic) values. # We need to call self.methodName for getting the (dynamic) values.
# If methodName begins with _appy_, it is a special Appy method: # If methodName begins with _appy_, it is a special Appy method:
@ -381,6 +496,15 @@ class String(Field):
if methodName.startswith('tool:'): if methodName.startswith('tool:'):
obj = obj.getTool() obj = obj.getTool()
methodName = methodName[5:] 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? # Do we need to call the method on the object or on the wrapper?
if methodName.startswith('_appy_'): if methodName.startswith('_appy_'):
exec 'res = obj.%s(*args)' % methodName exec 'res = obj.%s(*args)' % methodName
@ -503,4 +627,47 @@ class String(Field):
'''Generates a password (we recycle here the captcha challenge '''Generates a password (we recycle here the captcha challenge
generator).''' generator).'''
return self.getCaptchaChallenge({})['text'] 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, def getPossibleValues(self, name, withTranslations, withBlankValue,
className=None): className=None):
'''Gets the possible values for field named p_name. This field must be a '''See docstring of String.getPossibleValues.'''
String with isSelection()=True. If p_withTranslations is True, field = self.getAppyType(name, className=className)
instead of returning a list of string values, the result is a list return field.getPossibleValues(self, withTranslations, withBlankValue,
of tuples (s_value, s_translation). If p_withBlankValue is True, a className=className)
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)
def getCaptchaChallenge(self, name): def getCaptchaChallenge(self, name):
return self.getAppyType(name).getCaptchaChallenge(self.REQUEST.SESSION) return self.getAppyType(name).getCaptchaChallenge(self.REQUEST.SESSION)