| 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. |
|---|
| 11 | ## |
|---|
| 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. |
|---|
| 16 | ## |
|---|
| 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 | from indico.util.contextManager import ContextManager |
|---|
| 21 | import time |
|---|
| 22 | from persistent import Persistent |
|---|
| 23 | from hashlib import md5 |
|---|
| 24 | from MaKaC.common.Counter import Counter |
|---|
| 25 | from MaKaC.common.utils import formatDateTime, parseDateTime |
|---|
| 26 | from MaKaC.common.timezoneUtils import getAdjustedDate, setAdjustedDate,\ |
|---|
| 27 | datetimeToUnixTimeInt |
|---|
| 28 | from MaKaC.webinterface import wcomponents, urlHandlers |
|---|
| 29 | from MaKaC.plugins import PluginsHolder |
|---|
| 30 | from MaKaC.errors import MaKaCError, NoReportError |
|---|
| 31 | from MaKaC.services.interface.rpc.common import ServiceError |
|---|
| 32 | from MaKaC.common.timezoneUtils import nowutc |
|---|
| 33 | from MaKaC.common.logger import Logger |
|---|
| 34 | from MaKaC.common.indexes import IndexesHolder |
|---|
| 35 | from MaKaC.plugins.Collaboration.collaborationTools import CollaborationTools,\ |
|---|
| 36 | MailTools |
|---|
| 37 | from MaKaC.conference import Observer |
|---|
| 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 indico.modules.scheduler.client import Client |
|---|
| 44 | from indico.modules.scheduler.tasks import HTTPTask |
|---|
| 45 | from indico.util import json |
|---|
| 46 | from indico.util.i18n import gettext_lazy |
|---|
| 47 | from indico.util.date_time import now_utc |
|---|
| 48 | from MaKaC.common.fossilize import Fossilizable, fossilizes |
|---|
| 49 | from MaKaC.common.externalOperationsManager import ExternalOperationsManager |
|---|
| 50 | from BTrees.OOBTree import OOBTree |
|---|
| 51 | |
|---|
| 52 | from MaKaC.plugins.Collaboration.fossils import ICSErrorBaseFossil, ICSSanitizationErrorFossil,\ |
|---|
| 53 | ICSBookingBaseConfModifFossil, ICSBookingBaseIndexingFossil,\ |
|---|
| 54 | ISpeakerWrapperBaseFossil |
|---|
| 55 | |
|---|
| 56 | |
|---|
| 57 | class CSBookingManager(Persistent, Observer): |
|---|
| 58 | """ Class for managing the bookins of a meeting. |
|---|
| 59 | It will store the list of bookings. Adding / removing / editing bookings should be through this class. |
|---|
| 60 | """ |
|---|
| 61 | |
|---|
| 62 | _shouldBeTitleNotified = True |
|---|
| 63 | _shouldBeDateChangeNotified = True |
|---|
| 64 | _shouldBeLocationChangeNotified = True |
|---|
| 65 | _shouldBeDeletionNotified = True |
|---|
| 66 | |
|---|
| 67 | def __init__(self, conf): |
|---|
| 68 | """ Constructor for the CSBookingManager class. |
|---|
| 69 | conf: a Conference object. The meeting that owns this CSBookingManager. |
|---|
| 70 | """ |
|---|
| 71 | self._conf = conf |
|---|
| 72 | self._counter = Counter(1) |
|---|
| 73 | # a dict where the bookings will be stored. The key will be the booking id, the value a CSBookingBase object. |
|---|
| 74 | self._bookings = {} |
|---|
| 75 | |
|---|
| 76 | # an index of bookings by type. The key will be a booking type (string), the value a list of booking id |
|---|
| 77 | self._bookingsByType = {} |
|---|
| 78 | |
|---|
| 79 | # an index of bookings to video services by event.uniqueId : video.uniqueId pairind. |
|---|
| 80 | self._bookingsToVideoServices = OOBTree() |
|---|
| 81 | |
|---|
| 82 | # a list of ids with hidden bookings |
|---|
| 83 | self._hiddenBookings = set() |
|---|
| 84 | |
|---|
| 85 | # an index of video services managers for each plugin. key: plugin name, value: list of users |
|---|
| 86 | self._managers = {} |
|---|
| 87 | |
|---|
| 88 | # list of speaker wrapper for a conference |
|---|
| 89 | self._speakerWrapperList = [] |
|---|
| 90 | self.updateSpeakerWrapperList() |
|---|
| 91 | |
|---|
| 92 | def getOwner(self): |
|---|
| 93 | """ Returns the Conference (the meeting) that owns this CSBookingManager object. |
|---|
| 94 | """ |
|---|
| 95 | return self._conf |
|---|
| 96 | |
|---|
| 97 | def isCSAllowed(self, user = None): |
|---|
| 98 | """ Returns if the associated event should display a Video Services tab |
|---|
| 99 | This can depend on the kind of event (meeting, lecture, conference), on the equipment of the room... |
|---|
| 100 | If a user is provided, we will take into account if the user can manage the plugin (for example, |
|---|
| 101 | an event manager cannot manage an admin-only plugin) |
|---|
| 102 | """ |
|---|
| 103 | pluginsPerEventType = CollaborationTools.getCollaborationPluginType().getOption("pluginsPerEventType").getValue() |
|---|
| 104 | if pluginsPerEventType: |
|---|
| 105 | for plugin in pluginsPerEventType[self._conf.getType()]: |
|---|
| 106 | if plugin.isActive() and (user is None or CollaborationTools.canUserManagePlugin(self._conf, plugin, user)): |
|---|
| 107 | return True |
|---|
| 108 | return False |
|---|
| 109 | |
|---|
| 110 | def getAllowedPlugins(self): |
|---|
| 111 | """ Returns a list of allowed plugins (Plugin objects) for this event. |
|---|
| 112 | Only active plugins are returned. |
|---|
| 113 | This can depend on the kind of event (meeting, lecture, conference), on the equipment of the room... |
|---|
| 114 | """ |
|---|
| 115 | pluginsPerEventType = CollaborationTools.getCollaborationPluginType().getOption("pluginsPerEventType").getValue() |
|---|
| 116 | if pluginsPerEventType is not None: |
|---|
| 117 | allowedForThisEvent = pluginsPerEventType[self._conf.getType()] |
|---|
| 118 | return [plugin for plugin in allowedForThisEvent if plugin.isActive()] |
|---|
| 119 | |
|---|
| 120 | |
|---|
| 121 | def getBookingList(self, sorted = False, filterByType = None, notify = False, onlyPublic = False): |
|---|
| 122 | """ Returns a list of all the bookings. |
|---|
| 123 | If sorted = True, the list of bookings will be sorted by id. |
|---|
| 124 | If filterByType = None, all bookings are returned. |
|---|
| 125 | Otherwise, just those of the type "filterByType" if filterByType is a string, |
|---|
| 126 | or if it is a list of strings, those who have a type included in filterByType. |
|---|
| 127 | """ |
|---|
| 128 | |
|---|
| 129 | if not hasattr(self, "_bookingsByType"): #TODO: remove when safe |
|---|
| 130 | self._bookingsByType = {} |
|---|
| 131 | |
|---|
| 132 | if filterByType is not None: |
|---|
| 133 | if type(filterByType) == str: |
|---|
| 134 | keys = self._bookingsByType.get(filterByType, []) |
|---|
| 135 | if type(filterByType) == list: |
|---|
| 136 | keys = [] |
|---|
| 137 | for pluginName in filterByType: |
|---|
| 138 | keys.extend(self._bookingsByType.get(pluginName, [])) |
|---|
| 139 | else: |
|---|
| 140 | keys = self._bookings.keys() |
|---|
| 141 | |
|---|
| 142 | if onlyPublic and self.getHiddenBookings(): |
|---|
| 143 | keys = set(keys) |
|---|
| 144 | keys = keys.difference(self.getHiddenBookings()) |
|---|
| 145 | keys = list(keys) |
|---|
| 146 | |
|---|
| 147 | if sorted: |
|---|
| 148 | keys.sort(key = lambda k: int(k)) |
|---|
| 149 | |
|---|
| 150 | bookingList = [self._bookings[k] for k in keys if not self._bookings[k].hasSessionOrContributionLink() or self._bookings[k].getLinkObject()] |
|---|
| 151 | |
|---|
| 152 | #we notify all the bookings that they have been viewed. If a booking doesn't need to be viewed, nothing will happen |
|---|
| 153 | if notify: |
|---|
| 154 | for booking in bookingList: |
|---|
| 155 | if booking.needsToBeNotifiedOnView(): |
|---|
| 156 | try: |
|---|
| 157 | booking._notifyOnView() |
|---|
| 158 | except Exception, e: |
|---|
| 159 | Logger.get('VideoServ').error("Exception while notifying to a booking that it is being viewed. Exception: " + str(e)) |
|---|
| 160 | |
|---|
| 161 | return bookingList |
|---|
| 162 | |
|---|
| 163 | def getBooking(self, id): |
|---|
| 164 | """ Returns a booking given its id. |
|---|
| 165 | """ |
|---|
| 166 | return self._bookings[id] |
|---|
| 167 | |
|---|
| 168 | def getSingleBooking(self, type, notify = False): |
|---|
| 169 | """ Returns the single booking of a plugin who only allows one booking. |
|---|
| 170 | type: a string with the name of the plugin |
|---|
| 171 | If the plugin actually allows multiple bookings, an exception will be thrown |
|---|
| 172 | If the plugin has no booking, None will be returned. |
|---|
| 173 | Otherwise the booking will be returned |
|---|
| 174 | """ |
|---|
| 175 | if CollaborationTools.getCSBookingClass(type)._allowMultiple: |
|---|
| 176 | raise CollaborationException("Plugin type " + str(type) + " is not a single-booking plugin") |
|---|
| 177 | blist = self._bookingsByType.get(type,[]) |
|---|
| 178 | if blist: |
|---|
| 179 | booking = self._bookings[blist[0]] |
|---|
| 180 | if notify: |
|---|
| 181 | try: |
|---|
| 182 | booking._notifyOnView() |
|---|
| 183 | except Exception, e: |
|---|
| 184 | Logger.get('VideoServ').error("Exception while notifying to a booking that it is being viewed. Exception: " + str(e)) |
|---|
| 185 | return booking |
|---|
| 186 | else: |
|---|
| 187 | return None |
|---|
| 188 | |
|---|
| 189 | def getHiddenBookings(self): |
|---|
| 190 | if not hasattr(self, '_hiddenBookings'): |
|---|
| 191 | self._hiddenBookings = set() |
|---|
| 192 | return self._hiddenBookings |
|---|
| 193 | |
|---|
| 194 | def hasBookings(self): |
|---|
| 195 | return len(self._bookings) > 0 |
|---|
| 196 | |
|---|
| 197 | def canCreateBooking(self, type): |
|---|
| 198 | """ Returns if it's possible to create a booking of this given type |
|---|
| 199 | """ |
|---|
| 200 | if not CollaborationTools.getCSBookingClass(type)._allowMultiple: |
|---|
| 201 | return len(self.getBookingList(filterByType = type)) == 0 |
|---|
| 202 | return True |
|---|
| 203 | |
|---|
| 204 | def checkVideoLink(self, bookingParams): |
|---|
| 205 | |
|---|
| 206 | if bookingParams.get('videoLinkType',"") == "session": |
|---|
| 207 | sessSlotId = bookingParams.get("videoLinkSession","") |
|---|
| 208 | import re |
|---|
| 209 | regExp = re.match(r"""(s[0-9a]*)(l[0-9]*)""", sessSlotId) |
|---|
| 210 | if not regExp: |
|---|
| 211 | raise CollaborationException(_('No session has been passed when the type is session.')) |
|---|
| 212 | sessionId = regExp.group(1)[1:] |
|---|
| 213 | slotId = regExp.group(2)[1:] |
|---|
| 214 | session = self._conf.getSessionById(sessionId) |
|---|
| 215 | if session is None: |
|---|
| 216 | raise CollaborationException(_('The session does not exist.')) |
|---|
| 217 | slot = session.getSlotById(slotId) |
|---|
| 218 | if slot is None: |
|---|
| 219 | raise CollaborationException(_('The session does not exist.')) |
|---|
| 220 | return slot.getUniqueId() |
|---|
| 221 | |
|---|
| 222 | elif bookingParams.get('videoLinkType',"") == "contribution": |
|---|
| 223 | contId = bookingParams.get("videoLinkContribution","") |
|---|
| 224 | if contId == "": |
|---|
| 225 | raise CollaborationException(_('No contribution has been passed when the type is contribution.')) |
|---|
| 226 | cont = self._conf.getContributionById(contId) |
|---|
| 227 | if cont is None: |
|---|
| 228 | raise CollaborationException(_('The contribution does not exist.')) |
|---|
| 229 | return cont.getUniqueId() |
|---|
| 230 | |
|---|
| 231 | return self._conf.getUniqueId() |
|---|
| 232 | |
|---|
| 233 | def addBooking(self, booking): |
|---|
| 234 | """ Adds an existing booking to the list of bookings. |
|---|
| 235 | |
|---|
| 236 | booking: The existing booking to be added. |
|---|
| 237 | """ |
|---|
| 238 | self._bookings[booking.getId()] = booking |
|---|
| 239 | self._bookingsByType.setdefault(booking.getType(),[]).append(booking.getId()) |
|---|
| 240 | if booking.isHidden(): |
|---|
| 241 | self.getHiddenBookings().add(booking.getId()) |
|---|
| 242 | self._indexBooking(booking) |
|---|
| 243 | self._notifyModification() |
|---|
| 244 | |
|---|
| 245 | def createBooking(self, bookingType, bookingParams = {}): |
|---|
| 246 | """ Adds a new booking to the list of bookings. |
|---|
| 247 | The id of the new booking is auto-generated incrementally. |
|---|
| 248 | After generating the booking, its "performBooking" method will be called. |
|---|
| 249 | |
|---|
| 250 | bookingType: a String with the booking's plugin. Example: "DummyPlugin", "EVO" |
|---|
| 251 | bookingParams: a dictionary with the parameters necessary to create the booking. |
|---|
| 252 | "create the booking" usually means Indico deciding if the booking can take place. |
|---|
| 253 | if "startDate" and "endDate" are among the keys, they will be taken out of the dictionary. |
|---|
| 254 | """ |
|---|
| 255 | if self.canCreateBooking(bookingType): |
|---|
| 256 | |
|---|
| 257 | uniqueId = self.checkVideoLink(bookingParams) |
|---|
| 258 | |
|---|
| 259 | if (self.hasVideoService(uniqueId) and bookingParams.has_key("videoLinkType") and bookingParams.get("videoLinkType","") != "event"): # Restriction: 1 video service per session or contribution. |
|---|
| 260 | raise NoReportError(_('Only one video service per contribution or session is allowed.')) |
|---|
| 261 | |
|---|
| 262 | newBooking = CollaborationTools.getCSBookingClass(bookingType)(bookingType, self._conf) |
|---|
| 263 | if bookingParams.has_key("videoLinkType"): |
|---|
| 264 | newBooking.setLinkType({bookingParams["videoLinkType"] : uniqueId}) |
|---|
| 265 | |
|---|
| 266 | error = newBooking.setBookingParams(bookingParams) |
|---|
| 267 | |
|---|
| 268 | if isinstance(error, CSErrorBase): |
|---|
| 269 | return error |
|---|
| 270 | elif error: |
|---|
| 271 | raise CollaborationServiceException("Problem while creating a booking of type " + bookingType) |
|---|
| 272 | else: |
|---|
| 273 | newId = self._getNewBookingId() |
|---|
| 274 | newBooking.setId(newId) |
|---|
| 275 | createResult = newBooking._create() |
|---|
| 276 | if isinstance(createResult, CSErrorBase): |
|---|
| 277 | return createResult |
|---|
| 278 | else: |
|---|
| 279 | self._bookings[newId] = newBooking |
|---|
| 280 | self._bookingsByType.setdefault(bookingType,[]).append(newId) |
|---|
| 281 | if newBooking.isHidden(): |
|---|
| 282 | self.getHiddenBookings().add(newId) |
|---|
| 283 | self._indexBooking(newBooking) |
|---|
| 284 | self._notifyModification() |
|---|
| 285 | |
|---|
| 286 | if uniqueId is not None: # if we're here and uniqueId has a value, register the video service. |
|---|
| 287 | self.addVideoService(uniqueId, newBooking) |
|---|
| 288 | |
|---|
| 289 | if MailTools.needToSendEmails(bookingType): |
|---|
| 290 | newBooking._sendNotifications('new') |
|---|
| 291 | |
|---|
| 292 | return newBooking |
|---|
| 293 | else: |
|---|
| 294 | #we raise an exception because the web interface should take care of this never actually happening |
|---|
| 295 | raise CollaborationServiceException(bookingType + " only allows to create 1 booking per event") |
|---|
| 296 | |
|---|
| 297 | def _indexBooking(self, booking): |
|---|
| 298 | if booking.shouldBeIndexed(): |
|---|
| 299 | indexes = self._getIndexList(booking) |
|---|
| 300 | for index in indexes: |
|---|
| 301 | index.indexBooking(booking) |
|---|
| 302 | |
|---|
| 303 | def changeBooking(self, bookingId, bookingParams): |
|---|
| 304 | """ |
|---|
| 305 | Changes the bookingParams of a CSBookingBase object. |
|---|
| 306 | After updating the booking, its 'performBooking' method will be called. |
|---|
| 307 | bookingId: the id of the CSBookingBase object to change |
|---|
| 308 | bookingParams: a dictionary with the new parameters that will modify the booking |
|---|
| 309 | 'modify the booking' can mean that maybe the booking will be rejected with the new parameters. |
|---|
| 310 | if 'startDate' and 'endDate' are among the keys, they will be taken out of the dictionary. |
|---|
| 311 | """ |
|---|
| 312 | booking = self.getBooking(bookingId) |
|---|
| 313 | |
|---|
| 314 | oldStartDate = booking.getStartDate() |
|---|
| 315 | oldModificationDate = booking.getModificationDate() |
|---|
| 316 | oldBookingParams = booking.getBookingParams() #this is a copy so it's ok |
|---|
| 317 | |
|---|
| 318 | error = booking.setBookingParams(bookingParams) |
|---|
| 319 | if isinstance(error, CSSanitizationError): |
|---|
| 320 | return error |
|---|
| 321 | elif error: |
|---|
| 322 | CSBookingManager._rollbackChanges(booking, oldBookingParams, oldModificationDate) |
|---|
| 323 | if isinstance(error, CSErrorBase): |
|---|
| 324 | return error |
|---|
| 325 | raise CollaborationServiceException("Problem while modifying a booking of type " + booking.getType()) |
|---|
| 326 | else: |
|---|
| 327 | modifyResult = booking._modify(oldBookingParams) |
|---|
| 328 | if isinstance(modifyResult, CSErrorBase): |
|---|
| 329 | CSBookingManager._rollbackChanges(booking, oldBookingParams, oldModificationDate) |
|---|
| 330 | return modifyResult |
|---|
| 331 | else: |
|---|
| 332 | modificationDate = now_utc() |
|---|
| 333 | booking.setModificationDate(modificationDate) |
|---|
| 334 | |
|---|
| 335 | if booking.isHidden(): |
|---|
| 336 | self.getHiddenBookings().add(booking.getId()) |
|---|
| 337 | elif booking.getId() in self.getHiddenBookings(): |
|---|
| 338 | self.getHiddenBookings().remove(booking.getId()) |
|---|
| 339 | |
|---|
| 340 | eventLinkUpdated = False |
|---|
| 341 | newLinkId = self.checkVideoLink(bookingParams) |
|---|
| 342 | |
|---|
| 343 | if booking.hasSessionOrContributionLink(): |
|---|
| 344 | oldLinkData = booking.getLinkIdDict() |
|---|
| 345 | oldLinkId = oldLinkData.values()[0] |
|---|
| 346 | |
|---|
| 347 | # Details changed, we need to remove the association and re-create it |
|---|
| 348 | if not (oldLinkData.has_key(bookingParams.get('videoLinkType','')) and oldLinkId == newLinkId): |
|---|
| 349 | self.removeVideoSingleService(booking.getLinkId(), booking) |
|---|
| 350 | eventLinkUpdated = True |
|---|
| 351 | |
|---|
| 352 | if eventLinkUpdated or (bookingParams.has_key("videoLinkType") and bookingParams.get("videoLinkType","") != "event"): |
|---|
| 353 | if self.hasVideoService(booking.getLinkId(), booking): |
|---|
| 354 | pass # No change in the event linking |
|---|
| 355 | elif newLinkId is not None: |
|---|
| 356 | if (self.hasVideoService(newLinkId) and bookingParams.has_key("videoLinkType") and bookingParams.get("videoLinkType","") != "event"): # Restriction: 1 video service per session or contribution. |
|---|
| 357 | raise NoReportError(_('Only one video service per contribution or session is allowed.')) |
|---|
| 358 | else: |
|---|
| 359 | self.addVideoService(newLinkId, booking) |
|---|
| 360 | if bookingParams.has_key("videoLinkType"): |
|---|
| 361 | booking.setLinkType({bookingParams['videoLinkType']: newLinkId}) |
|---|
| 362 | else: # If it's still None, event linking has been completely removed. |
|---|
| 363 | booking.resetLinkParams() |
|---|
| 364 | |
|---|
| 365 | self._changeStartDateInIndex(booking, oldStartDate, booking.getStartDate()) |
|---|
| 366 | self._changeModificationDateInIndex(booking, oldModificationDate, modificationDate) |
|---|
| 367 | |
|---|
| 368 | if booking.hasAcceptReject(): |
|---|
| 369 | if booking.getAcceptRejectStatus() is not None: |
|---|
| 370 | booking.clearAcceptRejectStatus() |
|---|
| 371 | self._addToPendingIndex(booking) |
|---|
| 372 | |
|---|
| 373 | self._notifyModification() |
|---|
| 374 | |
|---|
| 375 | if MailTools.needToSendEmails(booking.getType()): |
|---|
| 376 | booking._sendNotifications('modify') |
|---|
| 377 | |
|---|
| 378 | return booking |
|---|
| 379 | |
|---|
| 380 | @classmethod |
|---|
| 381 | def _rollbackChanges(cls, booking, oldBookingParams, oldModificationDate): |
|---|
| 382 | booking.setBookingParams(oldBookingParams) |
|---|
| 383 | booking.setModificationDate(oldModificationDate) |
|---|
| 384 | |
|---|
| 385 | def _changeConfTitleInIndex(self, booking, oldTitle, newTitle): |
|---|
| 386 | if booking.shouldBeIndexed(): |
|---|
| 387 | indexes = self._getIndexList(booking) |
|---|
| 388 | for index in indexes: |
|---|
| 389 | index.changeEventTitle(booking, oldTitle, newTitle) |
|---|
| 390 | |
|---|
| 391 | def _changeStartDateInIndex(self, booking, oldStartDate, newStartDate): |
|---|
| 392 | if booking.shouldBeIndexed() and booking.hasStartDate(): |
|---|
| 393 | indexes = self._getIndexList(booking) |
|---|
| 394 | for index in indexes: |
|---|
| 395 | index.changeStartDate(booking, oldStartDate, newStartDate) |
|---|
| 396 | |
|---|
| 397 | def _changeModificationDateInIndex(self, booking, oldModificationDate, newModificationDate): |
|---|
| 398 | if booking.shouldBeIndexed(): |
|---|
| 399 | indexes = self._getIndexList(booking) |
|---|
| 400 | for index in indexes: |
|---|
| 401 | index.changeModificationDate(booking, oldModificationDate, newModificationDate) |
|---|
| 402 | |
|---|
| 403 | def _changeConfStartDateInIndex(self, booking, oldConfStartDate, newConfStartDate): |
|---|
| 404 | if booking.shouldBeIndexed(): |
|---|
| 405 | indexes = self._getIndexList(booking) |
|---|
| 406 | for index in indexes: |
|---|
| 407 | index.changeConfStartDate(booking, oldConfStartDate, newConfStartDate) |
|---|
| 408 | |
|---|
| 409 | def removeBooking(self, id): |
|---|
| 410 | """ Removes a booking given its id. |
|---|
| 411 | """ |
|---|
| 412 | booking = self.getBooking(id) |
|---|
| 413 | bookingType = booking.getType() |
|---|
| 414 | bookingLinkId = booking.getLinkId() |
|---|
| 415 | |
|---|
| 416 | removeResult = booking._delete() |
|---|
| 417 | if isinstance(removeResult, CSErrorBase): |
|---|
| 418 | return removeResult |
|---|
| 419 | else: |
|---|
| 420 | del self._bookings[id] |
|---|
| 421 | self._bookingsByType[bookingType].remove(id) |
|---|
| 422 | if not self._bookingsByType[bookingType]: |
|---|
| 423 | del self._bookingsByType[bookingType] |
|---|
| 424 | if id in self.getHiddenBookings(): |
|---|
| 425 | self.getHiddenBookings().remove(id) |
|---|
| 426 | |
|---|
| 427 | # If there is an association to a session or contribution, remove it |
|---|
| 428 | if bookingLinkId is not None: |
|---|
| 429 | self.removeVideoSingleService(bookingLinkId, booking) |
|---|
| 430 | self._unindexBooking(booking) |
|---|
| 431 | |
|---|
| 432 | self._notifyModification() |
|---|
| 433 | |
|---|
| 434 | if MailTools.needToSendEmails(booking.getType()): |
|---|
| 435 | booking._sendNotifications('remove') |
|---|
| 436 | |
|---|
| 437 | return booking |
|---|
| 438 | |
|---|
| 439 | def _unindexBooking(self, booking): |
|---|
| 440 | if booking.shouldBeIndexed(): |
|---|
| 441 | indexes = self._getIndexList(booking) |
|---|
| 442 | for index in indexes: |
|---|
| 443 | index.unindexBooking(booking) |
|---|
| 444 | |
|---|
| 445 | def startBooking(self, id): |
|---|
| 446 | booking = self._bookings[id] |
|---|
| 447 | if booking.canBeStarted(): |
|---|
| 448 | booking._start() |
|---|
| 449 | return booking |
|---|
| 450 | else: |
|---|
| 451 | raise CollaborationException(_("Tried to start booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be started.")) |
|---|
| 452 | |
|---|
| 453 | def stopBooking(self, id): |
|---|
| 454 | booking = self._bookings[id] |
|---|
| 455 | if booking.canBeStopped(): |
|---|
| 456 | booking._stop() |
|---|
| 457 | return booking |
|---|
| 458 | else: |
|---|
| 459 | raise CollaborationException(_("Tried to stop booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be stopped.")) |
|---|
| 460 | |
|---|
| 461 | def connectBooking(self, id): |
|---|
| 462 | booking = self._bookings[id] |
|---|
| 463 | if booking.canBeConnected(): |
|---|
| 464 | return booking._connect() |
|---|
| 465 | else: |
|---|
| 466 | raise CollaborationException(_("Tried to connect booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be connected.")) |
|---|
| 467 | |
|---|
| 468 | def checkBookingStatus(self, id): |
|---|
| 469 | booking = self._bookings[id] |
|---|
| 470 | if booking.hasCheckStatus(): |
|---|
| 471 | result = booking._checkStatus() |
|---|
| 472 | if isinstance(result, CSErrorBase): |
|---|
| 473 | return result |
|---|
| 474 | else: |
|---|
| 475 | return booking |
|---|
| 476 | else: |
|---|
| 477 | 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.")) |
|---|
| 478 | |
|---|
| 479 | def acceptBooking(self, id, user = None): |
|---|
| 480 | booking = self._bookings[id] |
|---|
| 481 | if booking.hasAcceptReject(): |
|---|
| 482 | if booking.getAcceptRejectStatus() is None: |
|---|
| 483 | self._removeFromPendingIndex(booking) |
|---|
| 484 | booking.accept(user) |
|---|
| 485 | return booking |
|---|
| 486 | else: |
|---|
| 487 | raise ServiceError(message=_("Tried to accept booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be accepted.")) |
|---|
| 488 | |
|---|
| 489 | def rejectBooking(self, id, reason): |
|---|
| 490 | booking = self._bookings[id] |
|---|
| 491 | if booking.hasAcceptReject(): |
|---|
| 492 | if booking.getAcceptRejectStatus() is None: |
|---|
| 493 | self._removeFromPendingIndex(booking) |
|---|
| 494 | booking.reject(reason) |
|---|
| 495 | return booking |
|---|
| 496 | else: |
|---|
| 497 | raise ServiceError("ERR-COLL10", _("Tried to reject booking ") + str(id) + _(" of meeting ") + str(self._conf.getId()) + _(" but this booking cannot be rejected.")) |
|---|
| 498 | |
|---|
| 499 | def makeMeModeratorBooking(self, id, user): |
|---|
| 500 | booking = self._bookings[id] |
|---|
| 501 | bookingParams = booking.getBookingParams() |
|---|
| 502 | bookingParams["owner"] = user |
|---|
| 503 | return self.changeBooking(id,bookingParams) |
|---|
| 504 | |
|---|
| 505 | def _addToPendingIndex(self, booking): |
|---|
| 506 | if booking.shouldBeIndexed(): |
|---|
| 507 | indexes = self._getPendingIndexList(booking) |
|---|
| 508 | for index in indexes: |
|---|
| 509 | index.indexBooking(booking) |
|---|
| 510 | |
|---|
| 511 | def _removeFromPendingIndex(self, booking): |
|---|
| 512 | if booking.shouldBeIndexed(): |
|---|
| 513 | indexes = self._getPendingIndexList(booking) |
|---|
| 514 | for index in indexes: |
|---|
| 515 | index.unindexBooking(booking) |
|---|
| 516 | |
|---|
| 517 | def _getNewBookingId(self): |
|---|
| 518 | return self._counter.newCount() |
|---|
| 519 | |
|---|
| 520 | def _getIndexList(self, booking): |
|---|
| 521 | """ Returns a list of BookingsIndex objects where the booking should be indexed. |
|---|
| 522 | This list includes: |
|---|
| 523 | -an index of all bookings |
|---|
| 524 | -an index of bookings of the given type |
|---|
| 525 | -an index of all bookings in the category of the event |
|---|
| 526 | -an index of booking of the given type, in the category of the event |
|---|
| 527 | If the booking type declared common indexes: |
|---|
| 528 | -the common indexes |
|---|
| 529 | -the common indexes for the category of the event |
|---|
| 530 | If the booking is of the Accept/Reject type |
|---|
| 531 | -same indexes as above, but only for pending bookings |
|---|
| 532 | """ |
|---|
| 533 | collaborationIndex = IndexesHolder().getById("collaboration") |
|---|
| 534 | indexes = [collaborationIndex.getAllBookingsIndex(), |
|---|
| 535 | collaborationIndex.getIndex(booking.getType())] |
|---|
| 536 | |
|---|
| 537 | for commonIndexName in booking.getCommonIndexes(): |
|---|
| 538 | indexes.append(collaborationIndex.getIndex(commonIndexName)) |
|---|
| 539 | |
|---|
| 540 | if booking.hasAcceptReject() and booking.getAcceptRejectStatus() is None: |
|---|
| 541 | indexes.extend(self._getPendingIndexList(booking)) |
|---|
| 542 | |
|---|
| 543 | return indexes |
|---|
| 544 | |
|---|
| 545 | def _getPendingIndexList(self, booking): |
|---|
| 546 | collaborationIndex = IndexesHolder().getById("collaboration") |
|---|
| 547 | indexes = [collaborationIndex.getIndex("all_pending"), |
|---|
| 548 | collaborationIndex.getIndex(booking.getType() + "_pending")] |
|---|
| 549 | |
|---|
| 550 | for commonIndexName in booking.getCommonIndexes(): |
|---|
| 551 | indexes.append(collaborationIndex.getIndex(commonIndexName + "_pending")) |
|---|
| 552 | |
|---|
| 553 | return indexes |
|---|
| 554 | |
|---|
| 555 | def getManagers(self): |
|---|
| 556 | if not hasattr(self, "_managers"): |
|---|
| 557 | self._managers = {} |
|---|
| 558 | return self._managers |
|---|
| 559 | |
|---|
| 560 | def addPluginManager(self, plugin, user): |
|---|
| 561 | #TODO: use .linkTo on the user. To be done when the list of roles of a user is actually needed for smth... |
|---|
| 562 | self.getManagers().setdefault(plugin, []).append(user) |
|---|
| 563 | self._notifyModification() |
|---|
| 564 | |
|---|
| 565 | def removePluginManager(self, plugin, user): |
|---|
| 566 | #TODO: use .unlinkTo on the user. To be done when the list of roles of a user is actually needed for smth... |
|---|
| 567 | if user in self.getManagers().setdefault(plugin,[]): |
|---|
| 568 | self.getManagers()[plugin].remove(user) |
|---|
| 569 | self._notifyModification() |
|---|
| 570 | |
|---|
| 571 | def getVideoServicesManagers(self): |
|---|
| 572 | return self.getManagers().setdefault('all', []) |
|---|
| 573 | |
|---|
| 574 | def isVideoServicesManager(self, user): |
|---|
| 575 | return user in self.getManagers().setdefault('all', []) |
|---|
| 576 | |
|---|
| 577 | def getPluginManagers(self, plugin): |
|---|
| 578 | return self.getManagers().setdefault(plugin, []) |
|---|
| 579 | |
|---|
| 580 | def isPluginManager(self, plugin, user): |
|---|
| 581 | return user in self.getManagers().setdefault(plugin, []) |
|---|
| 582 | |
|---|
| 583 | def getAllManagers(self): |
|---|
| 584 | """ Returns a list with all the managers, no matter their type |
|---|
| 585 | The returned list is not ordered. |
|---|
| 586 | """ |
|---|
| 587 | managers = set() |
|---|
| 588 | for managerList in self.getManagers().itervalues(): |
|---|
| 589 | managers = managers.union(managerList) |
|---|
| 590 | return list(managers) |
|---|
| 591 | |
|---|
| 592 | def isPluginManagerOfAnyPlugin(self, user): |
|---|
| 593 | #TODO: this method is not optimal. to be optimal, we should store somewhere an index where the key |
|---|
| 594 | #is the user, and the value is a list of plugins where they are managers. |
|---|
| 595 | #this could be done with .getLinkTo, but we would need to change the .linkTo method to add extra information |
|---|
| 596 | #(since we cannot create a role for each plugin) |
|---|
| 597 | if self.isVideoServicesManager(user): |
|---|
| 598 | return True |
|---|
| 599 | else: |
|---|
| 600 | for plugin in self.getManagers().iterkeys(): |
|---|
| 601 | if self.isPluginManager(plugin, user): |
|---|
| 602 | return True |
|---|
| 603 | return False |
|---|
| 604 | |
|---|
| 605 | def notifyTitleChange(self, oldTitle, newTitle): |
|---|
| 606 | """ Notifies the CSBookingManager that the title of the event (meeting) it's attached to has changed. |
|---|
| 607 | The CSBookingManager will reindex all its bookings in the event title index. |
|---|
| 608 | This method will be called by the event (meeting) object |
|---|
| 609 | """ |
|---|
| 610 | for booking in self.getBookingList(): |
|---|
| 611 | try: |
|---|
| 612 | self._changeConfTitleInIndex(booking, oldTitle, newTitle) |
|---|
| 613 | except Exception, e: |
|---|
| 614 | Logger.get('VideoServ').exception("Exception while reindexing a booking in the event title index because its event's title changed: " + str(e)) |
|---|
| 615 | |
|---|
| 616 | def notifyInfoChange(self): |
|---|
| 617 | self.updateSpeakerWrapperList() |
|---|
| 618 | |
|---|
| 619 | def notifyEventDateChanges(self, oldStartDate = None, newStartDate = None, oldEndDate = None, newEndDate = None): |
|---|
| 620 | """ Notifies the CSBookingManager that the start and / or end dates of the event it's attached to have changed. |
|---|
| 621 | The CSBookingManager will change the dates of all the bookings that want to be updated. |
|---|
| 622 | If there are problems (such as a booking not being able to be modified) |
|---|
| 623 | it will write a list of strings describing the problems in the 'dateChangeNotificationProblems' context variable. |
|---|
| 624 | (each string is produced by the _booking2NotifyProblem method). |
|---|
| 625 | This method will be called by the event (meeting) object. |
|---|
| 626 | """ |
|---|
| 627 | startDateChanged = oldStartDate is not None and newStartDate is not None and not oldStartDate == newStartDate |
|---|
| 628 | endDateChanged = oldEndDate is not None and newEndDate is not None and not oldEndDate == newEndDate |
|---|
| 629 | someDateChanged = startDateChanged or endDateChanged |
|---|
| 630 | |
|---|
| 631 | Logger.get("VideoServ").info("""CSBookingManager: starting notifyEventDateChanges. Arguments: confId=%s, oldStartDate=%s, newStartDate=%s, oldEndDate=%s, newEndDate=%s""" % |
|---|
| 632 | (str(self._conf.getId()), str(oldStartDate), str(newStartDate), str(oldEndDate), str(newEndDate))) |
|---|
| 633 | |
|---|
| 634 | if someDateChanged: |
|---|
| 635 | problems = [] |
|---|
| 636 | for booking in self.getBookingList(): |
|---|
| 637 | if booking.hasStartDate(): |
|---|
| 638 | if startDateChanged: |
|---|
| 639 | try: |
|---|
| 640 | self._changeConfStartDateInIndex(booking, oldStartDate, newStartDate) |
|---|
| 641 | except Exception, e: |
|---|
| 642 | Logger.get('VideoServ').error("Exception while reindexing a booking in the event start date index because its event's start date changed: " + str(e)) |
|---|
| 643 | |
|---|
| 644 | if booking.needsToBeNotifiedOfDateChanges(): |
|---|
| 645 | Logger.get("VideoServ").info("""CSBookingManager: notifying date changes to booking %s of event %s""" % |
|---|
| 646 | (str(booking.getId()), str(self._conf.getId()))) |
|---|
| 647 | oldBookingStartDate = booking.getStartDate() |
|---|
| 648 | oldBookingEndDate = booking.getEndDate() |
|---|
| 649 | oldBookingParams = booking.getBookingParams() #this is a copy so it's ok |
|---|
| 650 | |
|---|
| 651 | if startDateChanged: |
|---|
| 652 | booking.setStartDate(oldBookingStartDate + (newStartDate - oldStartDate) ) |
|---|
| 653 | if endDateChanged: |
|---|
| 654 | booking.setEndDate(oldBookingEndDate + (newEndDate - oldEndDate) ) |
|---|
| 655 | |
|---|
| 656 | rollback = False |
|---|
| 657 | modifyResult = None |
|---|
| 658 | try: |
|---|
| 659 | modifyResult = booking._modify(oldBookingParams) |
|---|
| 660 | if isinstance(modifyResult, CSErrorBase): |
|---|
| 661 | Logger.get('VideoServ').warning("""Error while changing the dates of booking %s of event %s after event dates changed: %s""" % |
|---|
| 662 | (str(booking.getId()), str(self._conf.getId()), modifyResult.getLogMessage())) |
|---|
| 663 | rollback = True |
|---|
| 664 | except Exception, e: |
|---|
| 665 | Logger.get('VideoServ').error("""Exception while changing the dates of booking %s of event %s after event dates changed: %s""" % |
|---|
| 666 | (str(booking.getId()), str(self._conf.getId()), str(e))) |
|---|
| 667 | rollback = True |
|---|
| 668 | |
|---|
| 669 | if rollback: |
|---|
| 670 | booking.setStartDate(oldBookingStartDate) |
|---|
| 671 | booking.setEndDate(oldBookingEndDate) |
|---|
| 672 | problems.append(CSBookingManager._booking2NotifyProblem(booking, modifyResult)) |
|---|
| 673 | elif startDateChanged: |
|---|
| 674 | self._changeStartDateInIndex(booking, oldBookingStartDate, booking.getStartDate()) |
|---|
| 675 | |
|---|
| 676 | if hasattr(booking, "notifyEventDateChanges"): |
|---|
| 677 | try: |
|---|
| 678 | booking.notifyEventDateChanges(oldStartDate, newStartDate, oldEndDate, newEndDate) |
|---|
| 679 | except Exception, e: |
|---|
| 680 | Logger.get('VideoServ').exception("Exception while notifying a plugin of an event date changed: " + str(e)) |
|---|
| 681 | |
|---|
| 682 | if problems: |
|---|
| 683 | ContextManager.get('dateChangeNotificationProblems')['Collaboration'] = [ |
|---|
| 684 | 'Some Video Services bookings could not be moved:', |
|---|
| 685 | problems, |
|---|
| 686 | 'Go to [[' + str(urlHandlers.UHConfModifCollaboration.getURL(self.getOwner(), secure = ContextManager.get('currentRH').use_https())) + ' the Video Services section]] to modify them yourself.' |
|---|
| 687 | ] |
|---|
| 688 | |
|---|
| 689 | |
|---|
| 690 | def notifyTimezoneChange(self, oldTimezone, newTimezone): |
|---|
| 691 | """ Notifies the CSBookingManager that the timezone of the event it's attached to has changed. |
|---|
| 692 | The CSBookingManager will change the dates of all the bookings that want to be updated. |
|---|
| 693 | This method will be called by the event (Conference) object |
|---|
| 694 | """ |
|---|
| 695 | return [] |
|---|
| 696 | |
|---|
| 697 | def notifyLocationChange(self): |
|---|
| 698 | for booking in self.getBookingList(): |
|---|
| 699 | if hasattr(booking, "notifyLocationChange"): |
|---|
| 700 | try: |
|---|
| 701 | booking.notifyLocationChange() |
|---|
| 702 | except Exception, e: |
|---|
| 703 | Logger.get('VideoServ').exception("Exception while notifying a plugin of a location change: " + str(e)) |
|---|
| 704 | |
|---|
| 705 | @classmethod |
|---|
| 706 | def _booking2NotifyProblem(cls, booking, modifyError): |
|---|
| 707 | """ Turns a booking into a string used to tell the user |
|---|
| 708 | why a date change of a booking triggered by the event's start or end date change |
|---|
| 709 | went bad. |
|---|
| 710 | """ |
|---|
| 711 | |
|---|
| 712 | message = [] |
|---|
| 713 | message.extend(["The dates of the ", booking.getType(), " booking"]) |
|---|
| 714 | if booking.hasTitle(): |
|---|
| 715 | message.extend([': "', booking._getTitle(), '" (', booking.getStartDateAsString(), ' - ', booking.getEndDateAsString(), ')']) |
|---|
| 716 | else: |
|---|
| 717 | message.extend([' ongoing from ', booking.getStartDateAsString(), ' to ', booking.getEndDateAsString(), '']) |
|---|
| 718 | |
|---|
| 719 | message.append(' could not be changed.') |
|---|
| 720 | if modifyError and modifyError.getUserMessage(): |
|---|
| 721 | message.extend([' Reason: ', modifyError.getUserMessage()]) |
|---|
| 722 | return "".join(message) |
|---|
| 723 | |
|---|
| 724 | |
|---|
| 725 | def notifyDeletion(self): |
|---|
| 726 | """ Notifies the CSBookingManager that the Conference object it is attached to has been deleted. |
|---|
| 727 | The CSBookingManager will change the dates of all the bookings that want to be updated. |
|---|
| 728 | This method will be called by the event (Conference) object |
|---|
| 729 | """ |
|---|
| 730 | for booking in self.getBookingList(): |
|---|
| 731 | try: |
|---|
| 732 | removeResult = booking._delete() |
|---|
| 733 | if isinstance(removeResult, CSErrorBase): |
|---|
| 734 | Logger.get('VideoServ').warning("Error while deleting a booking of type %s after deleting an event: %s"%(booking.getType(), removeResult.getLogMessage() )) |
|---|
| 735 | self._unindexBooking(booking) |
|---|
| 736 | except Exception, e: |
|---|
| 737 | Logger.get('VideoServ').exception("Exception while deleting a booking of type %s after deleting an event: %s" % (booking.getType(), str(e))) |
|---|
| 738 | |
|---|
| 739 | def getEventDisplayPlugins(self, sorted = False): |
|---|
| 740 | """ Returns a list of names (strings) of plugins which have been configured |
|---|
| 741 | as showing bookings in the event display page, and which have bookings |
|---|
| 742 | already (or previously) created in the event. |
|---|
| 743 | (does not check if the bookings are hidden or not) |
|---|
| 744 | """ |
|---|
| 745 | |
|---|
| 746 | pluginsWithEventDisplay = CollaborationTools.pluginsWithEventDisplay() |
|---|
| 747 | l = [] |
|---|
| 748 | for pluginName in self._bookingsByType: |
|---|
| 749 | if pluginName in pluginsWithEventDisplay: |
|---|
| 750 | l.append(pluginName) |
|---|
| 751 | if sorted: |
|---|
| 752 | l.sort() |
|---|
| 753 | return l |
|---|
| 754 | |
|---|
| 755 | def createTestBooking(self, bookingParams = {}): |
|---|
| 756 | """ Function that creates a 'test' booking for performance test. |
|---|
| 757 | Avoids to use any of the plugins except DummyPlugin |
|---|
| 758 | """ |
|---|
| 759 | from MaKaC.plugins.Collaboration.DummyPlugin.collaboration import CSBooking as DummyBooking |
|---|
| 760 | bookingType = 'DummyPlugin' |
|---|
| 761 | newBooking = DummyBooking(bookingType, self._conf) |
|---|
| 762 | error = newBooking.setBookingParams(bookingParams) |
|---|
| 763 | if error: |
|---|
| 764 | raise CollaborationServiceException("Problem while creating a test booking") |
|---|
| 765 | else: |
|---|
| 766 | newId = self._getNewBookingId() |
|---|
| 767 | newBooking.setId(newId) |
|---|
| 768 | createResult = newBooking._create() |
|---|
| 769 | if isinstance(createResult, CSErrorBase): |
|---|
| 770 | return createResult |
|---|
| 771 | else: |
|---|
| 772 | self._bookings[newId] = newBooking |
|---|
| 773 | self._bookingsByType.setdefault(bookingType,[]).append(newId) |
|---|
| 774 | if newBooking.isHidden(): |
|---|
| 775 | self.getHiddenBookings().add(newId) |
|---|
| 776 | self._indexBooking(newBooking) |
|---|
| 777 | self._notifyModification() |
|---|
| 778 | return newBooking |
|---|
| 779 | |
|---|
| 780 | def _notifyModification(self): |
|---|
| 781 | self._p_changed = 1 |
|---|
| 782 | |
|---|
| 783 | def getSortedContributionSpeaker(self, exclusive): |
|---|
| 784 | ''' This method will create a dictionary by sorting the contribution/speakers |
|---|
| 785 | that they are in recording, webcast or in both. |
|---|
| 786 | bool: exclusive - if True, every dicts (recording, webcast, both) will |
|---|
| 787 | have different speaker list (no repetition allowed) |
|---|
| 788 | if an element is present in 'both', it will be deleted from |
|---|
| 789 | 'recording and 'webcast' |
|---|
| 790 | |
|---|
| 791 | returns d = { 'recording': {}, 'webcast' : {}, 'both': {} } |
|---|
| 792 | ''' |
|---|
| 793 | |
|---|
| 794 | d = {} |
|---|
| 795 | |
|---|
| 796 | recordingBooking = self.getSingleBooking("RecordingRequest") |
|---|
| 797 | webcastBooking = self.getSingleBooking("WebcastRequest") |
|---|
| 798 | |
|---|
| 799 | d["recording"] = recordingBooking.getContributionSpeakerSingleBooking() if recordingBooking else {} |
|---|
| 800 | d["webcast"] = webcastBooking.getContributionSpeakerSingleBooking() if webcastBooking else {} |
|---|
| 801 | |
|---|
| 802 | contributions = {} |
|---|
| 803 | ''' Look for speaker intersections between 'recording' and 'webcast' dicts |
|---|
| 804 | and put them in 'both' dict. Additionally, if any intersection has been found, |
|---|
| 805 | we exclude them from the original dictionary. |
|---|
| 806 | ''' |
|---|
| 807 | for cont in d["recording"].copy(): |
|---|
| 808 | if cont in d["webcast"].copy(): |
|---|
| 809 | # Check if same contribution/speaker in 'recording' and 'webcast' |
|---|
| 810 | intersection = set(d['recording'][cont]) & set(d['webcast'][cont]) |
|---|
| 811 | if intersection: |
|---|
| 812 | contributions[cont] = list(intersection) |
|---|
| 813 | |
|---|
| 814 | # if exclusive is True, and as we found same contribution/speaker, |
|---|
| 815 | # we delete them from 'recording' and 'webcast' dicts |
|---|
| 816 | if exclusive: |
|---|
| 817 | exclusion = set(d['recording'][cont]) ^ set(contributions[cont]) |
|---|
| 818 | if not exclusion: |
|---|
| 819 | del d["recording"][cont] |
|---|
| 820 | else: |
|---|
| 821 | d["recording"][cont] = list(exclusion) |
|---|
| 822 | |
|---|
| 823 | exclusion = set(d['webcast'][cont]) ^ set(contributions[cont]) |
|---|
| 824 | if not exclusion: |
|---|
| 825 | del d["webcast"][cont] |
|---|
| 826 | else: |
|---|
| 827 | d["webcast"][cont] = list(exclusion) |
|---|
| 828 | |
|---|
| 829 | d["both"] = contributions |
|---|
| 830 | |
|---|
| 831 | return d |
|---|
| 832 | |
|---|
| 833 | def getContributionSpeakerByType(self, requestType): |
|---|
| 834 | ''' Return a plain dict of contribution/speaker according to the requestType |
|---|
| 835 | if the request type is 'both', we need to merge the lists |
|---|
| 836 | ''' |
|---|
| 837 | d = self.getSortedContributionSpeaker(False) # We want non exclusive dict |
|---|
| 838 | |
|---|
| 839 | if requestType == "recording": |
|---|
| 840 | return d['recording'] |
|---|
| 841 | elif requestType == "webcast": |
|---|
| 842 | return d['webcast'] |
|---|
| 843 | elif requestType == "both": |
|---|
| 844 | # We merge 'recording' and 'webcast' |
|---|
| 845 | m = dict(((cont, list(set(spks) | \ |
|---|
| 846 | set(d['webcast'].get(cont, [])))) for cont, spks in d['recording'].iteritems())) |
|---|
| 847 | m.update(dict((cont, spks) for cont, spks in d['webcast'].iteritems() if cont not in m)) |
|---|
| 848 | |
|---|
| 849 | return m |
|---|
| 850 | else: |
|---|
| 851 | return {} |
|---|
| 852 | |
|---|
| 853 | def updateSpeakerWrapperList(self, newList = False): |
|---|
| 854 | """ |
|---|
| 855 | if newList arg is True, don't check if there is an existing speakerWrapperList |
|---|
| 856 | and create a new one straight forward. (Done to avoid loops) |
|---|
| 857 | """ |
|---|
| 858 | SWList = [] |
|---|
| 859 | contributions = self.getSortedContributionSpeaker(True) |
|---|
| 860 | requestType = ['recording', 'webcast', 'both'] |
|---|
| 861 | |
|---|
| 862 | for type in requestType: |
|---|
| 863 | for cont in contributions[type]: |
|---|
| 864 | for spk in contributions[type][cont]: |
|---|
| 865 | if newList: |
|---|
| 866 | sw = None |
|---|
| 867 | else: |
|---|
| 868 | sw = self.getSpeakerWrapperByUniqueId("%s.%s"%(cont, spk.getId())) |
|---|
| 869 | |
|---|
| 870 | if sw: |
|---|
| 871 | if not sw.getObject().getEmail(): |
|---|
| 872 | if sw.getStatus() not in [SpeakerStatusEnum.SIGNED, |
|---|
| 873 | SpeakerStatusEnum.FROMFILE, |
|---|
| 874 | SpeakerStatusEnum.REFUSED]: |
|---|
| 875 | sw.setStatus(SpeakerStatusEnum.NOEMAIL) |
|---|
| 876 | elif sw.getStatus() == SpeakerStatusEnum.NOEMAIL: |
|---|
| 877 | sw.setStatus(SpeakerStatusEnum.NOTSIGNED) |
|---|
| 878 | sw.setRequestType(type) |
|---|
| 879 | SWList.append(sw) |
|---|
| 880 | else: |
|---|
| 881 | newSw = SpeakerWrapper(spk, cont, type) |
|---|
| 882 | if not newSw.getObject().getEmail(): |
|---|
| 883 | newSw.setStatus(SpeakerStatusEnum.NOEMAIL) |
|---|
| 884 | SWList.append(newSw) |
|---|
| 885 | |
|---|
| 886 | self._speakerWrapperList = SWList |
|---|
| 887 | |
|---|
| 888 | def getSpeakerWrapperList(self): |
|---|
| 889 | if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe |
|---|
| 890 | self.updateSpeakerWrapperList(True) |
|---|
| 891 | |
|---|
| 892 | return self._speakerWrapperList |
|---|
| 893 | |
|---|
| 894 | def getSpeakerWrapperByUniqueId(self, id): |
|---|
| 895 | |
|---|
| 896 | if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe |
|---|
| 897 | self.updateSpeakerWrapperList(True) |
|---|
| 898 | |
|---|
| 899 | for spkWrap in self._speakerWrapperList: |
|---|
| 900 | if spkWrap.getUniqueId() == id: |
|---|
| 901 | return spkWrap |
|---|
| 902 | |
|---|
| 903 | return None |
|---|
| 904 | |
|---|
| 905 | def areSignatureCompleted(self): |
|---|
| 906 | value = True; |
|---|
| 907 | for spkWrap in self._speakerWrapperList: |
|---|
| 908 | if spkWrap.getStatus() != SpeakerStatusEnum.FROMFILE and \ |
|---|
| 909 | spkWrap.getStatus() != SpeakerStatusEnum.SIGNED: |
|---|
| 910 | value = False; |
|---|
| 911 | |
|---|
| 912 | return value |
|---|
| 913 | |
|---|
| 914 | def getSpeakerWrapperListByStatus(self, status): |
|---|
| 915 | '''Return a list of SpeakerWrapper matching the status. |
|---|
| 916 | ''' |
|---|
| 917 | list = [] |
|---|
| 918 | for spkWrap in self._speakerWrapperList: |
|---|
| 919 | if spkWrap.getStatus() == status: |
|---|
| 920 | list.append(spkWrap) |
|---|
| 921 | |
|---|
| 922 | return list |
|---|
| 923 | |
|---|
| 924 | def getSpeakerEmailByUniqueId(self, id, user): |
|---|
| 925 | ''' Return the email of a speaker according to the uniqueId. |
|---|
| 926 | id: uniqueId of the speaker wrapper. |
|---|
| 927 | user: user object of the sender of the emails, in order to check the rights. |
|---|
| 928 | ''' |
|---|
| 929 | |
|---|
| 930 | canManageRequest = CollaborationTools.getRequestTypeUserCanManage(self._conf, user) |
|---|
| 931 | requestTypeAccepted = "" |
|---|
| 932 | |
|---|
| 933 | if canManageRequest == "recording": |
|---|
| 934 | requestTypeAccepted = ["recording"] |
|---|
| 935 | elif canManageRequest == "webcast": |
|---|
| 936 | requestTypeAccepted = ["webcast"] |
|---|
| 937 | elif canManageRequest == "both": |
|---|
| 938 | requestTypeAccepted = ["recording", "webcast", "both"] |
|---|
| 939 | |
|---|
| 940 | list = [] |
|---|
| 941 | for spkWrap in self._speakerWrapperList: |
|---|
| 942 | if spkWrap.getUniqueId() == id and \ |
|---|
| 943 | spkWrap.hasEmail() and spkWrap.getStatus() not in \ |
|---|
| 944 | [SpeakerStatusEnum.SIGNED, SpeakerStatusEnum.FROMFILE] and \ |
|---|
| 945 | spkWrap.getRequestType() in requestTypeAccepted: |
|---|
| 946 | |
|---|
| 947 | list.append(spkWrap.getObject().getEmail()) |
|---|
| 948 | |
|---|
| 949 | return list |
|---|
| 950 | |
|---|
| 951 | def addVideoService(self, uniqueId, videoService): |
|---|
| 952 | """ Adds a video service to Contribution / Session link in the tracking |
|---|
| 953 | dictionary in order {uniqueId : videoService} |
|---|
| 954 | """ |
|---|
| 955 | |
|---|
| 956 | if self.getVideoServices().has_key(uniqueId): |
|---|
| 957 | self.getVideoServices()[uniqueId].append(videoService) |
|---|
| 958 | else: |
|---|
| 959 | self.getVideoServices()[uniqueId] = [videoService] |
|---|
| 960 | |
|---|
| 961 | def removeVideoAllServices(self, uniqueId): |
|---|
| 962 | """ Removes all associations of Contributions / Sessions with video |
|---|
| 963 | services from the dictionary, key included. |
|---|
| 964 | """ |
|---|
| 965 | |
|---|
| 966 | if not self.hasVideoService(uniqueId): |
|---|
| 967 | return None |
|---|
| 968 | |
|---|
| 969 | del self.getVideoServices()[uniqueId] |
|---|
| 970 | |
|---|
| 971 | def removeVideoSingleService(self, uniqueId, videoService): |
|---|
| 972 | """ Removes a specific video service from a specific contribution. As |
|---|
| 973 | the list of services is unordered, iterate through to match for |
|---|
| 974 | removal - performance cost therefore occurs here. |
|---|
| 975 | """ |
|---|
| 976 | |
|---|
| 977 | if not self.hasVideoService(uniqueId): |
|---|
| 978 | return None |
|---|
| 979 | |
|---|
| 980 | target = self.getVideoServicesById(uniqueId) |
|---|
| 981 | |
|---|
| 982 | for service in target: |
|---|
| 983 | if service == videoService: |
|---|
| 984 | target.remove(service) |
|---|
| 985 | break |
|---|
| 986 | |
|---|
| 987 | # There are no more entries, therefore remove the dictionary entry too. |
|---|
| 988 | if len(target) == 0: |
|---|
| 989 | self.removeVideoAllServices(uniqueId) |
|---|
| 990 | |
|---|
| 991 | def getVideoServices(self): |
|---|
| 992 | """ Returns the OOBTree associating event unique IDs with the List |
|---|
| 993 | of video services associated. |
|---|
| 994 | """ |
|---|
| 995 | |
|---|
| 996 | if not hasattr(self, "_bookingsToVideoServices"): |
|---|
| 997 | self._bookingsToVideoServices = OOBTree() |
|---|
| 998 | |
|---|
| 999 | return self._bookingsToVideoServices |
|---|
| 1000 | |
|---|
| 1001 | def getVideoServicesById(self, uniqueId): |
|---|
| 1002 | """ Returns a list of video services associated with the uniqueId |
|---|
| 1003 | for printing in event timetable. Returns None if no video services |
|---|
| 1004 | are found. |
|---|
| 1005 | """ |
|---|
| 1006 | |
|---|
| 1007 | if not self.hasVideoService(uniqueId): |
|---|
| 1008 | return None |
|---|
| 1009 | |
|---|
| 1010 | return self.getVideoServices()[uniqueId] |
|---|
| 1011 | |
|---|
| 1012 | def hasVideoService(self, uniqueId, service=None): |
|---|
| 1013 | """ Returns True if the uniqueId of the Contribution or Session provided |
|---|
| 1014 | has an entry in the self._bookingsToVideoServices dictionary, thusly |
|---|
| 1015 | denoting the presence of linked bookings. Second parameter is for more |
|---|
| 1016 | specific matching, i.e. returns True if unique ID is associated with |
|---|
| 1017 | specific service. |
|---|
| 1018 | """ |
|---|
| 1019 | if service is None: |
|---|
| 1020 | return self.getVideoServices().has_key(uniqueId) |
|---|
| 1021 | |
|---|
| 1022 | if self.getVideoServices().has_key(uniqueId): |
|---|
| 1023 | for serv in self.getVideoServicesById(uniqueId): |
|---|
| 1024 | if serv == service: |
|---|
| 1025 | return True |
|---|
| 1026 | else: |
|---|
| 1027 | return self.getVideoServices().has_key(uniqueId) |
|---|
| 1028 | |
|---|
| 1029 | def isAnyRequestAccepted(self): |
|---|
| 1030 | ''' |
|---|
| 1031 | Return true if at least one between recording and webcast request |
|---|
| 1032 | has been accepted. |
|---|
| 1033 | ''' |
|---|
| 1034 | value = False |
|---|
| 1035 | rr = self.getSingleBooking("RecordingRequest") |
|---|
| 1036 | wr = self.getSingleBooking("WebcastRequest") |
|---|
| 1037 | |
|---|
| 1038 | if rr: |
|---|
| 1039 | value = rr.getAcceptRejectStatus() |
|---|
| 1040 | |
|---|
| 1041 | if wr: |
|---|
| 1042 | value = value or wr.getAcceptRejectStatus() |
|---|
| 1043 | |
|---|
| 1044 | return value |
|---|
| 1045 | |
|---|
| 1046 | def isContributionReadyToBePublished(self, contId): |
|---|
| 1047 | if not hasattr(self, "_speakerWrapperList"):#TODO: remove when safe |
|---|
| 1048 | self.updateSpeakerWrapperList(True) |
|---|
| 1049 | |
|---|
| 1050 | exists = False |
|---|
| 1051 | for spkWrap in self._speakerWrapperList: |
|---|
| 1052 | if spkWrap.getContId() == contId: |
|---|
| 1053 | exists = True |
|---|
| 1054 | if spkWrap.getStatus() != SpeakerStatusEnum.SIGNED and \ |
|---|
| 1055 | spkWrap.getStatus() != SpeakerStatusEnum.FROMFILE: |
|---|
| 1056 | return False |
|---|
| 1057 | |
|---|
| 1058 | #The list has to have at least one spkWrap with the given contId |
|---|
| 1059 | return exists |
|---|
| 1060 | |
|---|
| 1061 | class CSBookingBase(Persistent, Fossilizable): |
|---|
| 1062 | fossilizes(ICSBookingBaseConfModifFossil, ICSBookingBaseIndexingFossil) |
|---|
| 1063 | |
|---|
| 1064 | """ Base class that represents a Collaboration Systems booking. |
|---|
| 1065 | Every Collaboration plugin will have to implement this class. |
|---|
| 1066 | In the base class are gathered all the functionalities / elements that are common for all plugins. |
|---|
| 1067 | A booking is Persistent (DateChangeObserver inherits from Persistent) so it will be stored in the database. |
|---|
| 1068 | Also, every CSBookingBase object in the server will be mirrored by a Javascript object in the client, through "Pickling". |
|---|
| 1069 | |
|---|
| 1070 | Every class that implements the CSBookingBase has to declare the following class attributes: |
|---|
| 1071 | _hasStart : True if the plugin has a "start" concept. Otherwise, the "start" button will not appear, etc. |
|---|
| 1072 | _hasStop : True if the plugin has a "stop" concept. Otherwise, the "stop" button will not appear, etc. |
|---|
| 1073 | _hasConnect : True if the plugin has a "connect" concept. Otherwise, the "connect" button will not appear, etc. |
|---|
| 1074 | _hasCheckStatus: True if the plugin has a "check status" concept. Otherwise, the "check status" button will not appear, etc. |
|---|
| 1075 | _hasAcceptReject: True if the plugin has a "accept or reject" concept. Otherwise, the "accept" and "reject" buttons will not appear, etc. |
|---|
| 1076 | _requiresServerCallForStart : True if we should notify the server when the user presses the "start" button. |
|---|
| 1077 | _requiresServerCallForStop : True if we should notify the server when the user presses the "stop" button. |
|---|
| 1078 | _requiresServerCallForConnect : True if we should notify the server when the user presses the "connect" button. |
|---|
| 1079 | _requiresClientCallForStart : True if the browser should execute some JS action when the user presses the "start" button. |
|---|
| 1080 | _requiresClientCallForStop : True if the browser should execute some JS action when the user presses the "stop" button. |
|---|
| 1081 | _requiresClientCallForConnect : True if the browser should execute some JS action when the user presses the "connect" button. |
|---|
| 1082 | _needsBookingParamsCheck : True if the booking parameters should be checked after the booking is added / edited. |
|---|
| 1083 | If True, the _checkBookingParams method will be called by the setBookingParams method. |
|---|
| 1084 | _needsToBeNotifiedOnView: True if the booking object needs to be notified (through the "notifyOnView" method) |
|---|
| 1085 | when the user "sees" the booking, for example when returning the list of bookings. |
|---|
| 1086 | _canBeNotifiedOfEventDateChanges: True if bookings of this type should be able to be notified |
|---|
| 1087 | of their owner Event changing start date, end date or timezone. |
|---|
| 1088 | _allowMultiple: True if this booking type allows more than 1 booking per event. |
|---|
| 1089 | """ |
|---|
| 1090 | |
|---|
| 1091 | _hasStart = False |
|---|
| 1092 | _hasStop = False |
|---|
| 1093 | _hasConnect = False |
|---|
| 1094 | _hasCheckStatus = False |
|---|
| 1095 | _hasAcceptReject = False |
|---|
| 1096 | _hasStartStopAll = False |
|---|
| 1097 | _requiresServerCallForStart = False |
|---|
| 1098 | _requiresServerCallForStop = False |
|---|
| 1099 | _requiresServerCallForConnect = False |
|---|
| 1100 | _requiresClientCallForStart = False |
|---|
| 1101 | _requiresClientCallForStop = False |
|---|
| 1102 | _requiresClientCallForConnect = False |
|---|
| 1103 | _needsBookingParamsCheck = False |
|---|
| 1104 | _needsToBeNotifiedOnView = False |
|---|
| 1105 | _canBeNotifiedOfEventDateChanges = True |
|---|
| 1106 | _allowMultiple = True |
|---|
| 1107 | _shouldBeIndexed = True |
|---|
| 1108 | _commonIndexes = [] |
|---|
| 1109 | _hasStartDate = True |
|---|
| 1110 | _hasEventDisplay = False |
|---|
| 1111 | _hasTitle = False |
|---|
| 1112 | _adminOnly = False |
|---|
| 1113 | _complexParameters = [] |
|---|
| 1114 | _linkVideoType = None |
|---|
| 1115 | _linkVideoId = None |
|---|
| 1116 | |
|---|
| 1117 | def __init__(self, bookingType, conf): |
|---|
| 1118 | """ Constructor for the CSBookingBase class. |
|---|
| 1119 | id: a string with the id of the booking |
|---|
| 1120 | bookingType: a string with the type of the booking. Example: "DummyPlugin", "EVO" |
|---|
| 1121 | conf: a Conference object to which this booking belongs (through the CSBookingManager object). The meeting of this booking. |
|---|
| 1122 | startTime: TODO |
|---|
| 1123 | endTime: TODO |
|---|
| 1124 | |
|---|
| 1125 | Other attributes initialized by this constructor: |
|---|
| 1126 | -_bookingParams: the parameters necessary to perform the booking. |
|---|
| 1127 | The plugins will decide if the booking gets authorized or not depending on this. |
|---|
| 1128 | Needs to be defined by the implementing class, as keys with empty values. |
|---|
| 1129 | -_startingParams: the parameters necessary to start the booking. |
|---|
| 1130 | They will be used on the client for the local start action. |
|---|
| 1131 | Needs to be defined by the implementing class, as keys with empty values. |
|---|
| 1132 | -_warning: A warning is a plugin-defined object, with information to show to the user when |
|---|
| 1133 | the operation went well but we still have to show some info to the user. |
|---|
| 1134 | -_permissionToStart : Even if the "start" button for a booking is able to be pushed, there may be cases where the booking should |
|---|
| 1135 | not start. For example, if it's not the correct time yet. |
|---|
| 1136 | In that case "permissionToStart" should be set to false so that the booking doesn't start. |
|---|
| 1137 | -_permissionToStop: Same as permissionToStart. Sometimes the booking should not be allowed to stop even if the "stop" button is available. |
|---|
| 1138 | -_permissionToConnect: Same as permissionToStart. Sometimes the booking should not be allowed to connect even if the "connect" button is available. |
|---|
| 1139 | """ |
|---|
| 1140 | self._id = None |
|---|
| 1141 | self._type = bookingType |
|---|
| 1142 | self._plugin = CollaborationTools.getPlugin(self._type) |
|---|
| 1143 | self._conf = conf |
|---|
| 1144 | self._warning = None |
|---|
| 1145 | self._creationDate = nowutc() |
|---|
| 1146 | self._modificationDate = nowutc() |
|---|
| 1147 | self._creationDateTimestamp = int(datetimeToUnixTimeInt(self._creationDate)) |
|---|
| 1148 | self._modificationDateTimestamp = int(datetimeToUnixTimeInt(self._modificationDate)) |
|---|
| 1149 | self._startDate = None |
|---|
| 1150 | self._endDate = None |
|---|
| 1151 | self._startDateTimestamp = None |
|---|
| 1152 | self._endDateTimestamp = None |
|---|
| 1153 | self._acceptRejectStatus = None #None = not yet accepted / rejected; True = accepted; False = rejected |
|---|
| 1154 | self._rejectReason = "" |
|---|
| 1155 | self._bookingParams = {} |
|---|
| 1156 | self._canBeDeleted = True |
|---|
| 1157 | self._permissionToStart = False |
|---|
| 1158 | self._permissionToStop = False |
|---|
| 1159 | self._permissionToConnect = False |
|---|
| 1160 | self._needsToBeNotifiedOfDateChanges = self._canBeNotifiedOfEventDateChanges |
|---|
| 1161 | self._hidden = False |
|---|
| 1162 | self._play_status = None |
|---|
| 1163 | |
|---|
| 1164 | setattr(self, "_" + bookingType + "Options", CollaborationTools.getPlugin(bookingType).getOptions()) |
|---|
| 1165 | #NOTE: Should maybe notify the creation of a new booking, specially if it's a single booking |
|---|
| 1166 | # like that can update requestType of the speaker wrapper... |
|---|
| 1167 | |
|---|
| 1168 | def getId(self): |
|---|
| 1169 | """ Returns the internal, per-conference id of the booking. |
|---|
| 1170 | This attribute will be available in Javascript with the "id" identifier. |
|---|
| 1171 | """ |
|---|
| 1172 | return self._id |
|---|
| 1173 | |
|---|
| 1174 | def setId(self, id): |
|---|
| 1175 | """ Sets the internal, per-conference id of the booking |
|---|
| 1176 | """ |
|---|
| 1177 | self._id = id |
|---|
| 1178 | |
|---|
| 1179 | def getUniqueId(self): |
|---|
| 1180 | """ Returns an unique Id that identifies this booking server-wide. |
|---|
| 1181 | Useful for ExternalOperationsManager |
|---|
| 1182 | """ |
|---|
| 1183 | return "%scsbook%s" % (self.getConference().getUniqueId(), self.getId()) |
|---|
| 1184 | |
|---|
| 1185 | def getType(self): |
|---|
| 1186 | """ Returns the type of the booking, as a string: "EVO", "DummyPlugin" |
|---|
| 1187 | This attribute will be available in Javascript with the "type" identifier. |
|---|
| 1188 | """ |
|---|
| 1189 | return self._type |
|---|
| 1190 | |
|---|
| 1191 | def getConference(self): |
|---|
| 1192 | """ Returns the owner of this CSBookingBase object, which is a Conference object representing the meeting. |
|---|
| 1193 | """ |
|---|
| 1194 | return self._conf |
|---|
| 1195 | |
|---|
| 1196 | def setConference(self, conf): |
|---|
| 1197 | """ Sets the owner of this CSBookingBase object, which is a Conference object representing the meeting. |
|---|
| 1198 | """ |
|---|
| 1199 | self._conf = conf |
|---|
| 1200 | |
|---|
| 1201 | def getWarning(self): |
|---|
| 1202 | """ Returns a warning attached to this booking. |
|---|
| 1203 | A warning is a plugin-defined object, with information to show to the user when |
|---|
| 1204 | the operation went well but we still have to show some info to the user. |
|---|
| 1205 | To be overloaded by plugins. |
|---|
| 1206 | """ |
|---|
| 1207 | if not hasattr(self, '_warning'): |
|---|
| 1208 | self._warning = None |
|---|
| 1209 | return self._warning |
|---|
| 1210 | |
|---|
| 1211 | def setWarning(self, warning): |
|---|
| 1212 | """ Sets a warning attached to this booking. |
|---|
| 1213 | A warning is a plugin-defined object, with information to show to the user when |
|---|
| 1214 | the operation went well but we still have to show some info to the user. |
|---|
| 1215 | To be overloaded by plugins. |
|---|
| 1216 | """ |
|---|
| 1217 | self._warning = warning |
|---|
| 1218 | |
|---|
| 1219 | def getCreationDate(self): |
|---|
| 1220 | """ Returns the date this booking was created, as a timezone localized datetime object |
|---|
| 1221 | """ |
|---|
| 1222 | if not hasattr(self, "_creationDate"): #TODO: remove when safe |
|---|
| 1223 | self._creationDate = nowutc() |
|---|
| 1224 | return self._creationDate |
|---|
| 1225 | |
|---|
| 1226 | def getAdjustedCreationDate(self, tz=None): |
|---|
| 1227 | """ Returns the booking creation date, adjusted to a given timezone. |
|---|
| 1228 | If no timezone is provided, the event's timezone is used |
|---|
| 1229 | """ |
|---|
| 1230 | return getAdjustedDate(self.getCreationDate(), self.getConference(), tz) |
|---|
| 1231 | |
|---|
| 1232 | def getCreationDateTimestamp(self): |
|---|
| 1233 | if not hasattr(object, "_creationDateTimestamp"): #TODO: remove when safe |
|---|
| 1234 | self._creationDateTimestamp = int(datetimeToUnixTimeInt(self._creationDate)) |
|---|
| 1235 | return self._creationDateTimestamp |
|---|
| 1236 | |
|---|
| 1237 | def getModificationDate(self): |
|---|
| 1238 | """ Returns the date this booking was modified last |
|---|
| 1239 | """ |
|---|
| 1240 | if not hasattr(self, "_modificationDate"): #TODO: remove when safe |
|---|
| 1241 | self._modificationDate = nowutc() |
|---|
| 1242 | return self._modificationDate |
|---|
| 1243 | |
|---|
| 1244 | def getAdjustedModificationDate(self, tz=None): |
|---|
| 1245 | """ Returns the booking last modification date, adjusted to a given timezone. |
|---|
| 1246 | If no timezone is provided, the event's timezone is used |
|---|
| 1247 | """ |
|---|
| 1248 | return getAdjustedDate(self.getModificationDate(), self.getConference(), tz) |
|---|
| 1249 | |
|---|
| 1250 | def getModificationDateTimestamp(self): |
|---|
| 1251 | if not hasattr(object, "_modificationDateTimestamp"): #TODO: remove when safe |
|---|
| 1252 | self._modificationDateTimestamp = int(datetimeToUnixTimeInt(self._modificationDate)) |
|---|
| 1253 | return self._modificationDateTimestamp |
|---|
| 1254 | |
|---|
| 1255 | def setModificationDate(self, date): |
|---|
| 1256 | """ Sets the date this booking was modified last |
|---|
| 1257 | """ |
|---|
| 1258 | self._modificationDate = date |
|---|
| 1259 | if date: |
|---|
| 1260 | self._modificationDateTimestamp = int(datetimeToUnixTimeInt(date)) |
|---|
| 1261 | else: |
|---|
| 1262 | self._modificationDateTimestamp = None |
|---|
| 1263 | |
|---|
| 1264 | def getBookingsOfSameType(self, sorted = False): |
|---|
| 1265 | """ Returns a list of the bookings of the same type as this one (including this one) |
|---|
| 1266 | sorted: if true, bookings will be sorted by id |
|---|
| 1267 | """ |
|---|
| 1268 | return self._conf.getCSBookingManager().getBookingList(sorted, self._type) |
|---|
| 1269 | |
|---|
| 1270 | def getPlugin(self): |
|---|
| 1271 | """ Returns the Plugin object associated to this booking. |
|---|
| 1272 | """ |
|---|
| 1273 | return self._plugin |
|---|
| 1274 | |
|---|
| 1275 | def setPlugin(self, plugin): |
|---|
| 1276 | """ Sets the Plugin object associated to this booking. |
|---|
| 1277 | """ |
|---|
| 1278 | self._plugin = plugin |
|---|
| 1279 | |
|---|
| 1280 | def getPluginOptions(self): |
|---|
| 1281 | """ Utility method that returns the plugin options for this booking's type of plugin |
|---|
| 1282 | """ |
|---|
| 1283 | return self._plugin.getOptions() |
|---|
| 1284 | |
|---|
| 1285 | def getPluginOptionByName(self, optionName): |
|---|
| 1286 | """ Utility method that returns a plugin option, given its name, for this booking's type of plugin |
|---|
| 1287 | """ |
|---|
| 1288 | return self.getPluginOptions()[optionName] |
|---|
| 1289 | |
|---|
| 1290 | def getStartDate(self): |
|---|
| 1291 | """ Returns the start date as an datetime object with timezone information (adjusted to the meeting's timezone) |
|---|
| 1292 | """ |
|---|
| 1293 | return self._startDate |
|---|
| 1294 | |
|---|
| 1295 | def getAdjustedStartDate(self, tz=None): |
|---|
| 1296 | """ Returns the booking start date, adjusted to a given timezone. |
|---|
| 1297 | If no timezone is provided, the event's timezone is used |
|---|
| 1298 | """ |
|---|
| 1299 | if self.getStartDate(): |
|---|
| 1300 | return getAdjustedDate(self.getStartDate(), self.getConference(), tz) |
|---|
| 1301 | else: |
|---|
| 1302 | return None |
|---|
| 1303 | |
|---|
| 1304 | def getStartDateTimestamp(self): |
|---|
| 1305 | if not hasattr(object, "_startDateTimestamp"): #TODO: remove when safe |
|---|
| 1306 | self._startDateTimestamp = int(datetimeToUnixTimeInt(self._startDate)) |
|---|
| 1307 | return self._startDateTimestamp |
|---|
| 1308 | |
|---|
| 1309 | def setStartDateTimestamp(self, startDateTimestamp): |
|---|
| 1310 | self._startDateTimestamp = startDateTimestamp |
|---|
| 1311 | |
|---|
| 1312 | def getStartDateAsString(self): |
|---|
| 1313 | """ Returns the start date as a string, expressed in the meeting's timezone |
|---|
| 1314 | """ |
|---|
| 1315 | if self._startDate == None: |
|---|
| 1316 | return "" |
|---|
| 1317 | else: |
|---|
| 1318 | return formatDateTime(self.getAdjustedStartDate(), locale='en_US') |
|---|
| 1319 | |
|---|
| 1320 | def setStartDate(self, startDate): |
|---|
| 1321 | """ Sets the start date as an datetime object with timezone information (adjusted to the meeting's timezone) |
|---|
| 1322 | """ |
|---|
| 1323 | self._startDate = startDate |
|---|
| 1324 | if startDate: |
|---|
| 1325 | self._startDateTimestamp = int(datetimeToUnixTimeInt(startDate)) |
|---|
| 1326 | else: |
|---|
| 1327 | self._startDateTimestamp = None |
|---|
| 1328 | |
|---|
| 1329 | def setStartDateFromString(self, startDateString): |
|---|
| 1330 | """ Sets the start date from a string. It is assumed that the date is expressed in the meeting's timezone |
|---|
| 1331 | """ |
|---|
| 1332 | if startDateString == "": |
|---|
| 1333 | self.setStartDate(None) |
|---|
| 1334 | else: |
|---|
| 1335 | try: |
|---|
| 1336 | self.setStartDate(setAdjustedDate(parseDateTime(startDateString), self._conf)) |
|---|
| 1337 | except ValueError: |
|---|
| 1338 | raise CollaborationServiceException("startDate parameter (" + startDateString +" ) is in an incorrect format for booking with id: " + str(self._id)) |
|---|
| 1339 | |
|---|
| 1340 | def getEndDate(self): |
|---|
| 1341 | """ Returns the end date as an datetime object with timezone information (adjusted to the meeting's timezone) |
|---|
| 1342 | """ |
|---|
| 1343 | return self._endDate |
|---|
| 1344 | |
|---|
| 1345 | def isHappeningNow(self): |
|---|
| 1346 | now = nowutc() |
|---|
| 1347 | return self.getStartDate() < now and self.getEndDate() > now |
|---|
| 1348 | |
|---|
| 1349 | def hasHappened(self): |
|---|
| 1350 | now = nowutc() |
|---|
| 1351 | return now > self.getEndDate() |
|---|
| 1352 | |
|---|
| 1353 | def getAdjustedEndDate(self, tz=None): |
|---|
| 1354 | """ Returns the booking end date, adjusted to a given timezone. |
|---|
| 1355 | If no timezone is provided, the event's timezone is used |
|---|
| 1356 | """ |
|---|
| 1357 | return getAdjustedDate(self.getEndDate(), self.getConference(), tz) |
|---|
| 1358 | |
|---|
| 1359 | def getEndDateTimestamp(self): |
|---|
| 1360 | if not hasattr(object, "_endDateTimestamp"): #TODO: remove when safe |
|---|
| 1361 | self._endDateTimestamp = int(datetimeToUnixTimeInt(self._endDate)) |
|---|
| 1362 | return self._endDateTimestamp |
|---|
| 1363 | |
|---|
| 1364 | def setEndDateTimestamp(self, endDateTimestamp): |
|---|
| 1365 | self._endDateTimestamp = endDateTimestamp |
|---|
| 1366 | |
|---|
| 1367 | def getEndDateAsString(self): |
|---|
| 1368 | """ Returns the start date as a string, expressed in the meeting's timezone |
|---|
| 1369 | """ |
|---|
| 1370 | if self._endDate == None: |
|---|
| 1371 | return "" |
|---|
| 1372 | else: |
|---|
| 1373 | return formatDateTime(self.getAdjustedEndDate(), locale='en_US') |
|---|
| 1374 | |
|---|
| 1375 | def setEndDate(self, endDate): |
|---|
| 1376 | """ Sets the start date as an datetime object with timezone information (adjusted to the meeting's timezone) |
|---|
| 1377 | """ |
|---|
| 1378 | self._endDate = endDate |
|---|
| 1379 | if endDate: |
|---|
| 1380 | self._endDateTimestamp = int(datetimeToUnixTimeInt(endDate)) |
|---|
| 1381 | else: |
|---|
| 1382 | self._endDateTimestamp = None |
|---|
| 1383 | |
|---|
| 1384 | def setEndDateFromString(self, endDateString): |
|---|
| 1385 | """ Sets the start date from a string. It is assumed that the date is expressed in the meeting's timezone |
|---|
| 1386 | """ |
|---|
| 1387 | if endDateString == "": |
|---|
| 1388 | self.setEndDate(None) |
|---|
| 1389 | else: |
|---|
| 1390 | try: |
|---|
| 1391 | self.setEndDate(setAdjustedDate(parseDateTime(endDateString), self._conf)) |
|---|
| 1392 | except ValueError: |
|---|
| 1393 | raise CollaborationServiceException("endDate parameter (" + endDateString +" ) is in an incorrect format for booking with id: " + str(self._id)) |
|---|
| 1394 | |
|---|
| 1395 | def getStatusMessage(self): |
|---|
| 1396 | """ Returns the status message as a string. |
|---|
| 1397 | This attribute will be available in Javascript with the "statusMessage" |
|---|
| 1398 | """ |
|---|
| 1399 | status = self.getPlayStatus() |
|---|
| 1400 | if status == None: |
|---|
| 1401 | if self.isHappeningNow(): |
|---|
| 1402 | return _("Ready to start!") |
|---|
| 1403 | elif self.hasHappened(): |
|---|
| 1404 | return _("Already took place") |
|---|
| 1405 | else: |
|---|
| 1406 | return _("Booking created") |
|---|
| 1407 | elif status: |
|---|
| 1408 | return _("Conference started") |
|---|
| 1409 | elif not status: |
|---|
| 1410 | return _("Conference stopped") |
|---|
| 1411 | |
|---|
| 1412 | def getStatusClass(self): |
|---|
| 1413 | """ Returns the status message CSS class as a string. |
|---|
| 1414 | This attribute will be available in Javascript with the "statusClass" |
|---|
| 1415 | """ |
|---|
| 1416 | if self.getPlayStatus() == None or self.hasHappened(): |
|---|
| 1417 | return "statusMessageOther" |
|---|
| 1418 | else: |
|---|
| 1419 | return "statusMessageOK" |
|---|
| 1420 | |
|---|
| 1421 | def accept(self, user = None): |
|---|
| 1422 | """ Sets this booking as accepted |
|---|
| 1423 | """ |
|---|
| 1424 | self._acceptRejectStatus = True |
|---|
| 1425 | self._accept(user) |
|---|
| 1426 | |
|---|
| 1427 | def reject(self, reason): |
|---|
| 1428 | """ Sets this booking as rejected, and stores the reason |
|---|
| 1429 | """ |
|---|
| 1430 | self._acceptRejectStatus = False |
|---|
| 1431 | self._rejectReason = reason |
|---|
| 1432 | self._reject() |
|---|
| 1433 | |
|---|
| 1434 | def clearAcceptRejectStatus(self): |
|---|
| 1435 | """ Sets back the accept / reject status to None |
|---|
| 1436 | """ |
|---|
| 1437 | self._acceptRejectStatus = None |
|---|
| 1438 | |
|---|
| 1439 | def getAcceptRejectStatus(self): |
|---|
| 1440 | """ Returns the Accept/Reject status of the booking |
|---|
| 1441 | This attribute will be available in Javascript with the "acceptRejectStatus" |
|---|
| 1442 | Its value will be: |
|---|
| 1443 | -None if the booking has not been accepted or rejected yet, |
|---|
| 1444 | -True if it has been accepted, |
|---|
| 1445 | -False if it has been rejected |
|---|
| 1446 | """ |
|---|
| 1447 | if not hasattr(self, "_acceptRejectStatus"): |
|---|
| 1448 | self._acceptRejectStatus = None |
|---|
| 1449 | return self._acceptRejectStatus |
|---|
| 1450 | |
|---|
| 1451 | def getRejectReason(self): |
|---|
| 1452 | """ Returns the rejection reason. |
|---|
| 1453 | This attribute will be available in Javascript with the "rejectReason" |
|---|
| 1454 | """ |
|---|
| 1455 | if not hasattr(self, "_rejectReason"): |
|---|
| 1456 | self._rejectReason = "" |
|---|
| 1457 | return self._rejectReason |
|---|
| 1458 | |
|---|
| 1459 | ## methods relating to the linking of CSBooking objects to Contributions & Sessions |
|---|
| 1460 | |
|---|
| 1461 | def hasSessionOrContributionLink(self): |
|---|
| 1462 | return (self.isLinkedToContribution() or self.isLinkedToSession()) |
|---|
| 1463 | |
|---|
| 1464 | def isLinkedToSession(self): |
|---|
| 1465 | return (self._linkVideoType == "session") |
|---|
| 1466 | |
|---|
| 1467 | def isLinkedToContribution(self): |
|---|
| 1468 | return (self._linkVideoType == "contribution") |
|---|
| 1469 | |
|---|
| 1470 | def getLinkId(self): |
|---|
| 1471 | """ Returns the unique ID of the Contribution or Session which this |
|---|
| 1472 | object is associated with, completely agnostic of the link type. |
|---|
| 1473 | Returns None if no association (default) found. |
|---|
| 1474 | """ |
|---|
| 1475 | if not self.hasSessionOrContributionLink(): |
|---|
| 1476 | return None |
|---|
| 1477 | |
|---|
| 1478 | return self._linkVideoId |
|---|
| 1479 | |
|---|
| 1480 | def getLinkIdDict(self): |
|---|
| 1481 | """ Returns a dictionary of structure linkType (session | contribution) |
|---|
| 1482 | : unique ID of referenced object. |
|---|
| 1483 | Returns None if no association is found. |
|---|
| 1484 | """ |
|---|
| 1485 | linkId = self.getLinkId() |
|---|
| 1486 | |
|---|
| 1487 | if linkId == None: |
|---|
| 1488 | return linkId |
|---|
| 1489 | |
|---|
| 1490 | return {self._linkVideoType : linkId} |
|---|
| 1491 | |
|---|
| 1492 | def getLinkType(self): |
|---|
| 1493 | """ Returns a string denoting the link type, that is whether linked |
|---|
| 1494 | to a session or contribution. |
|---|
| 1495 | """ |
|---|
| 1496 | |
|---|
| 1497 | return self._linkVideoType |
|---|
| 1498 | |
|---|
| 1499 | def setLinkType(self, linkDict): |
|---|
| 1500 | """ Accepts a dictionary of linkType: linkId """ |
|---|
| 1501 | self._linkVideoType = linkDict.keys()[0] |
|---|
| 1502 | self._linkVideoId = linkDict.values()[0] |
|---|
| 1503 | |
|---|
| 1504 | def resetLinkParams(self): |
|---|
| 1505 | """ Removes all association with a Session or Contribution from this |
|---|
| 1506 | CSBooking only. |
|---|
| 1507 | """ |
|---|
| 1508 | |
|---|
| 1509 | self._linkVideoType = self._linkVideoId = None |
|---|
| 1510 | |
|---|
| 1511 | def getBookingParams(self): |
|---|
| 1512 | """ Returns a dictionary with the booking params. |
|---|
| 1513 | This attribute will be available in Javascript with the "bookingParams" |
|---|
| 1514 | |
|---|
| 1515 | If self._bookingParams has not been set by the implementing class, an exception is thrown. |
|---|
| 1516 | |
|---|
| 1517 | Support for "complex" parameters, that are not defined in the self._bookingParams dict, but can |
|---|
| 1518 | be retrieved through getter methods. |
|---|
| 1519 | If a subclass defines a class attributes called _complexParameters (a list of strings), |
|---|
| 1520 | parameter names that are in this list will also be included in the returned dictionary. |
|---|
| 1521 | Their value will be retrieved by calling the corresponding getXXX methods |
|---|
| 1522 | but instead the inheriting class's setXXX method will be called. |
|---|
| 1523 | Example: _complexParameters = ["communityName", "accessPassword", "hasAccessPassword"] correspond |
|---|
| 1524 | to the methods getCommunityName, getAccessPassword, getHasAccessPassword. |
|---|
| 1525 | If you include a parameter in the _complexParameters list, you always have to implement the corresponding getter method. |
|---|
| 1526 | """ |
|---|
| 1527 | bookingParams = {} |
|---|
| 1528 | for k, v in self.__class__._simpleParameters.iteritems(): |
|---|
| 1529 | if k in self._bookingParams: |
|---|
| 1530 | value = self._bookingParams[k] |
|---|
| 1531 | else: |
|---|
| 1532 | value = v[1] #we use the default value |
|---|
| 1533 | if v[0] is bool and value is True: #we assume it will be used in a single checkbox |
|---|
| 1534 | value = ["yes"] |
|---|
| 1535 | if value is not False: #we do not include False, it means the single checkbox is not checked |
|---|
| 1536 | bookingParams[k] = value |
|---|
| 1537 | |
|---|
| 1538 | if hasattr(self.__class__, "_complexParameters") and len(self.__class__._complexParameters) > 0: |
|---|
| 1539 | getterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('get'))) |
|---|
| 1540 | for paramName in self.__class__._complexParameters: |
|---|
| 1541 | getMethodName = 'get' + paramName[0].upper() + paramName[1:] |
|---|
| 1542 | if getMethodName in getterMethods: |
|---|
| 1543 | bookingParams[paramName] = getterMethods[getMethodName]() |
|---|
| 1544 | else: |
|---|
| 1545 | raise CollaborationServiceException("Tried to retrieve complex parameter " + str(paramName) + " but the corresponding getter method " + getMethodName + " is not implemented") |
|---|
| 1546 | |
|---|
| 1547 | bookingParams["startDate"] = self.getStartDateAsString() |
|---|
| 1548 | bookingParams["endDate"] = self.getEndDateAsString() |
|---|
| 1549 | if self.needsToBeNotifiedOfDateChanges(): |
|---|
| 1550 | bookingParams["notifyOnDateChanges"] = ["yes"] |
|---|
| 1551 | if self.isHidden(): |
|---|
| 1552 | bookingParams["hidden"] = ["yes"] |
|---|
| 1553 | return bookingParams |
|---|
| 1554 | |
|---|
| 1555 | |
|---|
| 1556 | def getBookingParamByName(self, paramName): |
|---|
| 1557 | if paramName in self.__class__._simpleParameters: |
|---|
| 1558 | if not paramName in self._bookingParams: |
|---|
| 1559 | self._bookingParams[paramName] = self.__class__._simpleParameters[paramName][1] |
|---|
| 1560 | return self._bookingParams[paramName] |
|---|
| 1561 | elif hasattr(self.__class__, "_complexParameters") and paramName in self.__class__._complexParameters: |
|---|
| 1562 | getterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('get'))) |
|---|
| 1563 | getMethodName = 'get' + paramName[0].upper() + paramName[1:] |
|---|
| 1564 | if getMethodName in getterMethods: |
|---|
| 1565 | return getterMethods[getMethodName]() |
|---|
| 1566 | else: |
|---|
| 1567 | raise CollaborationServiceException("Tried to retrieve complex parameter " + str(paramName) + " but the corresponding getter method " + getMethodName + " is not implemented") |
|---|
| 1568 | else: |
|---|
| 1569 | raise CollaborationServiceException("Tried to retrieve parameter " + str(paramName) + " but this parameter does not exist") |
|---|
| 1570 | |
|---|
| 1571 | def getContributionSpeakerSingleBooking(self): |
|---|
| 1572 | ''' Return a dictionnary with the contributions and their speakers that need to be recorded |
|---|
| 1573 | e.g: {contId:[Spk1Object, Spk2Object, Spk3Object], cont2:[Spk1Object]}... |
|---|
| 1574 | ''' |
|---|
| 1575 | request = {} |
|---|
| 1576 | |
|---|
| 1577 | recordingTalksChoice = self.getBookingParams()["talks"] #either "all", "choose" or "" |
|---|
| 1578 | listTalksToRecord = self.getBookingParams()["talkSelection"] |
|---|
| 1579 | |
|---|
| 1580 | if self._conf.getType() == "simple_event": |
|---|
| 1581 | request[self._conf.getId()] = [] |
|---|
| 1582 | for chair in self._conf.getChairList(): |
|---|
| 1583 | request[self._conf.getId()].append(chair) |
|---|
| 1584 | else: |
|---|
| 1585 | for cont in self._conf.getContributionList(): |
|---|
| 1586 | ''' We select the contributions that respect the following conditions: |
|---|
| 1587 | - They have Speakers assigned. |
|---|
| 1588 | - They are scheduled. (to discuss...) |
|---|
| 1589 | - They have been chosen for the recording request. |
|---|
| 1590 | ''' |
|---|
| 1591 | if recordingTalksChoice != "choose" or cont.getId() in listTalksToRecord: |
|---|
| 1592 | if cont.isScheduled(): |
|---|
| 1593 | request[cont.getId()] = [] |
|---|
| 1594 | for spk in cont.getSpeakerList(): |
|---|
| 1595 | request[cont.getId()].append(spk) |
|---|
| 1596 | |
|---|
| 1597 | return request |
|---|
| 1598 | |
|---|
| 1599 | def setBookingParams(self, params): |
|---|
| 1600 | """ Sets new booking parameters. |
|---|
| 1601 | params: a dict with key/value pairs with the new values for the booking parameters. |
|---|
| 1602 | If the plugin's _needsBookingParamsCheck is True, the _checkBookingParams() method will be called. |
|---|
| 1603 | This function will return False if all the checks were OK or if there were no checks, and otherwise will throw |
|---|
| 1604 | an exception or return a CSReturnedErrorBase error. |
|---|
| 1605 | |
|---|
| 1606 | Support for "complex" parameters, that are not defined in the self._bookingParams dict, but can |
|---|
| 1607 | be set through setter methods. |
|---|
| 1608 | If a subclass defines a class attributes called _complexParameters (a list of strings), |
|---|
| 1609 | parameter names that are in 'params' and also in this list will not be assigned directly, |
|---|
| 1610 | but instead the inheriting class's setXXX method will be called. |
|---|
| 1611 | |
|---|
| 1612 | Example: _complexParameters = ["communityName", "accessPassword", "hasAccessPassword"] corresponds |
|---|
| 1613 | to methods setCommunityName, setAccessPassword, setHasAccessPassword. |
|---|
| 1614 | Note: even if a parameter is in this list, you can decide not to implement its corresponding set |
|---|
| 1615 | method if you never expect the parameter name to come up inside 'params'. |
|---|
| 1616 | """ |
|---|
| 1617 | |
|---|
| 1618 | sanitizeResult = self.sanitizeParams(params) |
|---|
| 1619 | if sanitizeResult: |
|---|
| 1620 | return sanitizeResult |
|---|
| 1621 | |
|---|
| 1622 | self.setHidden(params.pop("hidden", False) == ["yes"]) |
|---|
| 1623 | self.setNeedsToBeNotifiedOfDateChanges(params.pop("notifyOnDateChanges", False) == ["yes"]) |
|---|
| 1624 | |
|---|
| 1625 | startDate = params.pop("startDate", None) |
|---|
| 1626 | if startDate is not None: |
|---|
| 1627 | self.setStartDateFromString(startDate) |
|---|
| 1628 | endDate = params.pop("endDate", None) |
|---|
| 1629 | if endDate is not None: |
|---|
| 1630 | self.setEndDateFromString(endDate) |
|---|
| 1631 | |
|---|
| 1632 | for k,v in params.iteritems(): |
|---|
| 1633 | if k in self.__class__._simpleParameters: |
|---|
| 1634 | if self.__class__._simpleParameters[k][0]: |
|---|
| 1635 | try: |
|---|
| 1636 | v = self.__class__._simpleParameters[k][0](v) |
|---|
| 1637 | except ValueError: |
|---|
| 1638 | 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") |
|---|
| 1639 | self._bookingParams[k] = v |
|---|
| 1640 | elif k in self.__class__._complexParameters: |
|---|
| 1641 | setterMethods = dict(inspect.getmembers(self, lambda m: inspect.ismethod(m) and m.__name__.startswith('set'))) |
|---|
| 1642 | setMethodName = 'set' + k[0].upper() + k[1:] |
|---|
| 1643 | if setMethodName in setterMethods: |
|---|
| 1644 | setterMethods[setMethodName](v) |
|---|
| 1645 | else: |
|---|
| 1646 | 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") |
|---|
| 1647 | else: |
|---|
| 1648 | raise CollaborationServiceException("Tried to set the value of a parameter with name " + str(k) + " that was not declared") |
|---|
| 1649 | |
|---|
| 1650 | for k, v in self.__class__._simpleParameters.iteritems(): |
|---|
| 1651 | if not k in self._bookingParams: |
|---|
| 1652 | self._bookingParams[k] = self.__class__._simpleParameters[k][1] |
|---|
| 1653 | |
|---|
| 1654 | if self.needsBookingParamsCheck(): |
|---|
| 1655 | return self._checkBookingParams() |
|---|
| 1656 | |
|---|
| 1657 | return False |
|---|
| 1658 | |
|---|
| 1659 | def sanitizeParams(self, params): |
|---|
| 1660 | """ Checks if the fields introduced into the booking / request form |
|---|
| 1661 | have any kind of HTML or script tag. |
|---|
| 1662 | """ |
|---|
| 1663 | if not isinstance(params, dict): |
|---|
| 1664 | raise CollaborationServiceException("Booking parameters are not a dictionary") |
|---|
| 1665 | |
|---|
| 1666 | invalidFields = [] |
|---|
| 1667 | for k, v in params.iteritems(): |
|---|
| 1668 | if type(v) == str and hasTags(v): |
|---|
| 1669 | invalidFields.append(k) |
|---|
| 1670 | |
|---|
| 1671 | if invalidFields: |
|---|
| 1672 | return CSSanitizationError(invalidFields) |
|---|
| 1673 | else: |
|---|
| 1674 | return None |
|---|
| 1675 | |
|---|
| 1676 | def _getTypeDisplayName(self): |
|---|
| 1677 | return CollaborationTools.getXMLGenerator(self._type).getDisplayName() |
|---|
| 1678 | |
|---|
| 1679 | def _getFirstLineInfo(self, tz): |
|---|
| 1680 | return CollaborationTools.getXMLGenerator(self._type).getFirstLineInfo(self, tz) |
|---|
| 1681 | |
|---|
| 1682 | def _getTitle(self): |
|---|
| 1683 | if self.hasEventDisplay(): |
|---|
| 1684 | raise CollaborationException("Method _getTitle was not overriden for the plugin type " + str(self._type)) |
|---|
| 1685 | |
|---|
| 1686 | def _getInformationDisplay(self, tz): |
|---|
| 1687 | templateClass = CollaborationTools.getTemplateClass(self.getType(), "WInformationDisplay") |
|---|
| 1688 | if templateClass: |
|---|
| 1689 | return templateClass(self, tz).getHTML() |
|---|
| 1690 | else: |
|---|
| 1691 | return None |
|---|
| 1692 | |
|---|
| 1693 | def _getLaunchDisplayInfo(self): |
|---|
| 1694 | """ To be overloaded by plugins |
|---|
| 1695 | """ |
|---|
| 1696 | return None |
|---|
| 1697 | |
|---|
| 1698 | def _checkBookingParams(self): |
|---|
| 1699 | """ To be overriden by inheriting classes. |
|---|
| 1700 | Verifies that the booking parameters are correct. For example, that a numeric field is actually a number. |
|---|
| 1701 | Otherwise, an exception should be thrown. |
|---|
| 1702 | If there are no errors, the method should just return. |
|---|
| 1703 | """ |
|---|
| 1704 | if self.needsBookingParamsCheck(): |
|---|
| 1705 | raise CollaborationServiceException("Method _checkBookingParams was not overriden for the plugin type " + str(self._type)) |
|---|
| 1706 | |
|---|
| 1707 | def hasStart(self): |
|---|
| 1708 | """ Returns if this booking belongs to a plugin who has a "start" concept. |
|---|
| 1709 | This attribute will be available in Javascript with the "hasStart" attribute |
|---|
| 1710 | """ |
|---|
| 1711 | return self._hasStart |
|---|
| 1712 | |
|---|
| 1713 | def hasStartStopAll(self): |
|---|
| 1714 | """ Returns if this booking belongs to a plugin who has a "start" concept, and all of its bookings for a conference |
|---|
| 1715 | can be started simultanously. |
|---|
| 1716 | This attribute will be available in Javascript with the "hasStart" attribute |
|---|
| 1717 | """ |
|---|
| 1718 | return self._hasStartStopAll |
|---|
| 1719 | |
|---|
| 1720 | def hasStop(self): |
|---|
| 1721 | """ Returns if this booking belongs to a plugin who has a "stop" concept. |
|---|
| 1722 | This attribute will be available in Javascript with the "hasStop" attribute |
|---|
| 1723 | """ |
|---|
| 1724 | return self._hasStop |
|---|
| 1725 | |
|---|
| 1726 | def hasConnect(self): |
|---|
| 1727 | """ Returns if this booking belongs to a plugin who has a "connect" concept. |
|---|
| 1728 | This attribute will be available in Javascript with the "hasConnect" attribute |
|---|
| 1729 | """ |
|---|
| 1730 | if not hasattr(self, '_hasConnect'): |
|---|
| 1731 | self._hasConnect = False |
|---|
| 1732 | return self._hasConnect |
|---|
| 1733 | |
|---|
| 1734 | def hasCheckStatus(self): |
|---|
| 1735 | """ Returns if this booking belongs to a plugin who has a "check status" concept. |
|---|
| 1736 | This attribute will be available in Javascript with the "hasCheckStatus" attribute |
|---|
| 1737 | """ |
|---|
| 1738 | return self._hasCheckStatus |
|---|
| 1739 | |
|---|
| 1740 | def hasAcceptReject(self): |
|---|
| 1741 | """ Returns if this booking belongs to a plugin who has a "accept or reject" concept. |
|---|
| 1742 | This attribute will be available in Javascript with the "hasAcceptReject" attribute |
|---|
| 1743 | """ |
|---|
| 1744 | return self._hasAcceptReject |
|---|
| 1745 | |
|---|
| 1746 | def requiresServerCallForStart(self): |
|---|
| 1747 | """ Returns if this booking belongs to a plugin who requires a server call when the start button is pressed. |
|---|
| 1748 | This attribute will be available in Javascript with the "requiresServerCallForStart" attribute |
|---|
| 1749 | """ |
|---|
| 1750 | return self._requiresServerCallForStart |
|---|
| 1751 | |
|---|
| 1752 | def requiresServerCallForStop(self): |
|---|
| 1753 | """ Returns if this booking belongs to a plugin who requires a server call when the stop button is pressed. |
|---|
| 1754 | This attribute will be available in Javascript with the "requiresServerCallForStop" attribute |
|---|
| 1755 | """ |
|---|
| 1756 | return self._requiresServerCallForStop |
|---|
| 1757 | |
|---|
| 1758 | def requiresServerCallForConnect(self): |
|---|
| 1759 | """ Returns if this booking belongs to a plugin who requires a server call when the connect button is pressed. |
|---|
| 1760 | This attribute will be available in Javascript with the "requiresServerCallForConnect" attribute |
|---|
| 1761 | """ |
|---|
| 1762 | if not hasattr(self, '_requiresServerCallForConnect'): |
|---|
| 1763 | self._requiresServerCallForConnect = False |
|---|
| 1764 | return self._requiresServerCallForConnect |
|---|
| 1765 | |
|---|
| 1766 | def requiresClientCallForStart(self): |
|---|
| 1767 | """ Returns if this booking belongs to a plugin who requires a client call when the start button is pressed. |
|---|
| 1768 | This attribute will be available in Javascript with the "requiresClientCallForStart" attribute |
|---|
| 1769 | """ |
|---|
| 1770 | return self._requiresClientCallForStart |
|---|
| 1771 | |
|---|
| 1772 | def requiresClientCallForStop(self): |
|---|
| 1773 | """ Returns if this booking belongs to a plugin who requires a client call when the stop button is pressed. |
|---|
| 1774 | This attribute will be available in Javascript with the "requiresClientCallForStop" attribute |
|---|
| 1775 | """ |
|---|
| 1776 | return self._requiresClientCallForStop |
|---|
| 1777 | |
|---|
| 1778 | def requiresClientCallForConnect(self): |
|---|
| 1779 | """ Returns if this booking belongs to a plugin who requires a client call when the connect button is pressed. |
|---|
| 1780 | This attribute will be available in Javascript with the "requiresClientCallForConnect" attribute |
|---|
| 1781 | """ |
|---|
| 1782 | if not hasattr(self, '_requiresClientCallForConnect'): |
|---|
| 1783 | self._requiresClientCallForConnect = False |
|---|
| 1784 | return self._requiresClientCallForConnect |
|---|
| 1785 | |
|---|
| 1786 | def canBeDeleted(self): |
|---|
| 1787 | """ Returns if this booking can be deleted, in the sense that the "Remove" button will be active and able to be pressed. |
|---|
| 1788 | This attribute will be available in Javascript with the "canBeDeleted" attribute |
|---|
| 1789 | """ |
|---|
| 1790 | |
|---|
| 1791 | return self._canBeDeleted |
|---|
| 1792 | |
|---|
| 1793 | def setCanBeDeleted(self, canBeDeleted): |
|---|
| 1794 | """ Sets if this booking can be deleted, in the sense that the "Remove" button will be active and able to be pressed. |
|---|
| 1795 | This attribute will be available in Javascript with the "canBeDeleted" attribute |
|---|
| 1796 | """ |
|---|
| 1797 | self._canBeDeleted = canBeDeleted |
|---|
| 1798 | |
|---|
| 1799 | def canBeStarted(self): |
|---|
| 1800 | """ Returns if this booking can be started, in the sense that the "Start" button will be active and able to be pressed. |
|---|
| 1801 | This attribute will be available in Javascript with the "canBeStarted" attribute |
|---|
| 1802 | """ |
|---|
| 1803 | return self.isHappeningNow() |
|---|
| 1804 | |
|---|
| 1805 | def canBeStopped(self): |
|---|
| 1806 | """ Returns if this booking can be stopped, in the sense that the "Stop" button will be active and able to be pressed. |
|---|
| 1807 | This attribute will be available in Javascript with the "canBeStopped" attribute |
|---|
| 1808 | """ |
|---|
| 1809 | return self.isHappeningNow() |
|---|
| 1810 | |
|---|
| 1811 | def canBeConnected(self): |
|---|
| 1812 | """ Returns if this booking can be connected, in the sense that the "Connect" button will be active and able to be pressed. |
|---|
| 1813 | This attribute will be available in Javascript with the "canBeConnected" attribute |
|---|
| 1814 | """ |
|---|
| 1815 | return self.isHappeningNow() |
|---|
| 1816 | |
|---|
| 1817 | def isPermittedToStart(self): |
|---|
| 1818 | """ Returns if this booking is allowed to start, in the sense that it will be started after the "Start" button is pressed. |
|---|
| 1819 | For example a booking should not be permitted to start before a given time, even if the button is active. |
|---|
| 1820 | This attribute will be available in Javascript with the "isPermittedToStart" attribute |
|---|
| 1821 | """ |
|---|
| 1822 | return self._permissionToStart |
|---|
| 1823 | |
|---|
| 1824 | def isPermittedToStop(self): |
|---|
| 1825 | """ Returns if this booking is allowed to stop, in the sense that it will be started after the "Stop" button is pressed. |
|---|
| 1826 | This attribute will be available in Javascript with the "isPermittedToStop" attribute |
|---|
| 1827 | """ |
|---|
| 1828 | return self._permissionToStop |
|---|
| 1829 | |
|---|
| 1830 | def isPermittedToConnect(self): |
|---|
| 1831 | """ Returns if this booking is allowed to stop, in the sense that it will be connect after the "Connect" button is pressed. |
|---|
| 1832 | This attribute will be available in Javascript with the "isPermittedToConnect" attribute |
|---|
| 1833 | """ |
|---|
| 1834 | if not hasattr(self, '_permissionToConnect'): |
|---|
| 1835 | self._permissionToConnect = False |
|---|
| 1836 | return self._permissionToConnect |
|---|
| 1837 | |
|---|
| 1838 | def needsBookingParamsCheck(self): |
|---|
| 1839 | """ Returns if this booking belongs to a plugin that needs to verify the booking parameters. |
|---|
| 1840 | """ |
|---|
| 1841 | return self._needsBookingParamsCheck |
|---|
| 1842 | |
|---|
| 1843 | def needsToBeNotifiedOnView(self): |
|---|
| 1844 | """ Returns if this booking needs to be notified when someone views it (for example when the list of bookings is returned) |
|---|
| 1845 | """ |
|---|
| 1846 | return self._needsToBeNotifiedOnView |
|---|
| 1847 | |
|---|
| 1848 | def canBeNotifiedOfEventDateChanges(self): |
|---|
| 1849 | """ Returns if bookings of this type should be able to be notified |
|---|
| 1850 | of their owner Event changing start date, end date or timezone. |
|---|
| 1851 | """ |
|---|
| 1852 | return False |
|---|
| 1853 | |
|---|
| 1854 | def needsToBeNotifiedOfDateChanges(self): |
|---|
| 1855 | """ Returns if this booking in particular needs to be notified |
|---|
| 1856 | of their owner Event changing start date, end date or timezone. |
|---|
| 1857 | """ |
|---|
| 1858 | return self._needsToBeNotifiedOfDateChanges |
|---|
| 1859 | |
|---|
| 1860 | def setNeedsToBeNotifiedOfDateChanges(self, needsToBeNotifiedOfDateChanges): |
|---|
| 1861 | """ Sets if this booking in particular needs to be notified |
|---|
| 1862 | of their owner Event changing start date, end date or timezone. |
|---|
| 1863 | """ |
|---|
| 1864 | self._needsToBeNotifiedOfDateChanges = needsToBeNotifiedOfDateChanges |
|---|
| 1865 | |
|---|
| 1866 | def isHidden(self): |
|---|
| 1867 | """ Return if this booking is "hidden" |
|---|
| 1868 | A hidden booking will not appear in display pages |
|---|
| 1869 | """ |
|---|
| 1870 | if not hasattr(self, '_hidden'): |
|---|
| 1871 | self._hidden = False |
|---|
| 1872 | return self._hidden |
|---|
| 1873 | |
|---|
| 1874 | def setHidden(self, hidden): |
|---|
| 1875 | """ Sets if this booking is "hidden" |
|---|
| 1876 | A hidden booking will not appear in display pages |
|---|
| 1877 | hidden: a Boolean |
|---|
| 1878 | """ |
|---|
| 1879 | self._hidden = hidden |
|---|
| 1880 | |
|---|
| 1881 | def isAllowMultiple(self): |
|---|
| 1882 | """ Returns if this booking belongs to a type that allows multiple bookings per event. |
|---|
| 1883 | """ |
|---|
| 1884 | return self._allowMultiple |
|---|
| 1885 | |
|---|
| 1886 | def shouldBeIndexed(self): |
|---|
| 1887 | """ Returns if bookings of this type should be indexed |
|---|
| 1888 | """ |
|---|
| 1889 | return self._shouldBeIndexed |
|---|
| 1890 | |
|---|
| 1891 | def getCommonIndexes(self): |
|---|
| 1892 | """ Returns a list of strings with the names of the |
|---|
| 1893 | common (shared) indexes that bookings of this type want to |
|---|
| 1894 | be included in. |
|---|
| 1895 | """ |
|---|
| 1896 | return self._commonIndexes |
|---|
| 1897 | |
|---|
| 1898 | def getModificationURL(self): |
|---|
| 1899 | return urlHandlers.UHConfModifCollaboration.getURL(self.getConference(), |
|---|
| 1900 | secure = ContextManager.get('currentRH').use_https(), |
|---|
| 1901 | tab = CollaborationTools.getPluginTab(self.getPlugin())) |
|---|
| 1902 | |
|---|
| 1903 | def hasStartDate(self): |
|---|
| 1904 | """ Returns if bookings of this type have a start date |
|---|
| 1905 | (they may only have creation / modification date) |
|---|
| 1906 | """ |
|---|
| 1907 | return self._hasStartDate |
|---|
| 1908 | |
|---|
| 1909 | def hasTitle(self): |
|---|
| 1910 | """ Returns if bookings of this type have a title |
|---|
| 1911 | """ |
|---|
| 1912 | return self._hasTitle |
|---|
| 1913 | |
|---|
| 1914 | def hasEventDisplay(self): |
|---|
| 1915 | """ Returns if the type of this booking should display something on |
|---|
| 1916 | an event display page |
|---|
| 1917 | """ |
|---|
| 1918 | return self._hasEventDisplay |
|---|
| 1919 | |
|---|
| 1920 | def isAdminOnly(self): |
|---|
| 1921 | """ Returns if this booking / this booking's plugin pages should only be displayed |
|---|
| 1922 | to Server Admins, Video Service Admins, or the respective plugin admins. |
|---|
| 1923 | """ |
|---|
| 1924 | return self._adminOnly |
|---|
| 1925 | |
|---|
| 1926 | def _create(self): |
|---|
| 1927 | """ To be overriden by inheriting classes. |
|---|
| 1928 | This method is called when a booking is created, after setting the booking parameters. |
|---|
| 1929 | The plugin should decide if the booking is accepted or not. |
|---|
| 1930 | Often this will involve communication with another entity, like an MCU for the multi-point H.323 plugin, |
|---|
| 1931 | or a EVO HTTP server in the EVO case. |
|---|
| 1932 | """ |
|---|
| 1933 | raise CollaborationException("Method _create was not overriden for the plugin type " + str(self._type)) |
|---|
| 1934 | |
|---|
| 1935 | def _modify(self, oldBookingParams): |
|---|
| 1936 | """ To be overriden by inheriting classes. |
|---|
| 1937 | This method is called when a booking is modifying, after setting the booking parameters. |
|---|
| 1938 | The plugin should decide if the booking is accepted or not. |
|---|
| 1939 | Often this will involve communication with another entity, like an MCU for the multi-point H.323 plugin |
|---|
| 1940 | or a EVO HTTP server in the EVO case. |
|---|
| 1941 | A dictionary with the previous booking params is passed. This dictionary is the one obtained |
|---|
| 1942 | by the method self.getBookingParams() before the new params input by the user are applied. |
|---|
| 1943 | """ |
|---|
| 1944 | raise CollaborationException("Method _modify was not overriden for the plugin type " + str(self._type)) |
|---|
| 1945 | |
|---|
| 1946 | def _start(self): |
|---|
| 1947 | """ To be overriden by inheriting classes |
|---|
| 1948 | This method is called when the user presses the "Start" button in a plugin who has a "Start" concept |
|---|
| 1949 | and whose flag _requiresServerCallForStart is True. |
|---|
| 1950 | Often this will involve communication with another entity. |
|---|
| 1951 | """ |
|---|
| 1952 | if self.hasStart(): |
|---|
| 1953 | raise CollaborationException("Method _start was not overriden for the plugin type " + str(self._type)) |
|---|
| 1954 | else: |
|---|
| 1955 | pass |
|---|
| 1956 | |
|---|
| 1957 | def _stop(self): |
|---|
| 1958 | """ To be overriden by inheriting classes |
|---|
| 1959 | This method is called when the user presses the "Stop" button in a plugin who has a "Stop" concept |
|---|
| 1960 | and whose flag _requiresServerCallForStop is True. |
|---|
| 1961 | Often this will involve communication with another entity. |
|---|
| 1962 | """ |
|---|
| 1963 | if self.hasStop(): |
|---|
| 1964 | raise CollaborationException("Method _stop was not overriden for the plugin type " + str(self._type)) |
|---|
| 1965 | else: |
|---|
| 1966 | pass |
|---|
| 1967 | |
|---|
| 1968 | def _connect(self): |
|---|
| 1969 | """ To be overriden by inheriting classes |
|---|
| 1970 | This method is called when the user presses the "Connect" button in a plugin who has a "Connect" concept |
|---|
| 1971 | and whose flag _requiresServerCallForConnect is True. |
|---|
| 1972 | Often this will involve communication with another entity. |
|---|
| 1973 | """ |
|---|
| 1974 | if self.hasConnect(): |
|---|
| 1975 | raise CollaborationException("Method _connect was not overriden for the plugin type " + str(self._type)) |
|---|
| 1976 | else: |
|---|
| 1977 | pass |
|---|
| 1978 | |
|---|
| 1979 | def _checkStatus(self): |
|---|
| 1980 | """ To be overriden by inheriting classes |
|---|
| 1981 | This method is called when the user presses the "Check Status" button in a plugin who has a "check status" concept. |
|---|
| 1982 | Often this will involve communication with another entity. |
|---|
| 1983 | """ |
|---|
| 1984 | if self.hasCheckStatus(): |
|---|
| 1985 | raise CollaborationException("Method _checkStatus was not overriden for the plugin type " + str(self._type)) |
|---|
| 1986 | else: |
|---|
| 1987 | pass |
|---|
| 1988 | |
|---|
| 1989 | def _accept(self, user = None): |
|---|
| 1990 | """ To be overriden by inheriting classes |
|---|
| 1991 | This method is called when a user with privileges presses the "Accept" button |
|---|
| 1992 | in a plugin who has a "accept or reject" concept. |
|---|
| 1993 | Often this will involve communication with another entity. |
|---|
| 1994 | """ |
|---|
| 1995 | if self.hasAcceptReject(): |
|---|
| 1996 | raise CollaborationException("Method _accept was not overriden for the plugin type " + str(self._type)) |
|---|
| 1997 | else: |
|---|
| 1998 | pass |
|---|
| 1999 | |
|---|
| 2000 | def _reject(self): |
|---|
| 2001 | """ To be overriden by inheriting classes |
|---|
| 2002 | This method is called when a user with privileges presses the "Reject" button |
|---|
| 2003 | in a plugin who has a "accept or reject" concept. |
|---|
| 2004 | Often this will involve communication with another entity. |
|---|
| 2005 | """ |
|---|
| 2006 | if self.hasAcceptReject(): |
|---|
| 2007 | raise CollaborationException("Method _reject was not overriden for the plugin type " + str(self._type)) |
|---|
| 2008 | else: |
|---|
| 2009 | pass |
|---|
| 2010 | |
|---|
| 2011 | def _notifyOnView(self): |
|---|
| 2012 | """ To be overriden by inheriting classes |
|---|
| 2013 | This method is called when a user "sees" a booking, for example when the list of bookings is displayed. |
|---|
| 2014 | Maybe in this moment the booking wants to update its status. |
|---|
| 2015 | """ |
|---|
| 2016 | if self.needsToBeNotifiedOnView(): |
|---|
| 2017 | raise CollaborationException("Method _notifyOnView was not overriden for the plugin type " + str(self._type)) |
|---|
| 2018 | else: |
|---|
| 2019 | pass |
|---|
| 2020 | |
|---|
| 2021 | def _delete(self): |
|---|
| 2022 | """ To be overriden by inheriting classes |
|---|
| 2023 | This method is called whent he user removes a booking. Maybe the plugin will need to liberate |
|---|
| 2024 | ressources that were allocated to it. |
|---|
| 2025 | This method does not unregister the booking from the list of date change observer of the meeting |
|---|
| 2026 | """ |
|---|
| 2027 | raise CollaborationException("Method _delete was not overriden for the plugin type " + str(self._type)) |
|---|
| 2028 | |
|---|
| 2029 | def _sendNotifications(self, operation): |
|---|
| 2030 | """ |
|---|
| 2031 | Sends a mail, wrapping it with ExternalOperationsManager |
|---|
| 2032 | """ |
|---|
| 2033 | ExternalOperationsManager.execute(self, "sendMail_" + operation, self._sendMail, operation) |
|---|
| 2034 | |
|---|
| 2035 | def _sendMail(self, operation): |
|---|
| 2036 | if operation == 'new': |
|---|
| 2037 | try: |
|---|
| 2038 | notification = mail.NewBookingNotification(self) |
|---|
| 2039 | GenericMailer.sendAndLog(notification, self._conf, |
|---|
| 2040 | "MaKaC/plugins/Collaboration/base.py", |
|---|
| 2041 | self._conf.getCreator()) |
|---|
| 2042 | except Exception, e: |
|---|
| 2043 | Logger.get('VideoServ').error( |
|---|
| 2044 | """Could not send NewBookingNotification for booking with id %s of event with id %s, exception: %s""" % |
|---|
| 2045 | (self.getId(), self._conf.getId(), str(e))) |
|---|
| 2046 | raise |
|---|
| 2047 | |
|---|
| 2048 | elif operation == 'modify': |
|---|
| 2049 | try: |
|---|
| 2050 | notification = mail.BookingModifiedNotification(self) |
|---|
| 2051 | GenericMailer.sendAndLog(notification, self._conf, |
|---|
| 2052 | "MaKaC/plugins/Collaboration/base.py", |
|---|
| 2053 | self._conf.getCreator()) |
|---|
| 2054 | except Exception, e: |
|---|
| 2055 | Logger.get('VideoServ').error( |
|---|
| 2056 | """Could not send BookingModifiedNotification for booking with id %s of event with id %s, exception: %s""" % |
|---|
| 2057 | (self.getId(), self._conf.getId(), str(e))) |
|---|
| 2058 | raise |
|---|
| 2059 | |
|---|
| 2060 | elif operation == 'remove': |
|---|
| 2061 | try: |
|---|
| 2062 | notification = mail.BookingDeletedNotification(self) |
|---|
| 2063 | GenericMailer.sendAndLog(notification, self._conf, |
|---|
| 2064 | "MaKaC/plugins/Collaboration/base.py", |
|---|
| 2065 | self._conf.getCreator()) |
|---|
| 2066 | except Exception, e: |
|---|
| 2067 | Logger.get('VideoServ').error( |
|---|
| 2068 | """Could not send BookingDeletedNotification for booking with id %s of event with id %s, exception: %s""" % |
|---|
| 2069 | (self.getId(), self._conf.getId(), str(e))) |
|---|
| 2070 | raise |
|---|
| 2071 | |
|---|
| 2072 | def getPlayStatus(self): |
|---|
| 2073 | if not hasattr(self, '_play_status'): |
|---|
| 2074 | self._play_status = None |
|---|
| 2075 | return self._play_status |
|---|
| 2076 | |
|---|
| 2077 | class WCSTemplateBase(wcomponents.WTemplated): |
|---|
| 2078 | """ Base class for Collaboration templates. |
|---|
| 2079 | It stores the following attributes: |
|---|
| 2080 | _conf : the corresponding Conference object. |
|---|
| 2081 | _pluginName: the corresponding plugin ("EVO", "DummyPlugin", etc.). |
|---|
| 2082 | _XXXOptions: a dictionary whose values are the options of the plugin called pluginName. |
|---|
| 2083 | So, for example, if an EVO template inherits from this class, an attribute self._EVOOptions will be available. |
|---|
| 2084 | This class also overloads the _setTPLFile method so that Indico knows where each plugin's *.tpl files are. |
|---|
| 2085 | """ |
|---|
| 2086 | |
|---|
| 2087 | def __init__(self, pluginId): |
|---|
| 2088 | """ Constructor for the WCSTemplateBase class. |
|---|
| 2089 | conf: a Conference object |
|---|
| 2090 | plugin: the corresponding plugin |
|---|
| 2091 | """ |
|---|
| 2092 | self._plugin = CollaborationTools.getPlugin(pluginId) |
|---|
| 2093 | self._pluginId = self._plugin.getId() |
|---|
| 2094 | self._ph = PluginsHolder() |
|---|
| 2095 | |
|---|
| 2096 | setattr(self, "_" + self._pluginId + "Options", self._plugin.getOptions()) |
|---|
| 2097 | |
|---|
| 2098 | def _setTPLFile(self, extension='tpl'): |
|---|
| 2099 | tplDir = os.path.join(self._plugin.getModule().__path__[0], "tpls") |
|---|
| 2100 | |
|---|
| 2101 | fname = "%s.%s" % (self.tplId, extension) |
|---|
| 2102 | self.tplFile = os.path.join(tplDir, fname) |
|---|
| 2103 | |
|---|
| 2104 | hfile = self._getSpecificTPL(os.path.join(tplDir,self._pluginId,'chelp'), self.tplId,extension='wohl') |
|---|
| 2105 | self.helpFile = os.path.join(tplDir,'chelp',hfile) |
|---|
| 2106 | |
|---|
| 2107 | |
|---|
| 2108 | class WCSPageTemplateBase(WCSTemplateBase): |
|---|
| 2109 | """ Base class for Collaboration templates for the create / modify booking form. |
|---|
| 2110 | """ |
|---|
| 2111 | |
|---|
| 2112 | def __init__(self, conf, pluginId, user): |
|---|
| 2113 | WCSTemplateBase.__init__(self, pluginId) |
|---|
| 2114 | self._conf = conf |
|---|
| 2115 | self._user = user |
|---|
| 2116 | |
|---|
| 2117 | |
|---|
| 2118 | class WJSBase(WCSTemplateBase): |
|---|
| 2119 | """ Base class for Collaboration templates for Javascript code template. |
|---|
| 2120 | It overloads _setTPLFile so that indico can find the Main.js, Extra.js and Indexing.js files. |
|---|
| 2121 | """ |
|---|
| 2122 | def __init__(self, conf, plugin, user): |
|---|
| 2123 | WCSTemplateBase.__init__(self, plugin) |
|---|
| 2124 | self._conf = conf |
|---|
| 2125 | self._user = user |
|---|
| 2126 | |
|---|
| 2127 | def _setTPLFile(self): |
|---|
| 2128 | WCSTemplateBase._setTPLFile(self, extension='js') |
|---|
| 2129 | self.helpFile = '' |
|---|
| 2130 | |
|---|
| 2131 | |
|---|
| 2132 | class WCSCSSBase(WCSTemplateBase): |
|---|
| 2133 | """ Base class for Collaboration templates for CSS code template |
|---|
| 2134 | It overloads _setTPLFile so that indico can find the style.css files. |
|---|
| 2135 | """ |
|---|
| 2136 | |
|---|
| 2137 | def _setTPLFile(self): |
|---|
| 2138 | tplDir = self._plugin.getModule().__path__[0] |
|---|
| 2139 | fname = "%s.css" % self.tplId |
|---|
| 2140 | self.tplFile = os.path.join(tplDir, fname) |
|---|
| 2141 | self.helpFile = '' |
|---|
| 2142 | |
|---|
| 2143 | |
|---|
| 2144 | class CSErrorBase(Fossilizable): |
|---|
| 2145 | fossilizes(ICSErrorBaseFossil) |
|---|
| 2146 | |
|---|
| 2147 | """ When _create, _modify or _remove want to return an error, |
|---|
| 2148 | they should return an error that inherits from this class |
|---|
| 2149 | """ |
|---|
| 2150 | |
|---|
| 2151 | def __init__(self): |
|---|
| 2152 | pass |
|---|
| 2153 | |
|---|
| 2154 | def getUserMessage(self): |
|---|
| 2155 | """ To be overloaded. |
|---|
| 2156 | Returns the string that will be shown to the user when this error will happen. |
|---|
| 2157 | """ |
|---|
| 2158 | raise CollaborationException("Method getUserMessage was not overriden for the a CSErrorBase object of class " + self.__class__.__name__) |
|---|
| 2159 | |
|---|
| 2160 | def getLogMessage(self): |
|---|
| 2161 | """ To be overloaded. |
|---|
| 2162 | Returns the string that will be printed in Indico's log when this error will happen. |
|---|
| 2163 | """ |
|---|
| 2164 | raise CollaborationException("Method getLogMessage was not overriden for the a CSErrorBase object of class " + self.__class__.__name__) |
|---|
| 2165 | |
|---|
| 2166 | class CSSanitizationError(CSErrorBase): #already Fossilizable |
|---|
| 2167 | fossilizes(ICSSanitizationErrorFossil) |
|---|
| 2168 | |
|---|
| 2169 | """ Class used to return which fields have a sanitization error (invalid html / script tags) |
|---|
| 2170 | """ |
|---|
| 2171 | |
|---|
| 2172 | def __init__(self, invalidFields): |
|---|
| 2173 | self._invalidFields = invalidFields |
|---|
| 2174 | |
|---|
| 2175 | def invalidFields(self): |
|---|
| 2176 | return self._invalidFields |
|---|
| 2177 | |
|---|
| 2178 | class CollaborationException(MaKaCError): |
|---|
| 2179 | """ Error for the Collaboration System "core". Each plugin should declare their own EVOError, etc. |
|---|
| 2180 | """ |
|---|
| 2181 | def __init__(self, msg, area = 'Collaboration', inner = None): |
|---|
| 2182 | MaKaCError.__init__(self, msg, area) |
|---|
| 2183 | self._inner = inner |
|---|
| 2184 | |
|---|
| 2185 | def getInner(self): |
|---|
| 2186 | return self._inner |
|---|
| 2187 | |
|---|
| 2188 | def __str__(self): |
|---|
| 2189 | return MaKaCError.__str__(self) + '. Inner: ' + str(self._inner) |
|---|
| 2190 | |
|---|
| 2191 | class CollaborationServiceException(ServiceError): |
|---|
| 2192 | """ Error for the Collaboration System "core", for Service calls. |
|---|
| 2193 | """ |
|---|
| 2194 | def __init__(self, message, inner = None): |
|---|
| 2195 | ServiceError.__init__(self, "ERR-COLL", message, inner) |
|---|
| 2196 | |
|---|
| 2197 | class SpeakerStatusEnum: |
|---|
| 2198 | (NOEMAIL, NOTSIGNED, SIGNED, FROMFILE, PENDING, REFUSED) = xrange(6) |
|---|
| 2199 | |
|---|
| 2200 | class SpeakerWrapper(Persistent, Fossilizable): |
|---|
| 2201 | |
|---|
| 2202 | fossilizes(ISpeakerWrapperBaseFossil) |
|---|
| 2203 | |
|---|
| 2204 | def __init__(self, speaker, contId, requestType): |
|---|
| 2205 | self.status = not speaker.getEmail() and SpeakerStatusEnum.NOEMAIL or SpeakerStatusEnum.NOTSIGNED |
|---|
| 2206 | self.speaker = speaker |
|---|
| 2207 | self.contId = contId |
|---|
| 2208 | self.requestType = requestType |
|---|
| 2209 | self.reason = "" |
|---|
| 2210 | self.localFile = None |
|---|
| 2211 | self.dateAgreement = 0 |
|---|
| 2212 | self.ipSignature = None |
|---|
| 2213 | self.modificationDate = nowutc() |
|---|
| 2214 | self.uniqueIdHash = md5("%s.%s"%(time.time(), self.getUniqueId())).hexdigest() |
|---|
| 2215 | |
|---|
| 2216 | def getUniqueId(self): |
|---|
| 2217 | return "%s.%s"%(self.contId, self.speaker.getId()) |
|---|
| 2218 | |
|---|
| 2219 | def getUniqueIdHash(self): |
|---|
| 2220 | # to remove once saved |
|---|
| 2221 | if not hasattr(self, "uniqueIdHash"):#TODO: remove when safe |
|---|
| 2222 | return md5(self.getUniqueId()).hexdigest() |
|---|
| 2223 | else: |
|---|
| 2224 | return self.uniqueIdHash |
|---|
| 2225 | |
|---|
| 2226 | def getStatus(self): |
|---|
| 2227 | return self.status |
|---|
| 2228 | |
|---|
| 2229 | def setStatus(self, newStatus, ip=None): |
|---|
| 2230 | try: |
|---|
| 2231 | self.status = newStatus; |
|---|
| 2232 | if newStatus == SpeakerStatusEnum.SIGNED or newStatus == SpeakerStatusEnum.FROMFILE: |
|---|
| 2233 | self.dateAgreement = now_utc() |
|---|
| 2234 | if newStatus == SpeakerStatusEnum.SIGNED: |
|---|
| 2235 | self.ipSignature = ip |
|---|
| 2236 | except Exception, e: |
|---|
| 2237 | Logger.get('VideoServ').error("Exception while changing the speaker status. Exception: " + str(e)) |
|---|
| 2238 | |
|---|
| 2239 | def getDateAgreementSigned(self): |
|---|
| 2240 | if hasattr(self, "dateAgreement"):#TODO: remove when safe |
|---|
| 2241 | return self.dateAgreement |
|---|
| 2242 | return 0 |
|---|
| 2243 | |
|---|
| 2244 | def getIpAddressWhenSigned(self): |
|---|
| 2245 | if hasattr(self, "ipSignature"):#TODO: remove when safe |
|---|
| 2246 | return self.ipSignature |
|---|
| 2247 | return None |
|---|
| 2248 | |
|---|
| 2249 | def getRejectReason(self): |
|---|
| 2250 | if hasattr(self, "reason"):#TODO: remove when safe |
|---|
| 2251 | if self.status == SpeakerStatusEnum.REFUSED and hasattr(self, "reason"): |
|---|
| 2252 | return self.reason |
|---|
| 2253 | else: |
|---|
| 2254 | return "This speaker has not refused the agreement." |
|---|
| 2255 | else: |
|---|
| 2256 | return "Information not available." |
|---|
| 2257 | |
|---|
| 2258 | def setRejectReason(self, reason): |
|---|
| 2259 | if hasattr(self, "reason"):#TODO: remove when safe |
|---|
| 2260 | self.reason = reason |
|---|
| 2261 | |
|---|
| 2262 | def getObject(self): |
|---|
| 2263 | return self.speaker |
|---|
| 2264 | |
|---|
| 2265 | def getContId(self): |
|---|
| 2266 | return self.contId |
|---|
| 2267 | |
|---|
| 2268 | def getRequestType(self): |
|---|
| 2269 | if hasattr(self, "requestType"):#TODO: remove when safe |
|---|
| 2270 | return self.requestType |
|---|
| 2271 | return "NA" |
|---|
| 2272 | |
|---|
| 2273 | def setRequestType(self, type): |
|---|
| 2274 | self.requestType = type |
|---|
| 2275 | |
|---|
| 2276 | def getSpeakerId(self): |
|---|
| 2277 | return self.speaker.getId() |
|---|
| 2278 | |
|---|
| 2279 | def getLocalFile(self): |
|---|
| 2280 | ''' |
|---|
| 2281 | If exists, return path to paper agreement |
|---|
| 2282 | ''' |
|---|
| 2283 | if hasattr(self, "localFile"):#TODO: remove when safe |
|---|
| 2284 | return self.localFile |
|---|
| 2285 | |
|---|
| 2286 | def setLocalFile(self, localFile): |
|---|
| 2287 | ''' |
|---|
| 2288 | Set localFile of paper agreement |
|---|
| 2289 | ''' |
|---|
| 2290 | if hasattr(self, "localFile"):#TODO: remove when safe |
|---|
| 2291 | self.localFile = localFile |
|---|
| 2292 | |
|---|
| 2293 | def hasEmail(self): |
|---|
| 2294 | if self.speaker.getEmail(): |
|---|
| 2295 | return True |
|---|
| 2296 | return False |
|---|
| 2297 | |
|---|
| 2298 | def getCategory(self): |
|---|
| 2299 | return None |
|---|
| 2300 | |
|---|
| 2301 | def getConference(self): |
|---|
| 2302 | return self.speaker.getConference() |
|---|
| 2303 | |
|---|
| 2304 | def getContribution(self): |
|---|
| 2305 | # if the conference is a lecture, the getContribution will fail. |
|---|
| 2306 | if self.getConference().getType() == "simple_event": |
|---|
| 2307 | return None |
|---|
| 2308 | else: |
|---|
| 2309 | return self.speaker.getContribution() |
|---|
| 2310 | |
|---|
| 2311 | def getSession(self): |
|---|
| 2312 | return None |
|---|
| 2313 | |
|---|
| 2314 | def getSubContribution(self): |
|---|
| 2315 | return None |
|---|
| 2316 | |
|---|
| 2317 | def getModificationDate(self): |
|---|
| 2318 | if hasattr(self, "modificationDate"): # TODO: remove when safe |
|---|
| 2319 | return self.modificationDate |
|---|
| 2320 | return None |
|---|
| 2321 | |
|---|
| 2322 | def setModificationDate(self): |
|---|
| 2323 | if hasattr(self, "modificationDate"): # TODO: remove when safe |
|---|
| 2324 | self.modificationDate = now_utc() |
|---|
| 2325 | |
|---|
| 2326 | def getLocator(self): |
|---|
| 2327 | return self.getContribution().getLocator() |
|---|
| 2328 | |
|---|
| 2329 | def triggerNotification(self): |
|---|
| 2330 | if self.getRequestType() in ('recording', 'webcast'): |
|---|
| 2331 | self._triggerNotification(self.getRequestType()) |
|---|
| 2332 | elif self.getRequestType() == 'both': |
|---|
| 2333 | self._triggerNotification('recording') |
|---|
| 2334 | self._triggerNotification('webcast') |
|---|
| 2335 | |
|---|
| 2336 | def _triggerNotification(self, type): |
|---|
| 2337 | url = None |
|---|
| 2338 | if type == 'recording': |
|---|
| 2339 | url = CollaborationTools.getOptionValue('RecordingRequest', 'AgreementNotificationURL') |
|---|
| 2340 | elif type == 'webcast': |
|---|
| 2341 | url = CollaborationTools.getOptionValue('WebcastRequest', 'AgreementNotificationURL') |
|---|
| 2342 | if not url: |
|---|
| 2343 | return |
|---|
| 2344 | signed = None |
|---|
| 2345 | if self.getStatus() in (SpeakerStatusEnum.FROMFILE, SpeakerStatusEnum.SIGNED): |
|---|
| 2346 | signed = True |
|---|
| 2347 | elif self.getStatus() == SpeakerStatusEnum.REFUSED: |
|---|
| 2348 | signed = False |
|---|
| 2349 | spk = self.getObject() |
|---|
| 2350 | payload = { |
|---|
| 2351 | 'confId': self.getConference().getId(), |
|---|
| 2352 | 'contrib': self.getContId(), |
|---|
| 2353 | 'type': type, |
|---|
| 2354 | 'status': self.getStatus(), |
|---|
| 2355 | 'signed': signed, |
|---|
| 2356 | 'speaker': { |
|---|
| 2357 | 'id': spk.getId(), |
|---|
| 2358 | 'name': spk.getFullName(), |
|---|
| 2359 | 'email': spk.getEmail() |
|---|
| 2360 | } |
|---|
| 2361 | } |
|---|
| 2362 | cl = Client() |
|---|
| 2363 | cl.enqueue(HTTPTask(url, {'data': json.dumps(payload)})) |
|---|