[gen] Calendar field: added a validation mechanism.
This commit is contained in:
parent
d1aec8d5e6
commit
09bf03f9bf
|
@ -2,7 +2,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import types
|
import types
|
||||||
from appy import Object
|
from appy import Object
|
||||||
from appy.shared.utils import splitList, IterSub
|
from appy.shared import utils as sutils
|
||||||
from appy.gen import Field
|
from appy.gen import Field
|
||||||
from appy.px import Px
|
from appy.px import Px
|
||||||
from DateTime import DateTime
|
from DateTime import DateTime
|
||||||
|
@ -33,6 +33,22 @@ class Timeslot:
|
||||||
if not self.eventTypes: return True
|
if not self.eventTypes: return True
|
||||||
return eventType in self.eventTypes
|
return eventType in self.eventTypes
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class Validation:
|
||||||
|
'''The validation process for a calendar consists in "converting" some event
|
||||||
|
types being "wishes" to other event types being the corresponding
|
||||||
|
validated events. This class holds information about this validation
|
||||||
|
process. For more information, see the Calendar constructor, parameter
|
||||||
|
"validation".'''
|
||||||
|
def __init__(self, method, schema):
|
||||||
|
# p_method holds a method that must return True if the currently logged
|
||||||
|
# user can validate whish events.
|
||||||
|
self.method = method
|
||||||
|
# p_schema must hold a dict whose keys are the event types being wishes
|
||||||
|
# and whose values are the event types being the corresponding validated
|
||||||
|
# event types.
|
||||||
|
self.schema = schema
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Other:
|
class Other:
|
||||||
'''Identifies a Calendar field that must be shown within another Calendar
|
'''Identifies a Calendar field that must be shown within another Calendar
|
||||||
|
@ -104,9 +120,10 @@ class Calendar(Field):
|
||||||
DateTime = DateTime
|
DateTime = DateTime
|
||||||
# Access to Calendar utility classes via the Calendar class
|
# Access to Calendar utility classes via the Calendar class
|
||||||
Timeslot = Timeslot
|
Timeslot = Timeslot
|
||||||
|
Validation = Validation
|
||||||
Other = Other
|
Other = Other
|
||||||
Event = Event
|
Event = Event
|
||||||
IterSub = IterSub
|
IterSub = sutils.IterSub
|
||||||
# Error messages
|
# Error messages
|
||||||
TIMESLOT_USED = 'An event is already defined at this timeslot.'
|
TIMESLOT_USED = 'An event is already defined at this timeslot.'
|
||||||
DAY_FULL = 'No more place for adding this event.'
|
DAY_FULL = 'No more place for adding this event.'
|
||||||
|
@ -155,6 +172,7 @@ class Calendar(Field):
|
||||||
# Timeline view for a calendar
|
# Timeline view for a calendar
|
||||||
pxViewTimeline = Px('''
|
pxViewTimeline = Px('''
|
||||||
<table cellpadding="0" cellspacing="0" class="list timeline"
|
<table cellpadding="0" cellspacing="0" class="list timeline"
|
||||||
|
id=":ajaxHookId + '_cal'"
|
||||||
var="monthsInfos=field.getTimelineMonths(grid, zobj)">
|
var="monthsInfos=field.getTimelineMonths(grid, zobj)">
|
||||||
<!-- Column specifiers -->
|
<!-- Column specifiers -->
|
||||||
<colgroup>
|
<colgroup>
|
||||||
|
@ -202,14 +220,12 @@ class Calendar(Field):
|
||||||
|
|
||||||
# Popup for adding an event in the month view
|
# Popup for adding an event in the month view
|
||||||
pxAddPopup = Px('''
|
pxAddPopup = Px('''
|
||||||
<div var="prefix='%s_newEvent' % field.name;
|
<div var="popupId=ajaxHookId + '_new'"
|
||||||
popupId=prefix + 'Popup'"
|
|
||||||
id=":popupId" class="popup" align="center">
|
id=":popupId" class="popup" align="center">
|
||||||
<form id=":prefix + 'Form'" method="post">
|
<form id=":popupId + 'Form'" method="post" action="/process">
|
||||||
<input type="hidden" name="fieldName" value=":field.name"/>
|
<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=":field.name"/>
|
<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="actionType" value="createEvent"/>
|
||||||
<input type="hidden" name="day"/>
|
<input type="hidden" name="day"/>
|
||||||
|
|
||||||
|
@ -236,9 +252,8 @@ class Calendar(Field):
|
||||||
</div>
|
</div>
|
||||||
<input type="button"
|
<input type="button"
|
||||||
value=":_('object_save')"
|
value=":_('object_save')"
|
||||||
onclick=":'triggerCalendarEvent(%s, %s, %s, %s, \
|
onclick=":'triggerCalendarEvent(%s, %s, %s_maxEventLength)' % \
|
||||||
%s_maxEventLength)' % (q('new'), q(ajaxHookId), \
|
(q(ajaxHookId), q('new'), field.name)"/>
|
||||||
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)"/>
|
||||||
|
@ -247,14 +262,12 @@ class Calendar(Field):
|
||||||
|
|
||||||
# Popup for removing events in the month view
|
# Popup for removing events in the month view
|
||||||
pxDelPopup = Px('''
|
pxDelPopup = Px('''
|
||||||
<div var="prefix='%s_delEvent' % field.name;
|
<div var="popupId=ajaxHookId + '_del'"
|
||||||
popupId=prefix + 'Popup'"
|
|
||||||
id=":popupId" class="popup" align="center">
|
id=":popupId" class="popup" align="center">
|
||||||
<form id=":prefix + 'Form'" method="post">
|
<form id=":popupId + 'Form'" method="post" action="/process">
|
||||||
<input type="hidden" name="fieldName" value=":field.name"/>
|
<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=":field.name"/>
|
<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="actionType" value="deleteEvent"/>
|
||||||
<input type="hidden" name="timeslot" value="main"/>
|
<input type="hidden" name="timeslot" value="main"/>
|
||||||
<input type="hidden" name="day"/>
|
<input type="hidden" name="day"/>
|
||||||
|
@ -263,8 +276,8 @@ class Calendar(Field):
|
||||||
|
|
||||||
<!-- Delete successive events ? -->
|
<!-- Delete successive events ? -->
|
||||||
<div class="discreet" style="margin-bottom: 10px"
|
<div class="discreet" style="margin-bottom: 10px"
|
||||||
id=":prefix + 'DelNextEvent'"
|
id=":ajaxHookId + '_DelNextEvent'"
|
||||||
var="cbId=prefix + '_cb'; hdId=prefix + '_hd'">
|
var="cbId=popupId + '_cb'; hdId=popupId + '_hd'">
|
||||||
<input type="checkbox" name="deleteNext_cb" id=":cbId"
|
<input type="checkbox" name="deleteNext_cb" id=":cbId"
|
||||||
onClick=":'toggleCheckbox(%s, %s)' % (q(cbId), q(hdId))"/>
|
onClick=":'toggleCheckbox(%s, %s)' % (q(cbId), q(hdId))"/>
|
||||||
<input type="hidden" id=":hdId" name="deleteNext"/>
|
<input type="hidden" id=":hdId" name="deleteNext"/>
|
||||||
|
@ -272,8 +285,8 @@ class Calendar(Field):
|
||||||
style="text-transform: none">:_('del_next_events')</label>
|
style="text-transform: none">:_('del_next_events')</label>
|
||||||
</div>
|
</div>
|
||||||
<input type="button" value=":_('yes')"
|
<input type="button" value=":_('yes')"
|
||||||
onClick=":'triggerCalendarEvent(%s, %s, %s, %s)' % \
|
onClick=":'triggerCalendarEvent(%s, %s)' % \
|
||||||
(q('del'), q(ajaxHookId), q(field.name), q(objUrl))"/>
|
(q(ajaxHookId), q('del'))"/>
|
||||||
<input type="button" value=":_('no')"
|
<input type="button" value=":_('no')"
|
||||||
onclick=":'closePopup(%s)' % q(popupId)"/>
|
onclick=":'closePopup(%s)' % q(popupId)"/>
|
||||||
</form>
|
</form>
|
||||||
|
@ -282,7 +295,7 @@ class Calendar(Field):
|
||||||
# Month view for a calendar
|
# Month view for a calendar
|
||||||
pxViewMonth = Px('''
|
pxViewMonth = Px('''
|
||||||
<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%" id=":ajaxHookId + '_cal'"
|
||||||
var="rowHeight=int(field.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">
|
||||||
|
@ -321,23 +334,28 @@ class Calendar(Field):
|
||||||
var2="freeSlots=field.getFreeSlotsAt(date, events, slotIds,\
|
var2="freeSlots=field.getFreeSlotsAt(date, events, slotIds,\
|
||||||
slotIdsStr, True)"
|
slotIdsStr, True)"
|
||||||
onclick=":'openEventPopup(%s,%s,%s,null,null,%s,%s,%s)' % \
|
onclick=":'openEventPopup(%s,%s,%s,null,null,%s,%s,%s)' % \
|
||||||
(q('new'), q(field.name), q(dayString), q(info.eventTypes), \
|
(q(ajaxHookId), q('new'), q(dayString), q(info.eventTypes), \
|
||||||
q(info.message), q(freeSlots))"/>
|
q(info.message), q(freeSlots))"/>
|
||||||
</x>
|
</x>
|
||||||
<!-- Icon for deleting event(s) -->
|
<!-- Icon for deleting event(s) -->
|
||||||
<img if="mayDelete" class="clickable" style="visibility:hidden"
|
<img if="mayDelete" class="clickable" style="visibility:hidden"
|
||||||
src=":url(single and 'delete' or 'deleteMany')"
|
src=":url(single and 'delete' or 'deleteMany')"
|
||||||
onclick=":'openEventPopup(%s,%s,%s,%s,%s)' % (q('del'), \
|
onclick=":'openEventPopup(%s,%s,%s,%s,%s)' % (q(ajaxHookId), \
|
||||||
q(field.name), q(dayString), q('main'), q(spansDays))"/>
|
q('del'), q(dayString), q('main'), q(spansDays))"/>
|
||||||
<!-- Events -->
|
<!-- Events -->
|
||||||
<x if="events">
|
<x if="events">
|
||||||
<div for="event in events" style="color: grey">
|
<div for="event in events" style="color: grey">
|
||||||
|
<!-- Checkbox for validating the event -->
|
||||||
|
<input type="checkbox" checked="checked" class="smallbox"
|
||||||
|
if="mayValidate and (event.eventType in field.validation.schema)"
|
||||||
|
id=":'%s_%s_%s' % (date.strftime('%Y%m%d'), event.eventType, \
|
||||||
|
event.timeslot)"/>
|
||||||
<x>::event.getName(allEventNames)</x>
|
<x>::event.getName(allEventNames)</x>
|
||||||
<!-- Icon for delete this particular event -->
|
<!-- Icon for delete this particular event -->
|
||||||
<img if="mayDelete and not single" class="clickable"
|
<img if="mayDelete and not single" class="clickable"
|
||||||
src=":url('delete')" style="visibility:hidden"
|
src=":url('delete')" style="visibility:hidden"
|
||||||
onclick=":'openEventPopup(%s,%s,%s,%s)' % (q('del'), \
|
onclick=":'openEventPopup(%s,%s,%s,%s)' % (q(ajaxHookId), \
|
||||||
q(field.name), q(dayString), q(event.timeslot))"/>
|
q('del'), q(dayString), q(event.timeslot))"/>
|
||||||
</div>
|
</div>
|
||||||
</x>
|
</x>
|
||||||
<!-- Events from other calendars -->
|
<!-- Events from other calendars -->
|
||||||
|
@ -384,12 +402,15 @@ class Calendar(Field):
|
||||||
namesOfDays=field.getNamesOfDays(_);
|
namesOfDays=field.getNamesOfDays(_);
|
||||||
showTimeslots=len(field.timeslots) > 1;
|
showTimeslots=len(field.timeslots) > 1;
|
||||||
slotIds=[slot.id for slot in field.timeslots];
|
slotIds=[slot.id for slot in field.timeslots];
|
||||||
slotIdsStr=','.join(slotIds)"
|
slotIdsStr=','.join(slotIds);
|
||||||
|
mayValidate=field.mayValidate(zobj)"
|
||||||
id=":ajaxHookId">
|
id=":ajaxHookId">
|
||||||
<script>:'var %s_maxEventLength = %d;' % \
|
<script>:'var %s_maxEventLength = %d;' % \
|
||||||
(field.name, field.maxEventLength)</script>
|
(field.name, field.maxEventLength)</script>
|
||||||
|
<script>:field.getAjaxData(ajaxHookId, zobj, render=render, \
|
||||||
|
month=defaultDateMonth)</script>
|
||||||
|
|
||||||
<!-- Month chooser -->
|
<!-- Actions (month chooser, validation) -->
|
||||||
<div style="margin-bottom: 5px"
|
<div style="margin-bottom: 5px"
|
||||||
var="fmt='%Y/%m/%d';
|
var="fmt='%Y/%m/%d';
|
||||||
goBack=not startDate or (startDate.strftime(fmt) < \
|
goBack=not startDate or (startDate.strftime(fmt) < \
|
||||||
|
@ -398,23 +419,26 @@ class Calendar(Field):
|
||||||
grid[-1][-1].strftime(fmt))">
|
grid[-1][-1].strftime(fmt))">
|
||||||
<!-- Go to the previous month -->
|
<!-- Go to the previous month -->
|
||||||
<img class="clickable" if="goBack" src=":url('arrowLeft')"
|
<img class="clickable" if="goBack" src=":url('arrowLeft')"
|
||||||
onclick=":'askCalendar(%s,%s,%s,%s,%s)' % (q(ajaxHookId), \
|
onclick=":'askMonth(%s,%s)' % (q(ajaxHookId), q(previousMonth))"/>
|
||||||
q(objUrl), q(render), q(field.name), q(previousMonth))"/>
|
|
||||||
<!-- Go back to the default date -->
|
<!-- Go back to the default date -->
|
||||||
<input type="button" if="goBack or goForward"
|
<input type="button" if="goBack or goForward"
|
||||||
var="fmt='%Y/%m';
|
var="fmt='%Y/%m';
|
||||||
label=(defaultDate.strftime(fmt)==today.strftime(fmt)) and \
|
label=(defaultDate.strftime(fmt)==today.strftime(fmt)) and \
|
||||||
'today' or 'goto_source'"
|
'today' or 'goto_source'"
|
||||||
value=":_(label)"
|
value=":_(label)"
|
||||||
onclick=":'askCalendar(%s,%s,%s,%s,%s)' % (q(ajaxHookId), \
|
onclick=":'askMonth(%s,%s)' % (q(ajaxHookId),q(defaultDateMonth))"
|
||||||
q(objUrl), q(render), q(field.name), q(defaultDateMonth))"
|
|
||||||
disabled=":defaultDate.strftime(fmt)==monthDayOne.strftime(fmt)"/>
|
disabled=":defaultDate.strftime(fmt)==monthDayOne.strftime(fmt)"/>
|
||||||
<!-- Go to the next month -->
|
<!-- Go to the next month -->
|
||||||
<img class="clickable" if="goForward" src=":url('arrowRight')"
|
<img class="clickable" if="goForward" src=":url('arrowRight')"
|
||||||
onclick=":'askCalendar(%s,%s,%s,%s,%s)' % (q(ajaxHookId), \
|
onclick=":'askMonth(%s,%s)' % (q(ajaxHookId), q(nextMonth))"/>
|
||||||
q(objUrl), q(render), 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>
|
||||||
|
<!-- Validate button -->
|
||||||
|
<input if="mayValidate" type="button" value=":_('validate_events')"
|
||||||
|
class="buttonSmall button" style=":url('validate', bg=True)"
|
||||||
|
var2="js='validateEvents(%s)' % q(ajaxHookId)"
|
||||||
|
onclick=":'askConfirm(%s,%s,%s)' % (q('script'), q(js, False), \
|
||||||
|
q(_('validate_events_confirm')))"/>
|
||||||
</div>
|
</div>
|
||||||
<x>:getattr(field, 'pxView%s' % render.capitalize())</x>
|
<x>:getattr(field, 'pxView%s' % render.capitalize())</x>
|
||||||
</div>''')
|
</div>''')
|
||||||
|
@ -430,7 +454,8 @@ class Calendar(Field):
|
||||||
others=None, timelineName=None, additionalInfo=None,
|
others=None, timelineName=None, additionalInfo=None,
|
||||||
startDate=None, endDate=None, defaultDate=None, timeslots=None,
|
startDate=None, endDate=None, defaultDate=None, timeslots=None,
|
||||||
colors=None, showUncolored=False, preCompute=None,
|
colors=None, showUncolored=False, preCompute=None,
|
||||||
applicableEvents=None, view=None, xml=None, delete=True):
|
applicableEvents=None, validation=None, view=None, xml=None,
|
||||||
|
delete=True):
|
||||||
Field.__init__(self, validator, (0,1), default, show, page, group,
|
Field.__init__(self, validator, (0,1), default, show, page, group,
|
||||||
layouts, move, False, True, False, specificReadPermission,
|
layouts, move, False, True, False, specificReadPermission,
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
|
@ -533,6 +558,12 @@ class Calendar(Field):
|
||||||
# for explaining him why he can, for this day, only create events of a
|
# for explaining him why he can, for this day, only create events of a
|
||||||
# sub-set of the possible event types (or even no event at all).
|
# sub-set of the possible event types (or even no event at all).
|
||||||
self.applicableEvents = applicableEvents
|
self.applicableEvents = applicableEvents
|
||||||
|
# A validation process can be associated to a Calendar event. It
|
||||||
|
# consists in identifying validators and letting them "convert" event
|
||||||
|
# types being wished to final, validated event types. If you want to
|
||||||
|
# enable this, define a Validation instance (see the hereabove class)
|
||||||
|
# in parameter "validation".
|
||||||
|
self.validation = validation
|
||||||
# May the user delete events in this calendar? If "delete" is a method,
|
# May the user delete events in this calendar? If "delete" is a method,
|
||||||
# it must accept an event type as single arg.
|
# it must accept an event type as single arg.
|
||||||
self.delete = delete
|
self.delete = delete
|
||||||
|
@ -717,17 +748,21 @@ class Calendar(Field):
|
||||||
return ','.join(res)
|
return ','.join(res)
|
||||||
|
|
||||||
def getEventsAt(self, obj, date):
|
def getEventsAt(self, obj, date):
|
||||||
'''Returns the list of events that exist at some p_date (=day).'''
|
'''Returns the list of events that exist at some p_date (=day). p_date
|
||||||
|
can be a DateTime instance or a tuple (i_year, i_month, i_day).'''
|
||||||
obj = obj.o # Ensure p_obj is not a wrapper
|
obj = obj.o # Ensure p_obj is not a wrapper
|
||||||
if not hasattr(obj.aq_base, self.name): return
|
if not hasattr(obj.aq_base, self.name): return
|
||||||
years = getattr(obj, self.name)
|
years = getattr(obj, self.name)
|
||||||
year = date.year()
|
# Get year, month and name from p_date
|
||||||
|
if isinstance(date, tuple):
|
||||||
|
year, month, day = date
|
||||||
|
else:
|
||||||
|
year, month, day = date.year(), date.month(), date.day()
|
||||||
|
# Dig into the oobtree
|
||||||
if year not in years: return
|
if year not in years: return
|
||||||
months = years[year]
|
months = years[year]
|
||||||
month = date.month()
|
|
||||||
if month not in months: return
|
if month not in months: return
|
||||||
days = months[month]
|
days = months[month]
|
||||||
day = date.day()
|
|
||||||
if day not in days: return
|
if day not in days: return
|
||||||
return days[day]
|
return days[day]
|
||||||
|
|
||||||
|
@ -865,7 +900,7 @@ class Calendar(Field):
|
||||||
if isinstance(others, Other):
|
if isinstance(others, Other):
|
||||||
others.getEventsAt(res, self, date, eventNames, isTimeline, colors)
|
others.getEventsAt(res, self, date, eventNames, isTimeline, colors)
|
||||||
else:
|
else:
|
||||||
for other in IterSub(others):
|
for other in sutils.IterSub(others):
|
||||||
other.getEventsAt(res, self, date, eventNames,isTimeline,colors)
|
other.getEventsAt(res, self, date, eventNames,isTimeline,colors)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@ -888,7 +923,7 @@ class Calendar(Field):
|
||||||
res[0].append(et)
|
res[0].append(et)
|
||||||
res[1][et] = self.getEventName(obj, et)
|
res[1][et] = self.getEventName(obj, et)
|
||||||
if not others: return res
|
if not others: return res
|
||||||
for other in IterSub(others):
|
for other in sutils.IterSub(others):
|
||||||
eventTypes = other.field.getEventTypes(other.obj)
|
eventTypes = other.field.getEventTypes(other.obj)
|
||||||
if eventTypes:
|
if eventTypes:
|
||||||
for et in eventTypes:
|
for et in eventTypes:
|
||||||
|
@ -1126,5 +1161,48 @@ class Calendar(Field):
|
||||||
m.month = text
|
m.month = text
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def splitList(self, l, sub): return splitList(l, sub)
|
def splitList(self, l, sub): return sutils.splitList(l, sub)
|
||||||
|
def mayValidate(self, obj):
|
||||||
|
'''May the currently logged user validate wish events ?'''
|
||||||
|
if not self.validation: return
|
||||||
|
return self.validation.method(obj.appy())
|
||||||
|
|
||||||
|
def getAjaxData(self, hook, zobj, **params):
|
||||||
|
'''Initializes an AjaxData object on the DOM node corresponding to
|
||||||
|
this calendar field.'''
|
||||||
|
params = sutils.getStringDict(params)
|
||||||
|
return "new AjaxData('%s', '%s:pxView', %s, null, '%s')" % \
|
||||||
|
(hook, self.name, params, zobj.absolute_url())
|
||||||
|
|
||||||
|
def validateEvents(self, obj):
|
||||||
|
'''Validate or discard events from the request.'''
|
||||||
|
rq = obj.REQUEST.form
|
||||||
|
counts = {'validated': 0, 'discarded': 0}
|
||||||
|
for action in ('validated', 'discarded'):
|
||||||
|
if not rq[action]: continue
|
||||||
|
for info in rq[action].split(','):
|
||||||
|
sdate, eventType, timeslot = info.split('_')
|
||||||
|
# Get the events defined at that date
|
||||||
|
date = int(sdate[:4]), int(sdate[4:6]), int(sdate[6:8])
|
||||||
|
events = self.getEventsAt(obj, date)
|
||||||
|
i = len(events) - 1
|
||||||
|
while i >= 0:
|
||||||
|
# Get the event at that timeslot
|
||||||
|
event = events[i]
|
||||||
|
if event.timeslot == timeslot:
|
||||||
|
# We have found the event
|
||||||
|
if event.eventType != eventType:
|
||||||
|
raise Exception('Wrong event type')
|
||||||
|
# Validate or discard it
|
||||||
|
if action == 'validated':
|
||||||
|
event.eventType = self.validation.schema[eventType]
|
||||||
|
else:
|
||||||
|
del events[i]
|
||||||
|
counts[action] += 1
|
||||||
|
i -= 1
|
||||||
|
obj.log('%s:%s: %d event(s) validated and %d discarded.' % \
|
||||||
|
(obj.id, self.name, counts['validated'], counts['discarded']))
|
||||||
|
if not counts['validated'] and not counts['discarded']:
|
||||||
|
return obj.translate('action_null')
|
||||||
|
return obj.translate('validate_events_done', mapping=counts)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1254,8 +1254,8 @@ class Ref(Field):
|
||||||
(obj.id, self.name, code % ('objs', 'objs'), poss)
|
(obj.id, self.name, code % ('objs', 'objs'), poss)
|
||||||
|
|
||||||
def getAjaxData(self, hook, zobj, **params):
|
def getAjaxData(self, hook, zobj, **params):
|
||||||
'''Initializes an AjaxData object on the DOM node corresponding to
|
'''Initializes an AjaxData object on the DOM node corresponding to this
|
||||||
p_hook = the whole search result.'''
|
Ref field.'''
|
||||||
# Complete params with default parameters
|
# Complete params with default parameters
|
||||||
params['ajaxHookId'] = hook;
|
params['ajaxHookId'] = hook;
|
||||||
params['scope'] = hook.rsplit('_', 1)[-1]
|
params['scope'] = hook.rsplit('_', 1)[-1]
|
||||||
|
|
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
12
gen/tr/ar.po
12
gen/tr/ar.po
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
12
gen/tr/de.po
12
gen/tr/de.po
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
12
gen/tr/en.po
12
gen/tr/en.po
|
@ -728,6 +728,18 @@ msgstr "All day"
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr "Cannot create such an event in the ${slot} slot."
|
msgstr "Cannot create such an event in the ${slot} slot."
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr "Validate events"
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr "Inserted by ${userName}"
|
msgstr "Inserted by ${userName}"
|
||||||
|
|
12
gen/tr/es.po
12
gen/tr/es.po
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
12
gen/tr/fr.po
12
gen/tr/fr.po
|
@ -728,6 +728,18 @@ msgstr "Toute la journée"
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr "Impossible de créer ce type d'événement dans la plage horaire ${slot}."
|
msgstr "Impossible de créer ce type d'événement dans la plage horaire ${slot}."
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr "Valider les événements"
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr "Tous les événements sélectionnés seront confirmés, tandis que ceux qui sont désélectionnés seront rejetés. Le ou les utilisateurs concernés seront prévenus. Êtes-vous sûr?"
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr "${validated} événement(s) a (ont) été validé(s) et ${discarded} a (ont) été rejeté(s)."
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr "Inséré par ${userName}"
|
msgstr "Inséré par ${userName}"
|
||||||
|
|
12
gen/tr/it.po
12
gen/tr/it.po
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
12
gen/tr/nl.po
12
gen/tr/nl.po
|
@ -727,6 +727,18 @@ msgstr ""
|
||||||
msgid "timeslot_misfit"
|
msgid "timeslot_misfit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Validate events"
|
||||||
|
msgid "validate_events"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "All the checked events will be confirmed, while the unchecked ones will be discarded. The concerned user(s) will be warned. Are you sure?"
|
||||||
|
msgid "validate_events_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "${validated} event(s) was (were) validated and ${discarded} was (were) discarded."
|
||||||
|
msgid "validate_events_done"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Inserted by ${userName}"
|
#. Default: "Inserted by ${userName}"
|
||||||
msgid "history_insert"
|
msgid "history_insert"
|
||||||
msgstr "Ingevuld door ${userName}"
|
msgstr "Ingevuld door ${userName}"
|
||||||
|
|
|
@ -198,3 +198,4 @@ td.search { padding-top: 8px }
|
||||||
.highlight { background-color: yellow }
|
.highlight { background-color: yellow }
|
||||||
.globalActions { margin-bottom: 4px }
|
.globalActions { margin-bottom: 4px }
|
||||||
.objectActions { margin: 2px 0 }
|
.objectActions { margin: 2px 0 }
|
||||||
|
.smallbox { margin: 0 }
|
||||||
|
|
|
@ -295,9 +295,10 @@ function askAjax(hook, form, params) {
|
||||||
}
|
}
|
||||||
else var mode = d.mode;
|
else var mode = d.mode;
|
||||||
// Get p_params if given. Note that they override anything else.
|
// Get p_params if given. Note that they override anything else.
|
||||||
|
if (params && ('mode' in params)) {
|
||||||
|
mode = params['mode']; delete params['mode'] }
|
||||||
if (params) { for (var key in params) d.params[key] = params[key]; }
|
if (params) { for (var key in params) d.params[key] = params[key]; }
|
||||||
askAjaxChunk(hook, mode, d.url, d.px, d.params, d.beforeSend,
|
askAjaxChunk(hook,mode,d.url,d.px,d.params,d.beforeSend,evalInnerScripts);
|
||||||
evalInnerScripts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function askBunch(hookId, startNumber) {
|
function askBunch(hookId, startNumber) {
|
||||||
|
|
|
@ -8,11 +8,8 @@ function toggleVisibility(node, nodeType){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function askCalendar(hookId, objectUrl, render, fieldName, month) {
|
// Sends an Ajax request for getting the calendar, at p_month
|
||||||
// Sends an Ajax request for getting the calendar, at p_month
|
function askMonth(hookId, month) {askAjax(hookId, null, {'month': month})}
|
||||||
var params = {'month': month, 'render': render};
|
|
||||||
askAjaxChunk(hookId, 'GET', objectUrl, fieldName+':pxView', params);
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableOptions(select, enabled, selectFirst, message){
|
function enableOptions(select, enabled, selectFirst, message){
|
||||||
/* This function disables, in p_select, all options that are not in p_enabled.
|
/* This function disables, in p_select, all options that are not in p_enabled.
|
||||||
|
@ -46,7 +43,7 @@ function enableOptions(select, enabled, selectFirst, message){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEventPopup(action, fieldName, day, timeslot, spansDays,
|
function openEventPopup(hookId, action, day, timeslot, spansDays,
|
||||||
applicableEventTypes, message, freeSlots) {
|
applicableEventTypes, message, freeSlots) {
|
||||||
/* Opens the popup for creating (or deleting, depending on p_action) a
|
/* Opens the popup for creating (or deleting, depending on p_action) a
|
||||||
calendar event at some p_day. When action is "del", we need to know the
|
calendar event at some p_day. When action is "del", we need to know the
|
||||||
|
@ -57,13 +54,13 @@ function openEventPopup(action, fieldName, day, timeslot, spansDays,
|
||||||
p_applicableEventTypes; p_message contains an optional message explaining
|
p_applicableEventTypes; p_message contains an optional message explaining
|
||||||
why not applicable types are not applicable. When "new", p_freeSlots may
|
why not applicable types are not applicable. When "new", p_freeSlots may
|
||||||
list the available timeslots at p_day. */
|
list the available timeslots at p_day. */
|
||||||
var prefix = fieldName + '_' + action + 'Event';
|
var popupId = hookId + '_' + action;
|
||||||
var f = document.getElementById(prefix + 'Form');
|
var f = document.getElementById(popupId + 'Form');
|
||||||
f.day.value = day;
|
f.day.value = day;
|
||||||
if (action == 'del') {
|
if (action == 'del') {
|
||||||
if (f.timeslot) f.timeslot.value = timeslot;
|
if (f.timeslot) f.timeslot.value = timeslot;
|
||||||
// Show or hide the checkbox for deleting the event for successive days
|
// Show or hide the checkbox for deleting the event for successive days
|
||||||
var elem = document.getElementById(prefix + 'DelNextEvent');
|
var elem = document.getElementById(hookId + '_DelNextEvent');
|
||||||
var cb = elem.getElementsByTagName('input');
|
var cb = elem.getElementsByTagName('input');
|
||||||
cb[0].checked = false;
|
cb[0].checked = false;
|
||||||
cb[1].value = 'False';
|
cb[1].value = 'False';
|
||||||
|
@ -78,15 +75,15 @@ function openEventPopup(action, fieldName, day, timeslot, spansDays,
|
||||||
enableOptions(f.eventType, applicableEventTypes, false, message);
|
enableOptions(f.eventType, applicableEventTypes, false, message);
|
||||||
if (f.timeslot) enableOptions(f.timeslot, freeSlots, true, 'Not free');
|
if (f.timeslot) enableOptions(f.timeslot, freeSlots, true, 'Not free');
|
||||||
}
|
}
|
||||||
openPopup(prefix + 'Popup');
|
openPopup(popupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerCalendarEvent(action, hookId, fieldName, objectUrl,
|
function triggerCalendarEvent(hookId, action, maxEventLength) {
|
||||||
maxEventLength) {
|
|
||||||
/* Sends an Ajax request for triggering a calendar event (create or delete an
|
/* Sends an Ajax request for triggering a calendar event (create or delete an
|
||||||
event) and refreshing the view month. */
|
event) and refreshing the view month. */
|
||||||
var prefix = fieldName + '_' + action + 'Event';
|
var popupId = hookId + '_' + action;
|
||||||
var f = document.getElementById(prefix + 'Form');
|
var formId = popupId + 'Form';
|
||||||
|
var f = document.getElementById(formId);
|
||||||
if (action == 'new') {
|
if (action == 'new') {
|
||||||
// Check that an event span has been specified
|
// Check that an event span has been specified
|
||||||
if (f.eventType.selectedIndex == 0) {
|
if (f.eventType.selectedIndex == 0) {
|
||||||
|
@ -105,12 +102,25 @@ function triggerCalendarEvent(action, hookId, fieldName, objectUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var elems = f.elements;
|
closePopup(popupId);
|
||||||
var params = {};
|
askAjax(hookId, formId);
|
||||||
// Put form elements into "params"
|
}
|
||||||
for (var i=0; i < elems.length; i++) {
|
|
||||||
params[elems[i].name] = elems[i].value;
|
// Function for validating and discarding calendar events
|
||||||
}
|
function validateEvents(hookId) {
|
||||||
closePopup(prefix + 'Popup');
|
// Collect checkboxes from hookId and identify checked and unchecked ones
|
||||||
askAjaxChunk(hookId, 'POST', objectUrl, fieldName+':pxView', params);
|
var validated = [];
|
||||||
|
var discarded = [];
|
||||||
|
var node = document.getElementById(hookId + '_cal');
|
||||||
|
var cbs = node.getElementsByTagName('input');
|
||||||
|
for (var i=0; i<cbs.length; i++) {
|
||||||
|
if (cbs[i].type != 'checkbox') continue;
|
||||||
|
if (cbs[i].checked) validated.push(cbs[i].id);
|
||||||
|
else discarded.push(cbs[i].id);
|
||||||
|
}
|
||||||
|
validated = validated.join()
|
||||||
|
discarded = discarded.join()
|
||||||
|
var params = {'action': 'validateEvents', 'validated': validated,
|
||||||
|
'discarded': discarded, 'mode': 'POST'};
|
||||||
|
askAjax(hookId, null, params);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue