1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Plinn - http://plinn.org #
4 # Copyright (C) 2005-2007 Benoît PIN <benoit.pin@ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ Plinn calendar tool provides utilities to display content on a calendar layout
22 $Id: CalendarTool.py 1360 2008-08-01 15:59:58Z pin $
23 $URL: http://svn.cri.ensmp.fr/svn/Plinn/branches/CMF-2.1/CalendarTool.py $
26 from Products
.CMFCore
.utils
import UniqueObject
, getToolByName
27 from Products
.CMFCore
.permissions
import ManagePortal
, ListFolderContents
28 from Products
.CMFCore
.ActionProviderBase
import ActionProviderBase
29 from Products
.CMFCore
.ActionInformation
import ActionInformation
30 from Products
.CMFCore
.ActionInformation
import ActionInformation
31 from OFS
.SimpleItem
import SimpleItem
32 from AccessControl
import ClassSecurityInfo
33 from Globals
import InitializeClass
34 from Products
.PageTemplates
.PageTemplateFile
import PageTemplateFile
37 from DateTime
import DateTime
39 from types
import StringType
41 class CalendarTool (UniqueObject
, ActionProviderBase
, SimpleItem
):
42 """ a calendar tool """
43 id = 'portal_calendar'
44 meta_type
= 'Plinn Calendar Tool'
45 security
= ClassSecurityInfo()
47 manage_options
= ({ 'label' : 'Configure', 'action' : 'manage_configure' }, ) + \
48 ActionProviderBase
.manage_options
+ \
49 SimpleItem
.manage_options
57 security
.declareProtected( ManagePortal
, 'manage_configure' )
58 manage_configure
= PageTemplateFile('www/configureCalendarTool', globals(),
59 __name__
='manage_configure')
62 calendar
.setfirstweekday(0)
63 self
.dateIndexes
= ['created', 'modified', 'DateTimeOriginal']
64 self
.displayRange
= [0, 96]
65 #calViewActionInfo = ActionInformation('calendar_view',
66 # title = 'Calendar View',
67 # category = 'folder',
68 # permissions = (ListFolderContents, ),
69 # condition = 'python: folder is object',
70 # action = 'string: ${folder_url}/calendar_view')
71 self
._actions
= tuple()
73 security
.declareProtected(ManagePortal
, 'configureTool')
74 def configureTool(self
, dateIndexes
, displayRange
, REQUEST
= None) :
75 """ Define date indexes managed by this tool """
76 self
.dateIndexes
= dateIndexes
77 self
.displayRange
= map(lambda s
: int(s
) * 4, displayRange
)
79 return self
.manage_configure(self
, REQUEST
, manage_tabs_message
='Saved changes.')
81 security
.declarePublic('getDateIndexes')
82 def getDateIndexes(self
) :
83 """ Return managed date indexes """
85 return self
.dateIndexes
87 security
.declareProtected(ManagePortal
, 'getCandidateIndexes')
88 def getCandidateIndexes(self
) :
89 """ return portal_catalog date and field indexes """
91 cTool
= getToolByName(self
, 'portal_catalog')
92 fIndexes
= [index
.id for index
in cTool
.getIndexObjects() if index
.meta_type
== 'FieldIndex' or \
93 index
.meta_type
== 'DateIndex' ]
94 fIndexes
.sort(lambda a
, b
: cmp(a
.lower(), b
.lower()))
97 security
.declarePublic('getCommonIndexes')
98 def getCommonIndexes(self
, objects
) :
99 """ Return indexes which belongs to all objects """
106 if ob
.meta_type
in types
:
109 types
.append(ob
.meta_type
)
111 for index
in self
.dateIndexes
:
112 if hasattr(ob
, index
) :
113 obIndexes
.append(index
)
114 allIndexSets
.append(Set(obIndexes
))
115 return reduce(lambda a
, b
: a
& b
, allIndexSets
, Set(self
.dateIndexes
))
117 security
.declarePublic('getDisplayRange')
118 def getDisplayRange(self
) :
119 """ Return range to display in week view
121 return self
.displayRange
123 security
.declarePublic('indexIsCallable')
124 def indexIsCallable(self
, index
, objects
= []) :
125 """ Return 1 if callable 0 if not callable or -1 if it's unknown """
128 if callable(getattr(objects
[0], index
)) :
134 security
.declarePublic('buildDate')
135 def buildDate(self
, dateOrString
) :
136 """ Return DateTime instance """
137 if type(dateOrString
) == StringType
:
138 return DateTime(dateOrString
)
142 security
.declarePrivate('listActions')
143 def listActions(self
, object=None) :
144 """ List action according to indexes """
146 actions
= list(self
._actions
)
148 if getattr(object, 'isAnObjectManager', False) :
149 request
= object.REQUEST
151 visible
= request
['PATH_INFO'].split('/') [-1] == 'calendar_view' and True or False
153 if hasattr(object, 'listNearestFolderContents') :
154 objects
= object.listNearestFolderContents()
155 elif hasattr(object, 'listFolderContents') :
156 objects
= object.listFolderContents()
158 objects
= object.objectValues()
163 for index
in [ index
for index
in self
.getCommonIndexes(objects
) ] :
164 ai
= ActionInformation( index
165 , title
= 'sort_by_index_%s' % index
166 , category
= 'additional_tabs'
167 , action
= 'string:${folder_url}/calendar_view?sortBy=' + index
173 security
.declarePublic('sortObjectsByDate')
174 def sortObjectsByDate(self
, objects
, index
) :
175 """Sort objects by date index
178 if callable(getattr(objects
[0], index
)) :
179 objects
.sort(lambda a
, b
: cmp(getattr(a
, index
)(), getattr(b
, index
)()))
181 objects
.sort(lambda a
, b
: cmp(getattr(a
, index
), getattr(b
, index
)))
184 security
.declarePublic('getWeeksList')
185 def getWeeksList(self
, objects
, index
, year
=2004, month
=5) :
186 """Creates a series of weeks, each of which contains an integer day number.
187 A day number of 0 means that day is in the previous or next month.
191 getIndexValue
= callable(getattr(objects
[0], index
)) and \
192 ( lambda ob
: getattr(ob
, index
)() ) or \
193 ( lambda ob
: getattr(ob
, index
) )
194 buildDate
= type(getIndexValue(objects
[0])) == StringType
and \
195 ( lambda date
: DateTime(date
) ) or \
196 ( lambda date
: date
)
199 for week
in calendar
.monthcalendar(year
, month
) :
209 obDate
= buildDate(getIndexValue(ob
))
210 if obDate
.year() == year
and obDate
.month() == month
and obDate
.day() == day
:
216 dayInfo
= {'day' : day
,
219 weekInfoList
.append(dayInfo
)
221 weekList
.append(weekInfoList
)
225 security
.declarePublic('getDays')
226 def getDays(self
, letters
=2):
227 """ Returns a list of days with the correct start day first """
228 return calendar
.weekheader(letters
).split()
230 security
.declarePublic('isToday')
231 def isToday(self
, year
, month
, day
) :
232 """ return True if date is Today False otherwise
235 if now
.day() == day
and now
.month() == month
and now
.year() == year
:
240 security
.declarePublic('getMonthName')
241 def getMonthName(self
, month
) :
242 """ return month name """
243 return calendar
.month_name
[month
]
245 security
.declarePublic('getNextMonth')
246 def getNextMonth(self
, year
, month
) :
247 """ return next month """
252 return {'year' : year
, 'month' : month
}
254 security
.declarePublic('getPreviousMonth')
255 def getPreviousMonth(self
, year
, month
) :
256 """ return previous month """
261 return {'year' : year
, 'month' : month
}
263 security
.declarePublic('getWeek')
264 def getWeek(self
, objects
, index
, year
=2004, month
=5, day
=24) :
265 """ return week info """
267 weeksList
= self
.getWeeksList(objects
, index
, year
=year
, month
=month
)
268 for weekIndex
in range(len(weeksList
)) :
269 if day
in [ entry
['day'] for entry
in weeksList
[weekIndex
] ] :
271 week
= weeksList
[weekIndex
]
274 entry
.update({'month' : month
})
276 previousWeeksList
= None
279 if week
[0]['day'] == 0 :
280 nbOfDaysInMonthBefore
= [ entry
['day'] for entry
in week
].count(0)
281 previousMonth
= self
.getPreviousMonth(year
, month
)
282 previousWeeksList
= self
.getWeeksList(objects
, index
, year
=previousMonth
['year'], month
=previousMonth
['month'])
283 daysInPreviousMonth
= previousWeeksList
[-1][:nbOfDaysInMonthBefore
]
284 for entry
in daysInPreviousMonth
:
285 entry
.update({'month' : previousMonth
['month']})
287 daysInThisMonth
= week
[nbOfDaysInMonthBefore
:]
289 week
= daysInPreviousMonth
+ daysInThisMonth
290 elif week
[-1]['day'] == 0 :
291 nbOfDaysInMonthAfter
= [ entry
['day'] for entry
in week
].count(0)
292 nextMonth
= self
.getNextMonth(year
, month
)
293 nextWeeksList
= self
.getWeeksList(objects
, index
, year
=nextMonth
['year'], month
=nextMonth
['month'])
294 daysInNextMonth
= nextWeeksList
[0][-nbOfDaysInMonthAfter
:]
295 for entry
in daysInNextMonth
:
296 entry
.update({'month' : nextMonth
['month']})
298 daysInThisMonth
= week
[:7 - nbOfDaysInMonthAfter
]
300 week
= daysInThisMonth
+ daysInNextMonth
305 previousStartDay
= {'year' : year
,
307 'day' : weeksList
[weekIndex
- 1][-1]}
308 elif previousWeeksList
:
309 previousStartDay
= {'year' : previousMonth
['year'],
310 'month' : previousMonth
['month'],
311 'day' : previousWeeksList
[-2][0]}
313 # the first week of this month begin on monday
314 previousMonth
= self
.getPreviousMonth(year
, month
)
315 previousWeeksList
= self
.getWeeksList([], index
, year
=previousMonth
['year'], month
=previousMonth
['month'])
316 previousStartDay
= {'year' : previousMonth
['year'],
317 'month' : previousMonth
['month'],
318 'day' : previousWeeksList
[-1][0]}
322 if weekIndex
< len(weeksList
) - 1 :
323 nextStartDay
= {'year' : year
,
325 'day' : weeksList
[weekIndex
+ 1][0]}
327 nextStartDay
= {'year' : nextMonth
['year'],
328 'month' : nextMonth
['month'],
329 'day' : nextWeeksList
[1][0]}
331 # the last week of this month ends on sunday
332 nextMonth
= self
.getNextMonth(year
, month
)
333 nextWeeksList
= self
.getWeeksList([], index
, year
=nextMonth
['year'], month
=nextMonth
['month'])
334 nextStartDay
= {'year' : nextMonth
['year'],
335 'month' : nextMonth
['month'],
336 'day' : nextWeeksList
[0][0]}
339 return {'week' : week
,
340 'previousStartDay' : previousStartDay
,
341 'nextStartDay' : nextStartDay
}
343 security
.declarePublic('getWeekTable')
344 def getWeekTable(self
, week
, index
, indexIsCallable
) :
345 """ Utility method for transposing getWeek result
346 for an easy display in table.
349 weekMatrix
= [ [ [] for q
in range(96) ] for d
in range(7) ]
351 getIndexValue
= indexIsCallable
and \
352 ( lambda ob
: getattr(ob
, index
)() ) or \
353 ( lambda ob
: getattr(ob
, index
) )
354 reelRange
= self
.displayRange
[:]
355 for dayIndex
in range(7) :
356 dayInfo
= week
[dayIndex
]
357 for ob
in dayInfo
['objects'] :
358 date
= self
.buildDate(getIndexValue(ob
))
360 minutesAfterMidnight
= date
.hour() * 60 + date
.minute()
361 cellIndex
= minutesAfterMidnight
/ 15
363 if cellIndex
< reelRange
[0] :
364 reelRange
[0] = cellIndex
365 elif cellIndex
>= reelRange
[1] :
366 reelRange
[1] = cellIndex
+ 1
368 weekMatrix
[dayIndex
][cellIndex
].append(ob
)
370 reelRange
[0] = reelRange
[0] - reelRange
[0] % 4
371 reelRange
[1] = reelRange
[1] + reelRange
[1] % 4
373 return {'weekMatrix' : weekMatrix
, 'range' : reelRange
}
376 security
.declarePublic('getEventHeight')
377 def getEventHeight(self
, event
) :
378 """ Return event height
384 duration
= ( ee
.hour() * 60 + ee
.minute() ) - ( es
.hour() * 60 + es
.minute() )
385 height
= duration
/ 15
389 # raise ValueError, "%s event duration is more than 1 day" % event.id
391 InitializeClass(CalendarTool
)