[gen] Added a new calendar field, allowing to add a single (typed) event by day.
This commit is contained in:
parent
3bd66e3264
commit
93bde7a0f5
12 changed files with 523 additions and 19 deletions
178
gen/calendar.py
Normal file
178
gen/calendar.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy import Object
|
||||
from appy.gen import Type
|
||||
from DateTime import DateTime
|
||||
from BTrees.IOBTree import IOBTree
|
||||
from persistent.list import PersistentList
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class Calendar(Type):
|
||||
'''This field allows to produce an agenda and view/edit events on it.'''
|
||||
jsFiles = {'view': ('widgets/calendar.js',)}
|
||||
|
||||
def __init__(self, eventTypes, validator=None, default=None, show='view',
|
||||
page='main', group=None, layouts=None, move=0,
|
||||
specificReadPermission=False, specificWritePermission=False,
|
||||
width=None, height=300, colspan=1, master=None,
|
||||
masterValue=None, focus=False, mapping=None, label=None,
|
||||
maxEventLength=50):
|
||||
Type.__init__(self, validator, (0,1), None, default, False, False,
|
||||
show, page, group, layouts, move, False, False,
|
||||
specificReadPermission, specificWritePermission,
|
||||
width, height, None, colspan, master, masterValue, focus,
|
||||
False, True, mapping, label)
|
||||
# eventTypes is a list of strings that identify the types of events
|
||||
# that are supported by this calendar.
|
||||
self.eventTypes = eventTypes
|
||||
# It is not possible to create events that span more days than
|
||||
# maxEventLength.
|
||||
self.maxEventLength = maxEventLength
|
||||
|
||||
def getSiblingMonth(self, month, prevNext):
|
||||
'''Gets the next or previous month (depending of p_prevNext) relative
|
||||
to p_month.'''
|
||||
dayOne = DateTime('%s/01' % month)
|
||||
if prevNext == 'previous':
|
||||
refDate = dayOne - 1
|
||||
elif prevNext == 'next':
|
||||
refDate = dayOne + 33
|
||||
return refDate.strftime('%Y/%m')
|
||||
|
||||
weekDays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
|
||||
def getNamesOfDays(self, obj, short=True):
|
||||
res = []
|
||||
for day in self.weekDays:
|
||||
if short:
|
||||
suffix = '_short'
|
||||
else:
|
||||
suffix = ''
|
||||
res.append(obj.translate('day_%s%s' % (day, suffix)))
|
||||
return res
|
||||
|
||||
def getMonthGrid(self, month):
|
||||
'''Creates a list of lists of DateTime objects representing the calendar
|
||||
grid to render for a given p_month.'''
|
||||
# Month is a string "YYYY/mm".
|
||||
currentDay = DateTime('%s/01 12:00' % month)
|
||||
currentMonth = currentDay.month()
|
||||
res = [[]]
|
||||
dayOneNb = currentDay.dow() or 7 # This way, Sunday is 7 and not 0.
|
||||
if dayOneNb != 1:
|
||||
previousDate = DateTime(currentDay)
|
||||
# If the 1st day of the month is not a Monday, start the row with
|
||||
# the last days of the previous month.
|
||||
for i in range(1, dayOneNb):
|
||||
previousDate = previousDate - 1
|
||||
res[0].insert(0, previousDate)
|
||||
finished = False
|
||||
while not finished:
|
||||
# Insert currentDay in the grid
|
||||
if len(res[-1]) == 7:
|
||||
# Create a new row
|
||||
res.append([currentDay])
|
||||
else:
|
||||
res[-1].append(currentDay)
|
||||
currentDay = currentDay + 1
|
||||
if currentDay.month() != currentMonth:
|
||||
finished = True
|
||||
# Complete, if needed, the last row with the first days of the next
|
||||
# month.
|
||||
if len(res[-1]) != 7:
|
||||
while len(res[-1]) != 7:
|
||||
res[-1].append(currentDay)
|
||||
currentDay = currentDay + 1
|
||||
return res
|
||||
|
||||
def getEventsAt(self, obj, date, asDict=True):
|
||||
'''Returns the list of events that exist at some p_date (=day).'''
|
||||
if not hasattr(obj, self.name): return
|
||||
years = getattr(obj, self.name)
|
||||
year = date.year()
|
||||
if year not in years: return
|
||||
months = years[year]
|
||||
month = date.month()
|
||||
if month not in months: return
|
||||
days = months[month]
|
||||
day = date.day()
|
||||
if day not in days: return
|
||||
if asDict:
|
||||
res = [e.__dict__ for e in days[day]]
|
||||
else:
|
||||
res = days[day]
|
||||
return res
|
||||
|
||||
def hasEventsAt(self, obj, date, otherEvents):
|
||||
'''Returns True if, at p_date, an event is found of the same type as
|
||||
p_otherEvents.'''
|
||||
if not otherEvents: return False
|
||||
events = self.getEventsAt(obj, date, asDict=False)
|
||||
if not events: return False
|
||||
return events[0].eventType == otherEvents[0]['eventType']
|
||||
|
||||
def createEvent(self, obj, date, handleEventSpan=True):
|
||||
'''Create a new event in the calendar, at some p_date (day). If
|
||||
p_handleEventSpan is True, we will use rq["eventSpan"] and also
|
||||
create the same event for successive days.'''
|
||||
rq = obj.REQUEST
|
||||
year, month, day = date.year(), date.month(), date.day()
|
||||
# Check that the "preferences" dict exists or not.
|
||||
if not hasattr(obj.aq_base, self.name):
|
||||
# 1st level: create a IOBTree whose keys are years.
|
||||
setattr(obj, self.name, IOBTree())
|
||||
yearsDict = getattr(obj, self.name)
|
||||
# Get the sub-dict storing months for a given year
|
||||
if year in yearsDict:
|
||||
monthsDict = yearsDict[year]
|
||||
else:
|
||||
yearsDict[year] = monthsDict = IOBTree()
|
||||
# Get the sub-dict storing days of a given month
|
||||
if month in monthsDict:
|
||||
daysDict = monthsDict[month]
|
||||
else:
|
||||
monthsDict[month] = daysDict = IOBTree()
|
||||
# Get the list of events for a given day
|
||||
if day in daysDict:
|
||||
events = daysDict[day]
|
||||
else:
|
||||
daysDict[day] = events = PersistentList()
|
||||
# Create and store the event, excepted if an event already exists.
|
||||
if not events:
|
||||
event = Object(eventType=rq['eventType'])
|
||||
events.append(event)
|
||||
# Span the event on the successive days if required
|
||||
if handleEventSpan and rq['eventSpan']:
|
||||
nbOfDays = int(rq['eventSpan'])
|
||||
for i in range(nbOfDays):
|
||||
date = date + 1
|
||||
self.createEvent(obj, date, handleEventSpan=False)
|
||||
|
||||
def deleteEvent(self, obj, date, handleEventSpan=True):
|
||||
'''Deletes an event. It actually deletes all events at rq['day'].
|
||||
If p_handleEventSpan is True, we will use rq["deleteNext"] to
|
||||
delete successive events, too.'''
|
||||
rq = obj.REQUEST
|
||||
if not self.getEventsAt(obj, date): return
|
||||
daysDict = getattr(obj, self.name)[date.year()][date.month()]
|
||||
# Remember events, in case we must delete similar ones for next days.
|
||||
events = self.getEventsAt(obj, date)
|
||||
del daysDict[date.day()]
|
||||
if handleEventSpan and rq.has_key('deleteNext') and \
|
||||
(rq['deleteNext'] == 'True'):
|
||||
while True:
|
||||
date = date + 1
|
||||
if self.hasEventsAt(obj, date, events):
|
||||
self.deleteEvent(obj, date, handleEventSpan=False)
|
||||
else:
|
||||
break
|
||||
|
||||
def process(self, obj):
|
||||
'''Processes an action coming from the calendar widget, ie, the creation
|
||||
or deletion of a calendar event.'''
|
||||
rq = obj.REQUEST
|
||||
action = rq['actionType']
|
||||
# Get the date for this action
|
||||
if action == 'createEvent':
|
||||
return self.createEvent(obj, DateTime(rq['day']))
|
||||
elif action == 'deleteEvent':
|
||||
return self.deleteEvent(obj, DateTime(rq['day']))
|
||||
# ------------------------------------------------------------------------------
|
Loading…
Add table
Add a link
Reference in a new issue