[FTR] Electronic agreement
[cds-indico.git] / indico / MaKaC / plugins / Collaboration / base.py
blobd5e5a1909566885968c5868a8d1c730c0c83f588
1 # -*- coding: utf-8 -*-
2 ##
3 ##
4 ## This file is part of CDS Indico.
5 ## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6 ##
7 ## CDS Indico is free software; you can redistribute it and/or
8 ## modify it under the terms of the GNU General Public License as
9 ## published by the Free Software Foundation; either version 2 of the
10 ## License, or (at your option) any later version.
12 ## CDS Indico is distributed in the hope that it will be useful, but
13 ## WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ## General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20 import time
21 from persistent import Persistent
22 from hashlib import md5
23 from MaKaC.common.Counter import Counter
24 from MaKaC.common.utils import formatDateTime, parseDateTime
25 from MaKaC.common.timezoneUtils import getAdjustedDate, setAdjustedDate,\
26 datetimeToUnixTimeInt
27 from MaKaC.webinterface import wcomponents, urlHandlers
28 from MaKaC.plugins import PluginsHolder
29 from MaKaC.errors import MaKaCError
30 from MaKaC.services.interface.rpc.common import ServiceError
31 from MaKaC.common.timezoneUtils import nowutc
32 from MaKaC.common.logger import Logger
33 from MaKaC.common.indexes import IndexesHolder
34 from MaKaC.plugins.Collaboration.collaborationTools import CollaborationTools,\
35 MailTools
36 from MaKaC.conference import Observer
37 from MaKaC.common.contextManager import ContextManager
38 from MaKaC.webinterface.common.tools import hasTags
39 from MaKaC.plugins.Collaboration import mail
40 from MaKaC.common.mail import GenericMailer
41 import os, inspect
42 import MaKaC.plugins.Collaboration as Collaboration
43 from MaKaC.i18n import _
44 from MaKaC.common.fossilize import Fossilizable, fossilizes
45 from MaKaC.common.externalOperationsManager import ExternalOperationsManager
47 from MaKaC.plugins.Collaboration.fossils import ICSErrorBaseFossil, ICSSanitizationErrorFossil,\
48 ICSBookingBaseConfModifFossil, ICSBookingBaseIndexingFossil,\
49 ISpeakerWrapperBaseFossil
51 class CSBookingManager(Persistent, Observer):
52 """ Class for managing the bookins of a meeting.
53 It will store the list of bookings. Adding / removing / editing bookings should be through this class.
54 """
56 _shouldBeTitleNotified = True
57 _shouldBeDateChangeNotified = True
58 _shouldBeLocationChangeNotified = True
59 _shouldBeDeletionNotified = True
61 def __init__(self, conf):
62 """ Constructor for the CSBookingManager class.
63 conf: a Conference object. The meeting that owns this CSBookingManager.
64 """
65 self._conf = conf
66 self._counter = Counter(1)
67 # a dict where the bookings will be stored. The key will be the booking id, the value a CSBookingBase object.
68 self._bookings = {}
70 # an index of bookings by type. The key will be a booking type (string), the value a list of booking id
71 self._bookingsByType = {}
73 # a list of ids with hidden bookings
74 self._hiddenBookings = set()
76 # an index of video services managers for each plugin. key: plugin name, value: list of users
77 self._managers = {}
79 # list of speaker wrapper for a conference
80 self._speakerWrapperList = []
81 self.updateSpeakerWrapperList()
83 def getOwner(self):
84 """ Returns the Conference (the meeting) that owns this CSBookingManager object.
85 """
86 return self._conf
88 def isCSAllowed(self, user = None):
89 """ Returns if the associated event should display a Video Services tab
90 This can depend on the kind of event (meeting, lecture, conference), on the equipment of the room...
91 If a user is provided, we will take into account if the user can manage the plugin (for example,
92 an event manager cannot manage an admin-only plugin)
93 """
94 pluginsPerEventType = CollaborationTools.getCollaborationPluginType().getOption("pluginsPerEventType").getValue()
95 if pluginsPerEventType:
96 for plugin in pluginsPerEventType[self._conf.getType()]:
97 if plugin.isActive() and (user is None or CollaborationTools.canUserManagePlugin(self._conf, plugin, user)):
98 return True
99 return False
101 def getAllowedPlugins(self):
102 """ Returns a list of allowed plugins (Plugin objects) for this event.
103 Only active plugins are returned.
104 This can depend on the kind of event (meeting, lecture, conference), on the equipment of the room...
106 pluginsPerEventType = CollaborationTools.getCollaborationPluginType().getOption("pluginsPerEventType").getValue()
107 if pluginsPerEventType is not None:
108 allowedForThisEvent = pluginsPerEventType[self._conf.getType()]
109 return [plugin for plugin in allowedForThisEvent if plugin.isActive()]
112 def getBookingList(self, sorted = False, filterByType = None, notify = False, onlyPublic = False):
113 """ Returns a list of all the bookings.
114 If sorted = True, the list of bookings will be sorted by id.
115 If filterByType = None, all bookings are returned.
116 Otherwise, just those of the type "filterByType" if filterByType is a string,
117 or if it is a list of strings, those who have a type included in filterByType.
119 if not hasattr(self, "_bookingsByType"): #TODO: remove when safe
120 self._bookingsByType = {}
122 if filterByType is not None:
123 if type(filterByType) == str:
124 keys = self._bookingsByType.get(filterByType, [])
125 if type(filterByType) == list:
126 keys = []
127 for pluginName in filterByType:
128 keys.extend(self._bookingsByType.get(pluginName, []))
129 else:
130 keys = self._bookings.keys()
132 if onlyPublic and self.getHiddenBookings():
133 keys = set(keys)
134 keys = keys.difference(self.getHiddenBookings())
135 keys = list(keys)
137 if sorted:
138 keys.sort(key = lambda k: int(k))
140 bookingList = [self._bookings[k] for k in keys]
142 #we notify all the bookings that they have been viewed. If a booking doesn't need to be viewed, nothing will happen
143 if notify:
144 for booking in bookingList:
145 if booking.needsToBeNotifiedOnView():
146 try:
147 booking._notifyOnView()
148 except Exception, e:
149 Logger.get('VideoServ').error("Exception while notifying to a booking that it is being viewed. Exception: " + str(e))
151 return bookingList
153 def getBooking(self, id):
154 """ Returns a booking given its id.
156 return self._bookings[id]
158 def getSingleBooking(self, type, notify = False):
159 """ Returns the single booking of a plugin who only allows one booking.
160 type: a string with the name of the plugin
161 If the plugin actually allows multiple bookings, an exception will be thrown
162 If the plugin has no booking, None will be returned.
163 Otherwise the booking will be returned
165 if CollaborationTools.getCSBookingClass(type)._allowMultiple:
166 raise CollaborationException("Plugin type " + str(type) + " is not a single-booking plugin")
167 blist = self._bookingsByType.get(type,[])
168 if blist:
169 booking = self._bookings[blist[0]]
170 if notify:
171 try:
172 booking._notifyOnView()
173 except Exception, e:
174 Logger.get('VideoServ').error("Exception while notifying to a booking that it is being viewed. Exception: " + str(e))
175 return booking
176 else:
177 return None
179 def getHiddenBookings(self):
180 if not hasattr(self, '_hiddenBookings'):
181 self._hiddenBookings = set()
182 return self._hiddenBookings
184 def hasBookings(self):
185 return len(self._bookings) > 0
187 def canCreateBooking(self, type):
188 """ Returns if it's possible to create a booking of this given type
190 if not CollaborationTools.getCSBookingClass(type)._allowMultiple:
191 return len(self.getBookingList(filterByType = type)) == 0
192 return True
195 def addBooking(self, booking):
196 """ Adds an existing booking to the list of bookings.
198 booking: The existing booking to be added.
200 self._bookings[booking.getId()] = booking
201 self._bookingsByType.setdefault(booking.getType(),[]).append(booking.getId())
202 if booking.isHidden():
203 self.getHiddenBookings().add(booking.getId())
204 self._indexBooking(booking)
205 self._notifyModification()
207 def createBooking(self, bookingType, bookingParams = {}):
208 """ Adds a new booking to the list of bookings.
209 The id of the new booking is auto-generated incrementally.
210 After generating the booking, its "performBooking" method will be called.
212 bookingType: a String with the booking's plugin. Example: "DummyPlugin", "EVO"
213 bookingParams: a dictionary with the parameters necessary to create the booking.
214 "create the booking" usually means Indico deciding if the booking can take place.
215 if "startDate" and "endDate" are among the keys, they will be taken out of the dictionary.
217 if self.canCreateBooking(bookingType):
218 newBooking = CollaborationTools.getCSBookingClass(bookingType)(bookingType, self._conf)
220 error = newBooking.setBookingParams(bookingParams)
222 if isinstance(error, CSErrorBase):
223 return error
224 elif error:
225 raise CollaborationServiceException("Problem while creating a booking of type " + bookingType)
226 else:
227 newId = self._getNewBookingId()
228 newBooking.setId(newId)
229 createResult = newBooking._create()
230 if isinstance(createResult, CSErrorBase):
231 return createResult
232 else:
233 self._bookings[newId] = newBooking
234 self._bookingsByType.setdefault(bookingType,[]).append(newId)
235 if newBooking.isHidden():
236 self.getHiddenBookings().add(newId)
237 self._indexBooking(newBooking)
238 self._notifyModification()
240 if MailTools.needToSendEmails(bookingType):
241 newBooking._sendNotifications('new')
243 return newBooking
244 else:
245 #we raise an exception because the web interface should take care of this never actually happening
246 raise CollaborationServiceException(bookingType + " only allows to create 1 booking per event")
248 def _indexBooking(self, booking):
249 if booking.shouldBeIndexed():
250 indexes = self._getIndexList(booking)
251 for index in indexes:
252 index.indexBooking(booking)
254 def changeBooking(self, bookingId, bookingParams):
256 Changes the bookingParams of a CSBookingBase object.
257 After updating the booking, its 'performBooking' method will be called.
258 bookingId: the id of the CSBookingBase object to change
259 bookingParams: a dictionary with the new parameters that will modify the booking
260 'modify the booking' can mean that maybe the booking will be rejected with the new parameters.
261 if 'startDate' and 'endDate' are among the keys, they will be taken out of the dictionary.
264 booking = self.getBooking(bookingId)
267 oldStartDate = booking.getStartDate()
268 oldModificationDate = booking.getModificationDate()
269 oldBookingParams = booking.getBookingParams() #this is a copy so it's ok
271 error = booking.setBookingParams(bookingParams)
272 if isinstance(error, CSSanitizationError):
273 return error
274 elif error:
275 CSBookingManager._rollbackChanges(booking, oldBookingParams, oldModificationDate)
276 if isinstance(error, CSErrorBase):
277 return error
278 raise CollaborationServiceException("Problem while modifying a booking of type " + booking.getType())
279 else:
280 modifyResult = booking._modify(oldBookingParams)
281 if isinstance(modifyResult, CSErrorBase):
282 CSBookingManager._rollbackChanges(booking, oldBookingParams, oldModificationDate)
283 return modifyResult
284 else:
285 modificationDate = nowutc()
286 booking.setModificationDate(modificationDate)
288 if booking.isHidden():
289 self.getHiddenBookings().add(booking.getId())
290 elif booking.getId() in self.getHiddenBookings():
291 self.getHiddenBookings().remove(booking.getId())
293 self._changeStartDateInIndex(booking, oldStartDate, booking.getStartDate())
294 self._changeModificationDateInIndex(booking, oldModificationDate, modificationDate)
296 if booking.hasAcceptReject():
297 if booking.getAcceptRejectStatus() is not None:
298 booking.clearAcceptRejectStatus()
299 self._addToPendingIndex(booking)
301 self._notifyModification()
303 if MailTools.needToSendEmails(booking.getType()):
304 booking._sendNotifications('modify')
306 return booking
308 @classmethod
309 def _rollbackChanges(cls, booking, oldBookingParams, oldModificationDate):
310 booking.setBookingParams(oldBookingParams)
311 booking.setModificationDate(oldModificationDate)
313 def _changeConfTitleInIndex(self, booking, oldTitle, newTitle):
314 if booking.shouldBeIndexed():
315 indexes = self._getIndexList(booking)
316 for index in indexes:
317 index.changeEventTitle(booking, oldTitle, newTitle)
319 def _changeStartDateInIndex(self, booking, oldStartDate, newStartDate):
320 if booking.shouldBeIndexed() and booking.hasStartDate():
321 indexes = self._getIndexList(booking)
322 for index in indexes:
323 index.changeStartDate(booking, oldStartDate, newStartDate)
325 def _changeModificationDateInIndex(self, booking, oldModificationDate, newModificationDate):
326 if booking.shouldBeIndexed():
327 indexes = self._getIndexList(booking)
328 for index in indexes:
329 index.changeModificationDate(booking, oldModificationDate, newModificationDate)
331 def _changeConfStartDateInIndex(self, booking, oldConfStartDate, newConfStartDate):
332 if booking.shouldBeIndexed():
333 indexes = self._getIndexList(booking)
334 for index in indexes:
335 index.changeConfStartDate(booking, oldConfStartDate, newConfStartDate)
337 def removeBooking(self, id):
338 """ Removes a booking given its id.
340 booking = self.getBooking(id)
341 bookingType = booking.getType()
343 removeResult = booking._delete()
344 if isinstance(removeResult, CSErrorBase):
345 return removeResult
346 else:
347 del self._bookings[id]
348 self._bookingsByType[bookingType].remove(id)
349 if not self._bookingsByType[bookingType]:
350 del self._bookingsByType[bookingType]
351 if id in self.getHiddenBookings():
352 self.getHiddenBookings().remove(id)
354 self._unindexBooking(booking)
356 self._notifyModification()
358 if MailTools.needToSendEmails(booking.getType()):
359 booking._sendNotifications('remove')
361 return booking
363 def _unindexBooking(self, booking):
364 if booking.shouldBeIndexed():
365 indexes = self._getIndexList(booking)
366 for index in indexes:
367 index.unindexBooking(booking)
369 def startBooking(self, id):
370 booking = self._bookings[id]
371 if booking.canBeStarted():
372 booking._start()
373 return booking
374 else:
375 raise CollaborationException(_("Tried to start booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be started."))
377 def stopBooking(self, id):
378 booking = self._bookings[id]
379 if booking.canBeStopped():
380 booking._stop()
381 return booking
382 else:
383 raise CollaborationException(_("Tried to stop booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be stopped."))
385 def checkBookingStatus(self, id):
386 booking = self._bookings[id]
387 if booking.hasCheckStatus():
388 result = booking._checkStatus()
389 if isinstance(result, CSErrorBase):
390 return result
391 else:
392 return booking
393 else:
394 raise ServiceError(message=_("Tried to check status of booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking does not support the check status service."))
396 def acceptBooking(self, id, user = None):
397 booking = self._bookings[id]
398 if booking.hasAcceptReject():
399 if booking.getAcceptRejectStatus() is None:
400 self._removeFromPendingIndex(booking)
401 booking.accept(user)
402 return booking
403 else:
404 raise ServiceError(message=_("Tried to accept booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be accepted."))
406 def rejectBooking(self, id, reason):
407 booking = self._bookings[id]
408 if booking.hasAcceptReject():
409 if booking.getAcceptRejectStatus() is None:
410 self._removeFromPendingIndex(booking)
411 booking.reject(reason)
412 return booking
413 else:
414 raise ServiceError("ERR-COLL10", _("Tried to reject booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be rejected."))
416 def _addToPendingIndex(self, booking):
417 if booking.shouldBeIndexed():
418 indexes = self._getPendingIndexList(booking)
419 for index in indexes:
420 index.indexBooking(booking)
422 def _removeFromPendingIndex(self, booking):
423 if booking.shouldBeIndexed():
424 indexes = self._getPendingIndexList(booking)
425 for index in indexes:
426 index.unindexBooking(booking)
428 def _getNewBookingId(self):
429 return self._counter.newCount()
431 def _getIndexList(self, booking):
432 """ Returns a list of BookingsIndex objects where the booking should be indexed.
433 This list includes:
434 -an index of all bookings
435 -an index of bookings of the given type
436 -an index of all bookings in the category of the event
437 -an index of booking of the given type, in the category of the event
438 If the booking type declared common indexes:
439 -the common indexes
440 -the common indexes for the category of the event
441 If the booking is of the Accept/Reject type
442 -same indexes as above, but only for pending bookings
444 collaborationIndex = IndexesHolder().getById("collaboration")
445 indexes = [collaborationIndex.getAllBookingsIndex(),
446 collaborationIndex.getIndex(booking.getType())]
448 for commonIndexName in booking.getCommonIndexes():
449 indexes.append(collaborationIndex.getIndex(commonIndexName))
451 if booking.hasAcceptReject() and booking.getAcceptRejectStatus() is None:
452 indexes.extend(self._getPendingIndexList(booking))
454 return indexes
456 def _getPendingIndexList(self, booking):
457 collaborationIndex = IndexesHolder().getById("collaboration")
458 indexes = [collaborationIndex.getIndex("all_pending"),
459 collaborationIndex.getIndex(booking.getType() + "_pending")]
461 for commonIndexName in booking.getCommonIndexes():
462 indexes.append(collaborationIndex.getIndex(commonIndexName + "_pending"))
464 return indexes
466 def getManagers(self):
467 if not hasattr(self, "_managers"):
468 self._managers = {}
469 return self._managers
471 def addPluginManager(self, plugin, user):
472 #TODO: use .linkTo on the user. To be done when the list of roles of a user is actually needed for smth...
473 self.getManagers().setdefault(plugin, []).append(user)
474 self._notifyModification()
476 def removePluginManager(self, plugin, user):
477 #TODO: use .unlinkTo on the user. To be done when the list of roles of a user is actually needed for smth...
478 if user in self.getManagers().setdefault(plugin,[]):
479 self.getManagers()[plugin].remove(user)
480 self._notifyModification()
482 def getVideoServicesManagers(self):
483 return self.getManagers().setdefault('all', [])
485 def isVideoServicesManager(self, user):
486 return user in self.getManagers().setdefault('all', [])
488 def getPluginManagers(self, plugin):
489 return self.getManagers().setdefault(plugin, [])
491 def isPluginManager(self, plugin, user):
492 return user in self.getManagers().setdefault(plugin, [])
494 def getAllManagers(self):
495 """ Returns a list with all the managers, no matter their type
496 The returned list is not ordered.
498 managers = set()
499 for managerList in self.getManagers().itervalues():
500 managers = managers.union(managerList)
501 return list(managers)
503 def isPluginManagerOfAnyPlugin(self, user):
504 #TODO: this method is not optimal. to be optimal, we should store somewhere an index where the key
505 #is the user, and the value is a list of plugins where they are managers.
506 #this could be done with .getLinkTo, but we would need to change the .linkTo method to add extra information
507 #(since we cannot create a role for each plugin)
508 if self.isVideoServicesManager(user):
509 return True
510 else:
511 for plugin in self.getManagers().iterkeys():
512 if self.isPluginManager(plugin, user):
513 return True
514 return False
516 def notifyTitleChange(self, oldTitle, newTitle):
517 """ Notifies the CSBookingManager that the title of the event (meeting) it's attached to has changed.
518 The CSBookingManager will reindex all its bookings in the event title index.
519 This method will be called by the event (meeting) object
521 for booking in self.getBookingList():
522 try:
523 self._changeConfTitleInIndex(booking, oldTitle, newTitle)
524 except Exception, e:
525 Logger.get('VideoServ').exception("Exception while reindexing a booking in the event title index because its event's title changed: " + str(e))
527 def notifyInfoChange(self):
528 self.updateSpeakerWrapperList()
530 def notifyEventDateChanges(self, oldStartDate = None, newStartDate = None, oldEndDate = None, newEndDate = None):
531 """ Notifies the CSBookingManager that the start and / or end dates of the event it's attached to have changed.
532 The CSBookingManager will change the dates of all the bookings that want to be updated.
533 If there are problems (such as a booking not being able to be modified)
534 it will write a list of strings describing the problems in the 'dateChangeNotificationProblems' context variable.
535 (each string is produced by the _booking2NotifyProblem method).
536 This method will be called by the event (meeting) object.
538 startDateChanged = oldStartDate is not None and newStartDate is not None and not oldStartDate == newStartDate
539 endDateChanged = oldEndDate is not None and newEndDate is not None and not oldEndDate == newEndDate
540 someDateChanged = startDateChanged or endDateChanged
542 Logger.get("VideoServ").info("""CSBookingManager: starting notifyEventDateChanges. Arguments: confId=%s, oldStartDate=%s, newStartDate=%s, oldEndDate=%s, newEndDate=%s""" %
543 (str(self._conf.getId()), str(oldStartDate), str(newStartDate), str(oldEndDate), str(newEndDate)))
545 if someDateChanged:
546 problems = []
547 for booking in self.getBookingList():
548 if booking.hasStartDate():
549 if startDateChanged:
550 try:
551 self._changeConfStartDateInIndex(booking, oldStartDate, newStartDate)
552 except Exception, e:
553 Logger.get('VideoServ').error("Exception while reindexing a booking in the event start date index because its event's start date changed: " + str(e))
555 if booking.needsToBeNotifiedOfDateChanges():
556 Logger.get("VideoServ").info("""CSBookingManager: notifying date changes to booking %s of event %s""" %
557 (str(booking.getId()), str(self._conf.getId())))
558 oldBookingStartDate = booking.getStartDate()
559 oldBookingEndDate = booking.getEndDate()
560 oldBookingParams = booking.getBookingParams() #this is a copy so it's ok
562 if startDateChanged:
563 booking.setStartDate(oldBookingStartDate + (newStartDate - oldStartDate) )
564 if endDateChanged:
565 booking.setEndDate(oldBookingEndDate + (newEndDate - oldEndDate) )
567 rollback = False
568 modifyResult = None
569 try:
570 modifyResult = booking._modify(oldBookingParams)
571 if isinstance(modifyResult, CSErrorBase):
572 Logger.get('VideoServ').warning("""Error while changing the dates of booking %s of event %s after event dates changed: %s""" %
573 (str(booking.getId()), str(self._conf.getId()), modifyResult.getLogMessage()))
574 rollback = True
575 except Exception, e:
576 Logger.get('VideoServ').error("""Exception while changing the dates of booking %s of event %s after event dates changed: %s""" %
577 (str(booking.getId()), str(self._conf.getId()), str(e)))
578 rollback = True
580 if rollback:
581 booking.setStartDate(oldBookingStartDate)
582 booking.setEndDate(oldBookingEndDate)
583 problems.append(CSBookingManager._booking2NotifyProblem(booking, modifyResult))
584 elif startDateChanged:
585 self._changeStartDateInIndex(booking, oldBookingStartDate, booking.getStartDate())
587 if hasattr(booking, "notifyEventDateChanges"):
588 try:
589 booking.notifyEventDateChanges(oldStartDate, newStartDate, oldEndDate, newEndDate)
590 except Exception, e:
591 Logger.get('VideoServ').exception("Exception while notifying a plugin of an event date changed: " + str(e))
593 if problems:
594 ContextManager.get('dateChangeNotificationProblems')['Collaboration'] = [
595 'Some Video Services bookings could not be moved:',
596 problems,
597 'Go to [[' + str(urlHandlers.UHConfModifCollaboration.getURL(self.getOwner(), secure = CollaborationTools.isUsingHTTPS())) + ' the Video Services section]] to modify them yourself.'
601 def notifyTimezoneChange(self, oldTimezone, newTimezone):
602 """ Notifies the CSBookingManager that the timezone of the event it's attached to has changed.
603 The CSBookingManager will change the dates of all the bookings that want to be updated.
604 This method will be called by the event (Conference) object
606 return []
608 def notifyLocationChange(self):
609 for booking in self.getBookingList():
610 if hasattr(booking, "notifyLocationChange"):
611 try:
612 booking.notifyLocationChange()
613 except Exception, e:
614 Logger.get('VideoServ').exception("Exception while notifying a plugin of a location change: " + str(e))
616 @classmethod
617 def _booking2NotifyProblem(cls, booking, modifyError):
618 """ Turns a booking into a string used to tell the user
619 why a date change of a booking triggered by the event's start or end date change
620 went bad.
623 message = []
624 message.extend(["The dates of the ", booking.getType(), " booking"])
625 if booking.hasTitle():
626 message.extend([': "', booking._getTitle(), '" (', booking.getStartDateAsString(), ' - ', booking.getEndDateAsString(), ')'])
627 else:
628 message.extend([' ongoing from ', booking.getStartDateAsString(), ' to ', booking.getEndDateAsString(), ''])
630 message.append(' could not be changed.')
631 if modifyError and modifyError.getUserMessage():
632 message.extend([' Reason: ', modifyError.getUserMessage()])
633 return "".join(message)
636 def notifyDeletion(self):
637 """ Notifies the CSBookingManager that the Conference object it is attached to has been deleted.
638 The CSBookingManager will change the dates of all the bookings that want to be updated.
639 This method will be called by the event (Conference) object
641 for booking in self.getBookingList():
642 try:
643 removeResult = booking._delete()
644 if isinstance(removeResult, CSErrorBase):
645 Logger.get('VideoServ').warning("Error while deleting a booking of type %s after deleting an event: %s"%(booking.getType(), removeResult.getLogMessage() ))
646 self._unindexBooking(booking)
647 except Exception, e:
648 Logger.get('VideoServ').exception("Exception while deleting a booking of type %s after deleting an event: %s" % (booking.getType(), str(e)))
650 def getEventDisplayPlugins(self, sorted = False):
651 """ Returns a list of names (strings) of plugins which have been configured
652 as showing bookings in the event display page, and which have bookings
653 already (or previously) created in the event.
654 (does not check if the bookings are hidden or not)
657 pluginsWithEventDisplay = CollaborationTools.pluginsWithEventDisplay()
658 l = []
659 for pluginName in self._bookingsByType:
660 if pluginName in pluginsWithEventDisplay:
661 l.append(pluginName)
662 if sorted:
663 l.sort()
664 return l
666 def createTestBooking(self, bookingParams = {}):
667 """ Function that creates a 'test' booking for performance test.
668 Avoids to use any of the plugins except DummyPlugin
670 from MaKaC.plugins.Collaboration.DummyPlugin.collaboration import CSBooking as DummyBooking
671 bookingType = 'DummyPlugin'
672 newBooking = DummyBooking(bookingType, self._conf)
673 error = newBooking.setBookingParams(bookingParams)
674 if error:
675 raise CollaborationServiceException("Problem while creating a test booking")
676 else:
677 newId = self._getNewBookingId()
678 newBooking.setId(newId)
679 createResult = newBooking._create()
680 if isinstance(createResult, CSErrorBase):
681 return createResult
682 else:
683 self._bookings[newId] = newBooking
684 self._bookingsByType.setdefault(bookingType,[]).append(newId)
685 if newBooking.isHidden():
686 self.getHiddenBookings().add(newId)
687 self._indexBooking(newBooking)
688 self._notifyModification()
689 return newBooking
691 def _notifyModification(self):
692 self._p_changed = 1
694 def getSortedContributionSpeaker(self, exclusive):
695 ''' This method will create a dictionary by sorting the contribution/speakers
696 that they are in recording, webcast or in both.
697 bool: exclusive - if True, every dicts (recording, webcast, both) will
698 have different speaker list (no repetition allowed)
699 if an element is present in 'both', it will be deleted from
700 'recording and 'webcast'
702 returns dict = { 'recording': {}, 'webcast' : {}, 'both': {} }
705 recordingBooking = self.getSingleBooking("RecordingRequest")
706 webcastBooking = self.getSingleBooking("WebcastRequest")
708 dict = {}
709 if recordingBooking:
710 dict["recording"] = recordingBooking.getContributionSpeakerSingleBooking()
711 else:
712 dict["recording"] = {}
714 if webcastBooking:
715 dict["webcast"] = webcastBooking.getContributionSpeakerSingleBooking()
716 else:
717 dict["webcast"] = {}
719 contributions = {}
720 ''' Look for speaker intersections between 'recording' and 'webcast' dicts
721 and put them in 'both' dict. Additionally, if any intersection has been found,
722 we exclude them from the original dictionary.
724 for cont in dict["recording"].copy():
725 if cont in dict["webcast"].copy():
726 # Check if same contribution/speaker in 'recording' and 'webcast'
727 intersection = list(set(dict['recording'][cont]) & set(dict['webcast'][cont]))
728 if intersection:
729 contributions[cont] = intersection
731 #if exclusive is True, amd as we found same contribution/speaker,
732 #we delete them from 'recording' and 'webcast' dicts
733 if exclusive:
734 exclusion = list(set(dict['recording'][cont]) ^ set(contributions[cont]))
735 if not exclusion:
736 del dict["recording"][cont]
737 else:
738 dict["recording"][cont] = exclusion
740 exclusion = list(set(dict['webcast'][cont]) ^ set(contributions[cont]))
741 if not exclusion:
742 del dict["webcast"][cont]
743 else:
744 dict["webcast"][cont] = exclusion
746 dict["both"] = contributions
748 return dict
750 def getContributionSpeakerByType(self, requestType):
751 ''' Return a plain dict of contribution/speaker according to the requestType
752 if the request type is 'both', we need to merge the lists
754 dict = self.getSortedContributionSpeaker(False) # We want non exclusive dict
756 if requestType == "recording":
757 return dict['recording']
758 elif requestType == "webcast":
759 return dict['webcast']
760 elif requestType == "both":
761 #We merge the 3 dict 'recording', 'webcast' and 'both'
762 result = dict['webcast'].copy()
763 for elem in dict['recording']:
764 temp = dict['recording'][elem]
765 temp.extend(x for x in result.get(elem, []) if x not in temp)
766 result[elem] = temp
768 for elem in dict['both']:
769 temp = dict['both'][elem]
770 temp.extend(x for x in result.get(elem,[]) if x not in temp)
771 result[elem] = temp
773 return result
774 else:
775 return {}
777 def updateSpeakerWrapperList(self):
778 SWList = []
779 contributions = self.getSortedContributionSpeaker(True)
780 requestType = ['recording', 'webcast', 'both']
782 for type in requestType:
783 for cont in contributions[type]:
784 for spk in contributions[type][cont]:
785 sw = self.getSpeakerWrapperByUniqueId("%s.%s"%(cont, spk.getId()))
786 if sw:
787 if not sw.getObject().getEmail():
788 if sw.getStatus() != SpeakerStatusEnum.SIGNED and \
789 sw.getStatus() != SpeakerStatusEnum.FROMFILE and \
790 sw.getStatus() != SpeakerStatusEnum.REFUSED:
791 sw.setStatus(SpeakerStatusEnum.NOEMAIL)
792 elif sw.getStatus() == SpeakerStatusEnum.NOEMAIL:
793 sw.setStatus(SpeakerStatusEnum.NOTSIGNED)
794 sw.setRequestType(type)
795 SWList.append(sw)
796 else:
797 newSw = SpeakerWrapper(spk, cont, type)
798 if not newSw.getObject().getEmail():
799 newSw.setStatus(SpeakerStatusEnum.NOEMAIL)
800 SWList.append(newSw)
802 self._speakerWrapperList = SWList
804 def getSpeakerWrapperList(self):
805 if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe
806 self._speakerWrapperList = []
808 return self._speakerWrapperList
810 def getSpeakerWrapperByUniqueId(self, id):
812 if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe
813 return None
815 for spkWrap in self._speakerWrapperList:
816 if spkWrap.getUniqueId() == id:
817 return spkWrap
819 return None
821 def areSignatureCompleted(self):
822 value = True;
823 for spkWrap in self._speakerWrapperList:
824 if spkWrap.getStatus() != SpeakerStatusEnum.FROMFILE and \
825 spkWrap.getStatus() != SpeakerStatusEnum.SIGNED:
826 value = False;
828 return value
830 def getSpeakerWrapperListByStatus(self, status):
831 '''Return a list of SpeakerWrapper matching the status.
833 list = []
834 for spkWrap in self._speakerWrapperList:
835 if spkWrap.getStatus() == status:
836 list.append(spkWrap)
838 return list
840 def getSpeakerEmailByUniqueId(self, id, user):
841 ''' Return the email of a speaker according to the uniqueId.
842 id: uniqueId of the speaker wrapper.
843 user: user object of the sender of the emails, in order to check the rights.
846 canManageRequest = CollaborationTools.getRequestTypeUserCanManage(self._conf, user)
847 requestTypeAccepted = ""
849 if canManageRequest == "recording":
850 requestTypeAccepted = ["recording"]
851 elif canManageRequest == "webcast":
852 requestTypeAccepted = ["webcast"]
853 elif canManageRequest == "both":
854 requestTypeAccepted = ["recording", "webcast", "both"]
856 list = []
857 for spkWrap in self._speakerWrapperList:
858 if spkWrap.getUniqueId() == id and \
859 spkWrap.hasEmail() and \
860 spkWrap.getStatus() != SpeakerStatusEnum.SIGNED and \
861 spkWrap.getStatus() != SpeakerStatusEnum.FROMFILE and \
862 spkWrap.getRequestType() in requestTypeAccepted:
864 list.append(spkWrap.getObject().getEmail())
866 return list
868 def isAnyRequestAccepted(self):
870 Return true if at least one between recording and webcast request
871 has been accepted.
873 value = False
874 rr = self.getSingleBooking("RecordingRequest")
875 wr = self.getSingleBooking("WebcastRequest")
877 if rr:
878 value = rr.getAcceptRejectStatus()
880 if wr:
881 value = value or wr.getAcceptRejectStatus()
883 return value
885 def isContributionReadyToBePublished(self, contId):
886 if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe
887 return False
889 exists = False
890 for spkWrap in self._speakerWrapperList:
891 if spkWrap.getContId() == contId:
892 exists = True
893 if spkWrap.getStatus() != SpeakerStatusEnum.SIGNED and \
894 spkWrap.getStatus() != SpeakerStatusEnum.FROMFILE:
895 return False
897 #The list has to have at least one spkWrap with the given contId
898 return exists
900 class CSBookingBase(Persistent, Fossilizable):
901 fossilizes(ICSBookingBaseConfModifFossil, ICSBookingBaseIndexingFossil)
903 """ Base class that represents a Collaboration Systems booking.
904 Every Collaboration plugin will have to implement this class.
905 In the base class are gathered all the functionalities / elements that are common for all plugins.
906 A booking is Persistent (DateChangeObserver inherits from Persistent) so it will be stored in the database.
907 Also, every CSBookingBase object in the server will be mirrored by a Javascript object in the client, through "Pickling".
909 Every class that implements the CSBookingBase has to declare the following class attributes:
910 _hasStart : True if the plugin has a "start" concept. Otherwise, the "start" button will not appear, etc.
911 _hasStop : True if the plugin has a "stop" concept. Otherwise, the "stop" button will not appear, etc.
912 _hasCheckStatus: True if the plugin has a "check status" concept. Otherwise, the "check status" button will not appear, etc.
913 _hasAcceptReject: True if the plugin has a "accept or reject" concept. Otherwise, the "accept" and "reject" buttons will not appear, etc.
914 _requiresServerCallForStart : True if we should notify the server when the user presses the "start" button.
915 _requiresServerCallForStop : True if we should notify the server when the user presses the "stop" button.
916 _requiresClientCallForStart : True if the browser should execute some JS action when the user presses the "start" button.
917 _requiresClientCallForStop : True if the browser should execute some JS action when the user presses the "stop" button.
918 _needsBookingParamsCheck : True if the booking parameters should be checked after the booking is added / edited.
919 If True, the _checkBookingParams method will be called by the setBookingParams method.
920 _needsToBeNotifiedOnView: True if the booking object needs to be notified (through the "notifyOnView" method)
921 when the user "sees" the booking, for example when returning the list of bookings.
922 _canBeNotifiedOfEventDateChanges: True if bookings of this type should be able to be notified
923 of their owner Event changing start date, end date or timezone.
924 _allowMultiple: True if this booking type allows more than 1 booking per event.
927 _hasStart = False
928 _hasStop = False
929 _hasCheckStatus = False
930 _hasAcceptReject = False
931 _hasStartStopAll = False
932 _requiresServerCallForStart = False
933 _requiresServerCallForStop = False
934 _requiresClientCallForStart = False
935 _requiresClientCallForStop = False
936 _needsBookingParamsCheck = False
937 _needsToBeNotifiedOnView = False
938 _canBeNotifiedOfEventDateChanges = True
939 _allowMultiple = True
940 _shouldBeIndexed = True
941 _commonIndexes = []
942 _hasStartDate = True
943 _hasEventDisplay = False
944 _hasTitle = False
945 _adminOnly = False
947 def __init__(self, bookingType, conf):
948 """ Constructor for the CSBookingBase class.
949 id: a string with the id of the booking
950 bookingType: a string with the type of the booking. Example: "DummyPlugin", "EVO"
951 conf: a Conference object to which this booking belongs (through the CSBookingManager object). The meeting of this booking.
952 startTime: TODO
953 endTime: TODO
955 Other attributes initialized by this constructor:
956 -_bookingParams: the parameters necessary to perform the booking.
957 The plugins will decide if the booking gets authorized or not depending on this.
958 Needs to be defined by the implementing class, as keys with empty values.
959 -_startingParams: the parameters necessary to start the booking.
960 They will be used on the client for the local start action.
961 Needs to be defined by the implementing class, as keys with empty values.
962 -_statusMessage, _statusClass : they represent the status message (and its CSS class) that will be displayed.
963 The status of a booking can be, for example: "Booking Accepted" (in green), "Booking refused" (in red)
964 -_warning: A warning is a plugin-defined object, with information to show to the user when
965 the operation went well but we still have to show some info to the user.
966 -_canBeStarted: If its value is true, the "start" button for the booking will be able to be pushed.
967 It can be false if, for example:
968 + The plugin didn't like the booking parameters and doesn't give permission for the booking to be started,
969 + The booking has already been started, so the "start" button has to be faded in order not to be pressed twice.
970 -_canBeStopped: If its value is true, the "stop" button for the booking will be able to be pushed.
971 For example, before starting a booking the "stop" button for the booking will be faded.
972 -_permissionToStart : Even if the "start" button for a booking is able to be pushed, there may be cases where the booking should
973 not start. For example, if it's not the correct time yet.
974 In that case "permissionToStart" should be set to false so that the booking doesn't start.
975 -_permissionToStop: Same as permissionToStart. Sometimes the booking should not be allowed to stop even if the "stop" button is available.
977 self._id = None
978 self._type = bookingType
979 self._plugin = CollaborationTools.getPlugin(self._type)
980 self._conf = conf
981 self._warning = None
982 self._creationDate = nowutc()
983 self._modificationDate = nowutc()
984 self._creationDateTimestamp = int(datetimeToUnixTimeInt(self._creationDate))
985 self._modificationDateTimestamp = int(datetimeToUnixTimeInt(self._modificationDate))
986 self._startDate = None
987 self._endDate = None
988 self._startDateTimestamp = None
989 self._endDateTimestamp = None
990 self._statusMessage = ""
991 self._statusClass = ""
992 self._acceptRejectStatus = None #None = not yet accepted / rejected; True = accepted; False = rejected
993 self._rejectReason = ""
994 self._bookingParams = {}
995 self._canBeDeleted = True
996 self._canBeStarted = self._hasStart
997 self._canBeStopped = False
998 self._permissionToStart = False
999 self._permissionToStop = False
1000 self._needsToBeNotifiedOfDateChanges = self._canBeNotifiedOfEventDateChanges
1001 self._hidden = False
1003 setattr(self, "_" + bookingType + "Options", CollaborationTools.getPlugin(bookingType).getOptions())
1004 #NOTE: Should maybe notify the creation of a new booking, specially if it's a single booking
1005 # like that can update requestType of the speaker wrapper...
1007 def getId(self):
1008 """ Returns the internal, per-conference id of the booking.
1009 This attribute will be available in Javascript with the "id" identifier.
1011 return self._id
1013 def setId(self, id):
1014 """ Sets the internal, per-conference id of the booking
1016 self._id = id
1018 def getUniqueId(self):
1019 """ Returns an unique Id that identifies this booking server-wide.
1020 Useful for ExternalOperationsManager
1022 return "%scsbook%s" % (self.getConference().getUniqueId(), self.getId())
1024 def getType(self):
1025 """ Returns the type of the booking, as a string: "EVO", "DummyPlugin"
1026 This attribute will be available in Javascript with the "type" identifier.
1028 return self._type
1030 def getConference(self):
1031 """ Returns the owner of this CSBookingBase object, which is a Conference object representing the meeting.
1033 return self._conf
1035 def setConference(self, conf):
1036 """ Sets the owner of this CSBookingBase object, which is a Conference object representing the meeting.
1038 self._conf = conf
1040 def getWarning(self):
1041 """ Returns a warning attached to this booking.
1042 A warning is a plugin-defined object, with information to show to the user when
1043 the operation went well but we still have to show some info to the user.
1044 To be overloaded by plugins.
1046 if not hasattr(self, '_warning'):
1047 self._warning = None
1048 return self._warning
1050 def setWarning(self, warning):
1051 """ Sets a warning attached to this booking.
1052 A warning is a plugin-defined object, with information to show to the user when
1053 the operation went well but we still have to show some info to the user.
1054 To be overloaded by plugins.
1056 self._warning = warning
1058 def getCreationDate(self):
1059 """ Returns the date this booking was created, as a timezone localized datetime object
1061 if not hasattr(self, "_creationDate"): #TODO: remove when safe
1062 self._creationDate = nowutc()
1063 return self._creationDate
1065 def getAdjustedCreationDate(self, tz=None):
1066 """ Returns the booking creation date, adjusted to a given timezone.
1067 If no timezone is provided, the event's timezone is used
1069 return getAdjustedDate(self.getCreationDate(), self.getConference(), tz)
1071 def getCreationDateTimestamp(self):
1072 if not hasattr(object, "_creationDateTimestamp"): #TODO: remove when safe
1073 self._creationDateTimestamp = int(datetimeToUnixTimeInt(self._creationDate))
1074 return self._creationDateTimestamp
1076 def getModificationDate(self):
1077 """ Returns the date this booking was modified last
1079 if not hasattr(self, "_modificationDate"): #TODO: remove when safe
1080 self._modificationDate = nowutc()
1081 return self._modificationDate
1083 def getAdjustedModificationDate(self, tz=None):
1084 """ Returns the booking last modification date, adjusted to a given timezone.
1085 If no timezone is provided, the event's timezone is used
1087 return getAdjustedDate(self.getModificationDate(), self.getConference(), tz)
1089 def getModificationDateTimestamp(self):
1090 if not hasattr(object, "_modificationDateTimestamp"): #TODO: remove when safe
1091 self._modificationDateTimestamp = int(datetimeToUnixTimeInt(self._modificationDate))
1092 return self._modificationDateTimestamp
1094 def setModificationDate(self, date):
1095 """ Sets the date this booking was modified last
1097 self._modificationDate = date
1098 if date:
1099 self._modificationDateTimestamp = int(datetimeToUnixTimeInt(date))
1100 else:
1101 self._modificationDateTimestamp = None
1103 def getBookingsOfSameType(self, sorted = False):
1104 """ Returns a list of the bookings of the same type as this one (including this one)
1105 sorted: if true, bookings will be sorted by id
1107 return self._conf.getCSBookingManager().getBookingList(sorted, self._type)
1109 def getPlugin(self):
1110 """ Returns the Plugin object associated to this booking.
1112 return self._plugin
1114 def setPlugin(self, plugin):
1115 """ Sets the Plugin object associated to this booking.
1117 self._plugin = plugin
1119 def getPluginOptions(self):
1120 """ Utility method that returns the plugin options for this booking's type of plugin
1122 return self._plugin.getOptions()
1124 def getPluginOptionByName(self, optionName):
1125 """ Utility method that returns a plugin option, given its name, for this booking's type of plugin
1127 return self.getPluginOptions()[optionName]
1129 def getStartDate(self):
1130 """ Returns the start date as an datetime object with timezone information (adjusted to the meeting's timezone)
1132 return self._startDate
1134 def getAdjustedStartDate(self, tz=None):
1135 """ Returns the booking start date, adjusted to a given timezone.
1136 If no timezone is provided, the event's timezone is used
1138 if self.getStartDate():
1139 return getAdjustedDate(self.getStartDate(), self.getConference(), tz)
1140 else:
1141 return None
1143 def getStartDateTimestamp(self):
1144 if not hasattr(object, "_startDateTimestamp"): #TODO: remove when safe
1145 self._startDateTimestamp = int(datetimeToUnixTimeInt(self._startDate))
1146 return self._startDateTimestamp
1148 def setStartDateTimestamp(self, startDateTimestamp):
1149 self._startDateTimestamp = startDateTimestamp
1151 def getStartDateAsString(self):
1152 """ Returns the start date as a string, expressed in the meeting's timezone
1154 if self._startDate == None:
1155 return ""
1156 else:
1157 return formatDateTime(self.getAdjustedStartDate())
1159 def setStartDate(self, startDate):
1160 """ Sets the start date as an datetime object with timezone information (adjusted to the meeting's timezone)
1162 self._startDate = startDate
1163 if startDate:
1164 self._startDateTimestamp = int(datetimeToUnixTimeInt(startDate))
1165 else:
1166 self._startDateTimestamp = None
1168 def setStartDateFromString(self, startDateString):
1169 """ Sets the start date from a string. It is assumed that the date is expressed in the meeting's timezone
1171 if startDateString == "":
1172 self.setStartDate(None)
1173 else:
1174 try:
1175 self.setStartDate(setAdjustedDate(parseDateTime(startDateString), self._conf))
1176 except ValueError:
1177 raise CollaborationServiceException("startDate parameter (" + startDateString +" ) is in an incorrect format for booking with id: " + str(self._id))
1179 def getEndDate(self):
1180 """ Returns the end date as an datetime object with timezone information (adjusted to the meeting's timezone)
1182 return self._endDate
1184 def getAdjustedEndDate(self, tz=None):
1185 """ Returns the booking end date, adjusted to a given timezone.
1186 If no timezone is provided, the event's timezone is used
1188 return getAdjustedDate(self.getEndDate(), self.getConference(), tz)
1190 def getEndDateTimestamp(self):
1191 if not hasattr(object, "_endDateTimestamp"): #TODO: remove when safe
1192 self._endDateTimestamp = int(datetimeToUnixTimeInt(self._endDate))
1193 return self._endDateTimestamp
1195 def setEndDateTimestamp(self, endDateTimestamp):
1196 self._endDateTimestamp = endDateTimestamp
1197 def getEndDateAsString(self):
1198 """ Returns the start date as a string, expressed in the meeting's timezone
1200 if self._endDate == None:
1201 return ""
1202 else:
1203 return formatDateTime(self.getAdjustedEndDate())
1205 def setEndDate(self, endDate):
1206 """ Sets the start date as an datetime object with timezone information (adjusted to the meeting's timezone)
1208 self._endDate = endDate
1209 if endDate:
1210 self._endDateTimestamp = int(datetimeToUnixTimeInt(endDate))
1211 else:
1212 self._endDateTimestamp = None
1214 def setEndDateFromString(self, endDateString):
1215 """ Sets the start date from a string. It is assumed that the date is expressed in the meeting's timezone
1217 if endDateString == "":
1218 self.setEndDate(None)
1219 else:
1220 try:
1221 self.setEndDate(setAdjustedDate(parseDateTime(endDateString), self._conf))
1222 except ValueError:
1223 raise CollaborationServiceException("endDate parameter (" + endDateString +" ) is in an incorrect format for booking with id: " + str(self._id))
1225 def getStatusMessage(self):
1226 """ Returns the status message as a string.
1227 This attribute will be available in Javascript with the "statusMessage"
1229 return _(self._statusMessage)
1231 def setStatusMessage(self, statusMessage):
1232 """ Sets the status message as a string.
1233 This attribute will be available in Javascript with the "statusMessage"
1235 self._statusMessage = statusMessage
1237 def getStatusClass(self):
1238 """ Returns the status message CSS class as a string.
1239 This attribute will be available in Javascript with the "statusClass"
1241 if not hasattr(self, "_statusClass"): #TODO: remove when safe
1242 self._statusClass = ""
1243 return self._statusClass
1245 def setStatusClass(self, statusClass):
1246 """ Sets the status message CSS class as a string.
1247 This attribute will be available in Javascript with the "statusClass"
1249 self._statusClass = statusClass
1251 def accept(self, user = None):
1252 """ Sets this booking as accepted
1254 self._acceptRejectStatus = True
1255 self._accept(user)
1257 def reject(self, reason):
1258 """ Sets this booking as rejected, and stores the reason
1260 self._acceptRejectStatus = False
1261 self._rejectReason = reason
1262 self._reject()
1264 def clearAcceptRejectStatus(self):
1265 """ Sets back the accept / reject status to None
1267 self._acceptRejectStatus = None
1269 def getAcceptRejectStatus(self):
1270 """ Returns the Accept/Reject status of the booking
1271 This attribute will be available in Javascript with the "acceptRejectStatus"
1272 Its value will be:
1273 -None if the booking has not been accepted or rejected yet,
1274 -True if it has been accepted,
1275 -False if it has been rejected
1277 if not hasattr(self, "_acceptRejectStatus"):
1278 self._acceptRejectStatus = None
1279 return self._acceptRejectStatus
1281 def getRejectReason(self):
1282 """ Returns the rejection reason.
1283 This attribute will be available in Javascript with the "rejectReason"
1285 if not hasattr(self, "_rejectReason"):
1286 self._rejectReason = ""
1287 return self._rejectReason
1289 def getBookingParams(self):
1290 """ Returns a dictionary with the booking params.
1291 This attribute will be available in Javascript with the "bookingParams"
1293 If self._bookingParams has not been set by the implementing class, an exception is thrown.
1295 Support for "complex" parameters, that are not defined in the self._bookingParams dict, but can
1296 be retrieved through getter methods.
1297 If a subclass defines a class attributes called _complexParameters (a list of strings),
1298 parameter names that are in this list will also be included in the returned dictionary.
1299 Their value will be retrieved by calling the corresponding getXXX methods
1300 but instead the inheriting class's setXXX method will be called.
1301 Example: _complexParameters = ["communityName", "accessPassword", "hasAccessPassword"] correspond
1302 to the methods getCommunityName, getAccessPassword, getHasAccessPassword.
1303 If you include a parameter in the _complexParameters list, you always have to implement the corresponding getter method.
1305 bookingParams = {}
1306 for k, v in self.__class__._simpleParameters.iteritems():
1307 if k in self._bookingParams:
1308 value = self._bookingParams[k]
1309 else:
1310 value = v[1] #we use the default value
1311 if v[0] is bool and value is True: #we assume it will be used in a single checkbox
1312 value = ["yes"]
1313 if value is not False: #we do not include False, it means the single checkbox is not checked
1314 bookingParams[k] = value
1316 if hasattr(self.__class__, "_complexParameters") and len(self.__class__._complexParameters) > 0:
1317 getterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('get')))
1318 for paramName in self.__class__._complexParameters:
1319 getMethodName = 'get' + paramName[0].upper() + paramName[1:]
1320 if getMethodName in getterMethods:
1321 bookingParams[paramName] = getterMethods[getMethodName]()
1322 else:
1323 raise CollaborationServiceException("Tried to retrieve complex parameter " + str(paramName) + " but the corresponding getter method " + getMethodName + " is not implemented")
1325 bookingParams["startDate"] = self.getStartDateAsString()
1326 bookingParams["endDate"] = self.getEndDateAsString()
1327 if self.needsToBeNotifiedOfDateChanges():
1328 bookingParams["notifyOnDateChanges"] = ["yes"]
1329 if self.isHidden():
1330 bookingParams["hidden"] = ["yes"]
1332 return bookingParams
1335 def getBookingParamByName(self, paramName):
1336 if paramName in self.__class__._simpleParameters:
1337 if not paramName in self._bookingParams:
1338 self._bookingParams[paramName] = self.__class__._simpleParameters[paramName][1]
1339 return self._bookingParams[paramName]
1340 elif hasattr(self.__class__, "_complexParameters") and paramName in self.__class__._complexParameters:
1341 getterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('get')))
1342 getMethodName = 'get' + paramName[0].upper() + paramName[1:]
1343 if getMethodName in getterMethods:
1344 return getterMethods[getMethodName]()
1345 else:
1346 raise CollaborationServiceException("Tried to retrieve complex parameter " + str(paramName) + " but the corresponding getter method " + getMethodName + " is not implemented")
1347 else:
1348 raise CollaborationServiceException("Tried to retrieve parameter " + str(paramName) + " but this parameter does not exist")
1350 def getContributionSpeakerSingleBooking(self):
1351 ''' Return a dictionnary with the contributions and their speakers that need to be recorded
1352 e.g: {contId:[Spk1Object, Spk2Object, Spk3Object], cont2:[Spk1Object]}...
1354 request = {}
1356 recordingTalksChoice = self.getBookingParams()["talks"] #either "all", "choose" or ""
1357 listTalksToRecord = self.getBookingParams()["talkSelection"]
1359 if self._conf.getType() == "simple_event":
1360 request[self._conf.getId()] = []
1361 for chair in self._conf.getChairList():
1362 request[self._conf.getId()].append(chair)
1363 else:
1364 for cont in self._conf.getContributionList():
1365 ''' We select the contributions that respect the following conditions:
1366 - They have Speakers assigned.
1367 - They are scheduled. (to discuss...)
1368 - They have been chosen for the recording request.
1370 if recordingTalksChoice != "choose" or cont.getId() in listTalksToRecord:
1371 if cont.isScheduled():
1372 request[cont.getId()] = []
1373 for spk in cont.getSpeakerList():
1374 request[cont.getId()].append(spk)
1376 return request
1378 def setBookingParams(self, params):
1379 """ Sets new booking parameters.
1380 params: a dict with key/value pairs with the new values for the booking parameters.
1381 If the plugin's _needsBookingParamsCheck is True, the _checkBookingParams() method will be called.
1382 This function will return False if all the checks were OK or if there were no checks, and otherwise will throw
1383 an exception or return a CSReturnedErrorBase error.
1385 Support for "complex" parameters, that are not defined in the self._bookingParams dict, but can
1386 be set through setter methods.
1387 If a subclass defines a class attributes called _complexParameters (a list of strings),
1388 parameter names that are in 'params' and also in this list will not be assigned directly,
1389 but instead the inheriting class's setXXX method will be called.
1391 Example: _complexParameters = ["communityName", "accessPassword", "hasAccessPassword"] corresponds
1392 to methods setCommunityName, setAccessPassword, setHasAccessPassword.
1393 Note: even if a parameter is in this list, you can decide not to implement its corresponding set
1394 method if you never expect the parameter name to come up inside 'params'.
1397 sanitizeResult = self.sanitizeParams(params)
1398 if sanitizeResult:
1399 return sanitizeResult
1401 self.setHidden(params.pop("hidden", False) == ["yes"])
1402 self.setNeedsToBeNotifiedOfDateChanges(params.pop("notifyOnDateChanges", False) == ["yes"])
1404 startDate = params.pop("startDate", None)
1405 if startDate is not None:
1406 self.setStartDateFromString(startDate)
1407 endDate = params.pop("endDate", None)
1408 if endDate is not None:
1409 self.setEndDateFromString(endDate)
1411 for k,v in params.iteritems():
1412 if k in self.__class__._simpleParameters:
1413 if self.__class__._simpleParameters[k][0]:
1414 try:
1415 v = self.__class__._simpleParameters[k][0](v)
1416 except ValueError:
1417 raise CollaborationServiceException("Tried to set value of parameter with name " + str(k) + ", recognized as a simple parameter of type" + str(self._simpleParameters[k]) + ", but the conversion failed")
1418 self._bookingParams[k] = v
1419 elif k in self.__class__._complexParameters:
1420 setterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('set')))
1421 setMethodName = 'set' + k[0].upper() + k[1:]
1422 if setMethodName in setterMethods:
1423 setterMethods[setMethodName](v)
1424 else:
1425 raise CollaborationServiceException("Tried to set value of parameter with name " + str(k) + ", recognized as a complex parameter, but the corresponding setter method " + setMethodName + " is not implemented")
1426 else:
1427 raise CollaborationServiceException("Tried to set the value of a parameter with name " + str(k) + " that was not declared")
1429 for k, v in self.__class__._simpleParameters.iteritems():
1430 if not k in self._bookingParams:
1431 self._bookingParams[k] = self.__class__._simpleParameters[k][1]
1433 if self.needsBookingParamsCheck():
1434 return self._checkBookingParams()
1436 return False
1438 def sanitizeParams(self, params):
1439 """ Checks if the fields introduced into the booking / request form
1440 have any kind of HTML or script tag.
1442 if not isinstance(params, dict):
1443 raise CollaborationServiceException("Booking parameters are not a dictionary")
1445 invalidFields = []
1446 for k, v in params.iteritems():
1447 if type(v) == str and hasTags(v):
1448 invalidFields.append(k)
1450 if invalidFields:
1451 return CSSanitizationError(invalidFields)
1452 else:
1453 return None
1455 def _getTypeDisplayName(self):
1456 return CollaborationTools.getXMLGenerator(self._type).getDisplayName()
1458 def _getFirstLineInfo(self, tz):
1459 return CollaborationTools.getXMLGenerator(self._type).getFirstLineInfo(self, tz)
1461 def _getTitle(self):
1462 if self.hasEventDisplay():
1463 raise CollaborationException("Method _getTitle was not overriden for the plugin type " + str(self._type))
1465 def _getInformationDisplay(self, tz):
1466 templateClass = CollaborationTools.getTemplateClass(self.getType(), "WInformationDisplay")
1467 if templateClass:
1468 return templateClass(self, tz).getHTML()
1469 else:
1470 return None
1472 def _getLaunchDisplayInfo(self):
1473 """ To be overloaded by plugins
1475 return None
1477 def _checkBookingParams(self):
1478 """ To be overriden by inheriting classes.
1479 Verifies that the booking parameters are correct. For example, that a numeric field is actually a number.
1480 Otherwise, an exception should be thrown.
1481 If there are no errors, the method should just return.
1483 if self.needsBookingParamsCheck():
1484 raise CollaborationServiceException("Method _checkBookingParams was not overriden for the plugin type " + str(self._type))
1486 def hasStart(self):
1487 """ Returns if this booking belongs to a plugin who has a "start" concept.
1488 This attribute will be available in Javascript with the "hasStart" attribute
1490 return self._hasStart
1492 def hasStartStopAll(self):
1493 """ Returns if this booking belongs to a plugin who has a "start" concept, and all of its bookings for a conference
1494 can be started simultanously.
1495 This attribute will be available in Javascript with the "hasStart" attribute
1497 return self._hasStartStopAll
1499 def hasStop(self):
1500 """ Returns if this booking belongs to a plugin who has a "stop" concept.
1501 This attribute will be available in Javascript with the "hasStop" attribute
1503 return self._hasStop
1505 def hasCheckStatus(self):
1506 """ Returns if this booking belongs to a plugin who has a "check status" concept.
1507 This attribute will be available in Javascript with the "hasCheckStatus" attribute
1509 return self._hasCheckStatus
1511 def hasAcceptReject(self):
1512 """ Returns if this booking belongs to a plugin who has a "accept or reject" concept.
1513 This attribute will be available in Javascript with the "hasAcceptReject" attribute
1515 return self._hasAcceptReject
1517 def requiresServerCallForStart(self):
1518 """ Returns if this booking belongs to a plugin who requires a server call when the start button is pressed.
1519 This attribute will be available in Javascript with the "requiresServerCallForStart" attribute
1521 return self._requiresServerCallForStart
1523 def requiresServerCallForStop(self):
1524 """ Returns if this booking belongs to a plugin who requires a server call when the stop button is pressed.
1525 This attribute will be available in Javascript with the "requiresServerCallForStop" attribute
1527 return self._requiresServerCallForStop
1529 def requiresClientCallForStart(self):
1530 """ Returns if this booking belongs to a plugin who requires a client call when the start button is pressed.
1531 This attribute will be available in Javascript with the "requiresClientCallForStart" attribute
1533 return self._requiresClientCallForStart
1535 def requiresClientCallForStop(self):
1536 """ Returns if this booking belongs to a plugin who requires a client call when the stop button is pressed.
1537 This attribute will be available in Javascript with the "requiresClientCallForStop" attribute
1539 return self._requiresClientCallForStop
1541 def canBeDeleted(self):
1542 """ Returns if this booking can be deleted, in the sense that the "Remove" button will be active and able to be pressed.
1543 This attribute will be available in Javascript with the "canBeDeleted" attribute
1546 if not hasattr(self, '_canBeDeleted'):
1547 self._canBeDeleted = True
1548 return self._canBeDeleted
1550 def setCanBeDeleted(self, canBeDeleted):
1551 """ Sets if this booking can be deleted, in the sense that the "Remove" button will be active and able to be pressed.
1552 This attribute will be available in Javascript with the "canBeDeleted" attribute
1554 self._canBeDeleted = canBeDeleted
1556 def canBeStarted(self):
1557 """ Returns if this booking can be started, in the sense that the "Start" button will be active and able to be pressed.
1558 This attribute will be available in Javascript with the "canBeStarted" attribute
1560 return self._canBeStarted
1562 def canBeStopped(self):
1563 """ Returns if this booking can be stopped, in the sense that the "Stop" button will be active and able to be pressed.
1564 This attribute will be available in Javascript with the "canBeStopped" attribute
1566 return self._canBeStopped
1568 def isPermittedToStart(self):
1569 """ Returns if this booking is allowed to start, in the sense that it will be started after the "Start" button is pressed.
1570 For example a booking should not be permitted to start before a given time, even if the button is active.
1571 This attribute will be available in Javascript with the "isPermittedToStart" attribute
1573 return self._permissionToStart
1575 def isPermittedToStop(self):
1576 """ Returns if this booking is allowed to stop, in the sense that it will be started after the "Stop" button is pressed.
1577 This attribute will be available in Javascript with the "isPermittedToStop" attribute
1579 return self._permissionToStop
1581 def needsBookingParamsCheck(self):
1582 """ Returns if this booking belongs to a plugin that needs to verify the booking parameters.
1584 return self._needsBookingParamsCheck
1586 def needsToBeNotifiedOnView(self):
1587 """ Returns if this booking needs to be notified when someone views it (for example when the list of bookings is returned)
1589 return self._needsToBeNotifiedOnView
1591 def canBeNotifiedOfEventDateChanges(self):
1592 """ Returns if bookings of this type should be able to be notified
1593 of their owner Event changing start date, end date or timezone.
1595 return self._canBeNotifiedOfEventDateChanges
1597 def needsToBeNotifiedOfDateChanges(self):
1598 """ Returns if this booking in particular needs to be notified
1599 of their owner Event changing start date, end date or timezone.
1601 return self._needsToBeNotifiedOfDateChanges
1603 def setNeedsToBeNotifiedOfDateChanges(self, needsToBeNotifiedOfDateChanges):
1604 """ Sets if this booking in particular needs to be notified
1605 of their owner Event changing start date, end date or timezone.
1607 self._needsToBeNotifiedOfDateChanges = needsToBeNotifiedOfDateChanges
1609 def isHidden(self):
1610 """ Return if this booking is "hidden"
1611 A hidden booking will not appear in display pages
1613 if not hasattr(self, '_hidden'):
1614 self._hidden = False
1615 return self._hidden
1617 def setHidden(self, hidden):
1618 """ Sets if this booking is "hidden"
1619 A hidden booking will not appear in display pages
1620 hidden: a Boolean
1622 self._hidden = hidden
1624 def isAllowMultiple(self):
1625 """ Returns if this booking belongs to a type that allows multiple bookings per event.
1627 return self._allowMultiple
1629 def shouldBeIndexed(self):
1630 """ Returns if bookings of this type should be indexed
1632 return self._shouldBeIndexed
1634 def getCommonIndexes(self):
1635 """ Returns a list of strings with the names of the
1636 common (shared) indexes that bookings of this type want to
1637 be included in.
1639 return self._commonIndexes
1641 def getModificationURL(self):
1642 return urlHandlers.UHConfModifCollaboration.getURL(self.getConference(),
1643 secure = CollaborationTools.isUsingHTTPS(),
1644 tab = CollaborationTools.getPluginTab(self.getPlugin()))
1646 def hasStartDate(self):
1647 """ Returns if bookings of this type have a start date
1648 (they may only have creation / modification date)
1650 return self._hasStartDate
1652 def hasTitle(self):
1653 """ Returns if bookings of this type have a title
1655 return self._hasTitle
1657 def hasEventDisplay(self):
1658 """ Returns if the type of this booking should display something on
1659 an event display page
1661 return self._hasEventDisplay
1663 def isAdminOnly(self):
1664 """ Returns if this booking / this booking's plugin pages should only be displayed
1665 to Server Admins, Video Service Admins, or the respective plugin admins.
1667 return self._adminOnly
1669 def _create(self):
1670 """ To be overriden by inheriting classes.
1671 This method is called when a booking is created, after setting the booking parameters.
1672 The plugin should decide if the booking is accepted or not.
1673 Often this will involve communication with another entity, like an MCU for the multi-point H.323 plugin,
1674 or a EVO HTTP server in the EVO case.
1676 raise CollaborationException("Method _create was not overriden for the plugin type " + str(self._type))
1678 def _modify(self, oldBookingParams):
1679 """ To be overriden by inheriting classes.
1680 This method is called when a booking is modifying, after setting the booking parameters.
1681 The plugin should decide if the booking is accepted or not.
1682 Often this will involve communication with another entity, like an MCU for the multi-point H.323 plugin
1683 or a EVO HTTP server in the EVO case.
1684 A dictionary with the previous booking params is passed. This dictionary is the one obtained
1685 by the method self.getBookingParams() before the new params input by the user are applied.
1687 raise CollaborationException("Method _modify was not overriden for the plugin type " + str(self._type))
1689 def _start(self):
1690 """ To be overriden by inheriting classes
1691 This method is called when the user presses the "Start" button in a plugin who has a "Start" concept
1692 and whose flag _requiresServerCallForStart is True.
1693 Often this will involve communication with another entity.
1695 if self.hasStart():
1696 raise CollaborationException("Method _start was not overriden for the plugin type " + str(self._type))
1697 else:
1698 pass
1700 def _stop(self):
1701 """ To be overriden by inheriting classes
1702 This method is called when the user presses the "Stop" button in a plugin who has a "Stop" concept
1703 and whose flag _requiresServerCallForStop is True.
1704 Often this will involve communication with another entity.
1706 if self.hasStop():
1707 raise CollaborationException("Method _stop was not overriden for the plugin type " + str(self._type))
1708 else:
1709 pass
1711 def _checkStatus(self):
1712 """ To be overriden by inheriting classes
1713 This method is called when the user presses the "Check Status" button in a plugin who has a "check status" concept.
1714 Often this will involve communication with another entity.
1716 if self.hasCheckStatus():
1717 raise CollaborationException("Method _checkStatus was not overriden for the plugin type " + str(self._type))
1718 else:
1719 pass
1721 def _accept(self, user = None):
1722 """ To be overriden by inheriting classes
1723 This method is called when a user with privileges presses the "Accept" button
1724 in a plugin who has a "accept or reject" concept.
1725 Often this will involve communication with another entity.
1727 if self.hasAcceptReject():
1728 raise CollaborationException("Method _accept was not overriden for the plugin type " + str(self._type))
1729 else:
1730 pass
1732 def _reject(self):
1733 """ To be overriden by inheriting classes
1734 This method is called when a user with privileges presses the "Reject" button
1735 in a plugin who has a "accept or reject" concept.
1736 Often this will involve communication with another entity.
1738 if self.hasAcceptReject():
1739 raise CollaborationException("Method _reject was not overriden for the plugin type " + str(self._type))
1740 else:
1741 pass
1743 def _notifyOnView(self):
1744 """ To be overriden by inheriting classes
1745 This method is called when a user "sees" a booking, for example when the list of bookings is displayed.
1746 Maybe in this moment the booking wants to update its status.
1748 if self.needsToBeNotifiedOnView():
1749 raise CollaborationException("Method _notifyOnView was not overriden for the plugin type " + str(self._type))
1750 else:
1751 pass
1753 def _delete(self):
1754 """ To be overriden by inheriting classes
1755 This method is called whent he user removes a booking. Maybe the plugin will need to liberate
1756 ressources that were allocated to it.
1757 This method does not unregister the booking from the list of date change observer of the meeting
1759 raise CollaborationException("Method _delete was not overriden for the plugin type " + str(self._type))
1761 def _sendNotifications(self, operation):
1763 Sends a mail, wrapping it with ExternalOperationsManager
1765 ExternalOperationsManager.execute(self, "sendMail_" + operation, self._sendMail, operation)
1767 def _sendMail(self, operation):
1768 if operation == 'new':
1769 try:
1770 notification = mail.NewBookingNotification(self)
1771 GenericMailer.sendAndLog(notification, self._conf,
1772 "MaKaC/plugins/Collaboration/base.py",
1773 self._conf.getCreator())
1774 except Exception, e:
1775 Logger.get('VideoServ').error(
1776 """Could not send NewBookingNotification for booking with id %s of event with id %s, exception: %s""" %
1777 (self.getId(), self._conf.getId(), str(e)))
1778 raise
1780 elif operation == 'modify':
1781 try:
1782 notification = mail.BookingModifiedNotification(self)
1783 GenericMailer.sendAndLog(notification, self._conf,
1784 "MaKaC/plugins/Collaboration/base.py",
1785 self._conf.getCreator())
1786 except Exception, e:
1787 Logger.get('VideoServ').error(
1788 """Could not send BookingModifiedNotification for booking with id %s of event with id %s, exception: %s""" %
1789 (self.getId(), self._conf.getId(), str(e)))
1790 raise
1792 elif operation == 'remove':
1793 try:
1794 notification = mail.BookingDeletedNotification(self)
1795 GenericMailer.sendAndLog(notification, self._conf,
1796 "MaKaC/plugins/Collaboration/base.py",
1797 self._conf.getCreator())
1798 except Exception, e:
1799 Logger.get('VideoServ').error(
1800 """Could not send BookingDeletedNotification for booking with id %s of event with id %s, exception: %s""" %
1801 (self.getId(), self._conf.getId(), str(e)))
1802 raise
1804 class WCSTemplateBase(wcomponents.WTemplated):
1805 """ Base class for Collaboration templates.
1806 It stores the following attributes:
1807 _conf : the corresponding Conference object.
1808 _pluginName: the corresponding plugin ("EVO", "DummyPlugin", etc.).
1809 _XXXOptions: a dictionary whose values are the options of the plugin called pluginName.
1810 So, for example, if an EVO template inherits from this class, an attribute self._EVOOptions will be available.
1811 This class also overloads the _setTPLFile method so that Indico knows where each plugin's *.tpl files are.
1814 def __init__(self, pluginId):
1815 """ Constructor for the WCSTemplateBase class.
1816 conf: a Conference object
1817 plugin: the corresponding plugin
1819 self._plugin = CollaborationTools.getPlugin(pluginId)
1820 self._pluginId = self._plugin.getId()
1821 self._ph = PluginsHolder()
1823 setattr(self, "_" + self._pluginId + "Options", self._plugin.getOptions())
1825 def _setTPLFile(self, extension='tpl'):
1826 tplDir = os.path.join(self._plugin.getModule().__path__[0], "tpls")
1828 fname = "%s.%s" % (self.tplId, extension)
1829 self.tplFile = os.path.join(tplDir, fname)
1831 hfile = self._getSpecificTPL(os.path.join(tplDir,self._pluginId,'chelp'), self.tplId,extension='wohl')
1832 self.helpFile = os.path.join(tplDir,'chelp',hfile)
1835 class WCSPageTemplateBase(WCSTemplateBase):
1836 """ Base class for Collaboration templates for the create / modify booking form.
1839 def __init__(self, conf, pluginId, user):
1840 WCSTemplateBase.__init__(self, pluginId)
1841 self._conf = conf
1842 self._user = user
1845 class WJSBase(WCSTemplateBase):
1846 """ Base class for Collaboration templates for Javascript code template.
1847 It overloads _setTPLFile so that indico can find the Main.js, Extra.js and Indexing.js files.
1849 def __init__(self, conf, plugin, user):
1850 WCSTemplateBase.__init__(self, plugin)
1851 self._conf = conf
1852 self._user = user
1854 def _setTPLFile(self):
1855 WCSTemplateBase._setTPLFile(self, extension='js')
1856 self.helpFile = ''
1859 class WCSCSSBase(WCSTemplateBase):
1860 """ Base class for Collaboration templates for CSS code template
1861 It overloads _setTPLFile so that indico can find the style.css files.
1864 def _setTPLFile(self):
1865 tplDir = self._plugin.getModule().__path__[0]
1866 fname = "%s.css" % self.tplId
1867 self.tplFile = os.path.join(tplDir, fname)
1868 self.helpFile = ''
1871 class CSErrorBase(Fossilizable):
1872 fossilizes(ICSErrorBaseFossil)
1874 """ When _create, _modify or _remove want to return an error,
1875 they should return an error that inherits from this class
1878 def __init__(self):
1879 pass
1881 def getUserMessage(self):
1882 """ To be overloaded.
1883 Returns the string that will be shown to the user when this error will happen.
1885 raise CollaborationException("Method getUserMessage was not overriden for the a CSErrorBase object of class " + self.__class__.__name__)
1887 def getLogMessage(self):
1888 """ To be overloaded.
1889 Returns the string that will be printed in Indico's log when this error will happen.
1891 raise CollaborationException("Method getLogMessage was not overriden for the a CSErrorBase object of class " + self.__class__.__name__)
1893 class CSSanitizationError(CSErrorBase): #already Fossilizable
1894 fossilizes(ICSSanitizationErrorFossil)
1896 """ Class used to return which fields have a sanitization error (invalid html / script tags)
1899 def __init__(self, invalidFields):
1900 self._invalidFields = invalidFields
1902 def invalidFields(self):
1903 return self._invalidFields
1906 class CollaborationException(MaKaCError):
1907 """ Error for the Collaboration System "core". Each plugin should declare their own EVOError, etc.
1909 def __init__(self, msg, area = 'Collaboration', inner = None):
1910 MaKaCError.__init__(self, msg, area)
1911 self._inner = inner
1913 def getInner(self):
1914 return self._inner
1916 def __str__(self):
1917 return MaKaCError.__str__(self) + '. Inner: ' + str(self._inner)
1919 class CollaborationServiceException(ServiceError):
1920 """ Error for the Collaboration System "core", for Service calls.
1922 def __init__(self, message, inner = None):
1923 ServiceError.__init__(self, "ERR-COLL", message, inner)
1925 class SpeakerStatusEnum:
1926 (NOEMAIL, NOTSIGNED, SIGNED, FROMFILE, PENDING, REFUSED) = xrange(6)
1928 class SpeakerWrapper(Persistent, Fossilizable):
1930 fossilizes(ISpeakerWrapperBaseFossil)
1932 def __init__(self, speaker, contId, requestType):
1933 self.status = not speaker.getEmail() and SpeakerStatusEnum.NOEMAIL or SpeakerStatusEnum.NOTSIGNED
1934 self.speaker = speaker
1935 self.contId = contId
1936 self.requestType = requestType
1937 self.reason = ""
1938 self.localFile = None
1939 self.dateAgreement = 0
1940 self.ipSignature = None
1941 self.modificationDate = time.time()
1942 self.uniqueIdHash = md5("%s.%s"%(time.time(), self.getUniqueId())).hexdigest()
1943 self.statusString = {
1944 SpeakerStatusEnum.NOEMAIL:_("No Email"),
1945 SpeakerStatusEnum.NOTSIGNED:_("Not Signed"),
1946 SpeakerStatusEnum.SIGNED: _("Signed"),
1947 SpeakerStatusEnum.FROMFILE: _("Uploaded"),
1948 SpeakerStatusEnum.PENDING: _("Pending..."),
1949 SpeakerStatusEnum.REFUSED: _("Refused")
1951 def getUniqueId(self):
1952 return "%s.%s"%(self.contId, self.speaker.getId())
1954 def getUniqueIdHash(self):
1955 # to remove once saved
1956 if not hasattr(self, "uniqueIdHash"):#TODO: remove when safe
1957 return md5(self.getUniqueId()).hexdigest()
1958 else:
1959 return self.uniqueIdHash
1961 def getStatus(self):
1962 return self.status
1964 def getStatusString(self):
1965 return self.statusString[self.status]
1967 def setStatus(self, newStatus, ip=None):
1968 try:
1969 self.status = newStatus;
1970 if newStatus == SpeakerStatusEnum.SIGNED or newStatus == SpeakerStatusEnum.FROMFILE:
1971 self.dateAgreement = time.time()
1972 if newStatus == SpeakerStatusEnum.SIGNED:
1973 self.ipSignature = ip
1974 except Exception, e:
1975 Logger.get('VideoServ').error("Exception while changing the speaker status. Exception: " + str(e))
1977 def getDateAgreementSigned(self):
1978 if hasattr(self, "dateAgreement"):#TODO: remove when safe
1979 return self.dateAgreement
1980 return 0
1982 def getIpAddressWhenSigned(self):
1983 if hasattr(self, "ipSignature"):#TODO: remove when safe
1984 return self.ipSignature
1985 return None
1987 def getRejectReason(self):
1988 if hasattr(self, "reason"):#TODO: remove when safe
1989 if self.status == SpeakerStatusEnum.REFUSED and hasattr(self, "reason"):
1990 return self.reason
1991 else:
1992 return "This speaker has not refused the agreement."
1993 else:
1994 return "Information not available."
1996 def setRejectReason(self, reason):
1997 if hasattr(self, "reason"):#TODO: remove when safe
1998 self.reason = reason
2000 def getObject(self):
2001 return self.speaker
2003 def getContId(self):
2004 return self.contId
2006 def getRequestType(self):
2007 if hasattr(self, "requestType"):#TODO: remove when safe
2008 return self.requestType
2009 return "NA"
2011 def setRequestType(self, type):
2012 self.requestType = type
2014 def getSpeakerId(self):
2015 return self.speaker.getId()
2017 def getLocalFile(self):
2019 If exists, return path to paper agreement
2021 if hasattr(self, "localFile"):#TODO: remove when safe
2022 return self.localFile
2024 def setLocalFile(self, localFile):
2026 Set localFile of paper agreement
2028 if hasattr(self, "localFile"):#TODO: remove when safe
2029 self.localFile = localFile
2031 def hasEmail(self):
2032 if self.speaker.getEmail():
2033 return True
2034 return False
2036 def getCategory(self):
2037 return None
2039 def getConference(self):
2040 return self.speaker.getConference()
2042 def getContribution(self):
2043 # if the conference is a lecture, the getContribution will fail.
2044 if self.getConference().getType() == "simple_event":
2045 return None
2046 else:
2047 return self.speaker.getContribution()
2049 def getSession(self):
2050 return None
2052 def getSubContribution(self):
2053 return None
2055 def getModificationDate(self):
2056 if hasattr(self, "modificationDate"):#TODO: remove when safe
2057 return self.modificationDate
2058 return None
2060 def setModificationDate(self):
2061 if hasattr(self, "modificationDate"):#TODO: remove when safe
2062 self.modificationDate = time.time()
2064 def getLocator(self):
2065 return self.getContribution().getLocator()