VC: Fix error on clone page for legacy-ID events
[cds-indico.git] / indico / MaKaC / conference.py
blobe278616e0e267054f2ebbdd409036f4987b9d834
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from itertools import ifilter
18 from flask_pluginengine import plugin_context
19 from sqlalchemy.orm import lazyload
21 from indico.modules.rb.models.reservations import Reservation
22 from MaKaC.common.timezoneUtils import datetimeToUnixTimeInt
23 from MaKaC.fossils.subcontribution import ISubContribParticipationFossil,\
24 ISubContribParticipationFullFossil, ISubContributionFossil, ISubContributionWithSpeakersFossil
25 from MaKaC.fossils.contribution import IContributionParticipationFossil,\
26 IContributionFossil, IContributionWithSpeakersFossil, IContributionParticipationMinimalFossil, \
27 IContributionWithSubContribsFossil,\
28 IContributionParticipationTTDisplayFossil, \
29 IContributionParticipationTTMgmtFossil
30 from MaKaC.fossils.conference import IConferenceMinimalFossil, \
31 IConferenceEventInfoFossil, IConferenceFossil,\
32 ISessionFossil, ISessionSlotFossil, IMaterialMinimalFossil,\
33 IMaterialFossil, IConferenceParticipationFossil,\
34 IResourceMinimalFossil, ILinkMinimalFossil, ILocalFileMinimalFossil,\
35 IResourceFossil, ILinkFossil, ILocalFileFossil,\
36 ILocalFileExtendedFossil, IConferenceParticipationMinimalFossil,\
37 ICategoryFossil, ILocalFileAbstractMaterialFossil
38 from MaKaC.common.fossilize import fossilizes, Fossilizable
39 from MaKaC.common.url import ShortURLMapper
40 from MaKaC.contributionReviewing import Review
41 from indico.modules.rb.models.rooms import Room
42 from indico.modules.rb.models.locations import Location
43 from indico.modules.users import User
44 from indico.modules.users.legacy import AvatarUserWrapper
45 from indico.modules.groups.legacy import GroupWrapper
46 from indico.util.i18n import L_
47 from indico.util.string import safe_upper, safe_slice, fix_broken_string, return_ascii, is_legacy_id
48 from MaKaC.review import AbstractFieldContent
51 import re, os
52 import tempfile
53 import copy
54 import stat
55 from datetime import datetime, timedelta
56 from flask import session, request, has_request_context
58 from MaKaC.contributionReviewing import ReviewManager
59 from MaKaC.paperReviewing import ConferencePaperReview as ConferencePaperReview
60 from MaKaC.abstractReviewing import ConferenceAbstractReview as ConferenceAbstractReview
62 from pytz import timezone
63 from pytz import all_timezones
65 from persistent import Persistent
66 from BTrees.OOBTree import OOBTree, OOTreeSet
67 from BTrees.OIBTree import OIBTree,OISet,union
68 import MaKaC
69 from MaKaC.common import indexes
70 from MaKaC.common.timezoneUtils import nowutc, maxDatetime
71 import MaKaC.fileRepository as fileRepository
72 from MaKaC.schedule import (ConferenceSchedule, SessionSchedule, SlotSchTypeFactory, ContribSchEntry,
73 LinkedTimeSchEntry, BreakTimeSchEntry)
74 import MaKaC.review as review
75 from MaKaC.common import utils
76 from MaKaC.common.Counter import Counter
77 from MaKaC.common.ObjectHolders import ObjectHolder
78 from MaKaC.common.Locators import Locator
79 from MaKaC.accessControl import AccessController
80 from MaKaC.errors import MaKaCError, TimingError, ParentTimingError, EntryTimingError, NotFoundError, FormValuesError
81 from MaKaC import registration
82 from MaKaC.evaluation import Evaluation
83 from MaKaC.trashCan import TrashCanManager
84 from MaKaC.user import AvatarHolder
85 from MaKaC.common import pendingQueues
86 from MaKaC.common.info import HelperMaKaCInfo
87 from MaKaC.participant import Participation
88 from MaKaC.common.log import LogHandler
89 from MaKaC.badge import BadgeTemplateManager
90 from MaKaC.poster import PosterTemplateManager
91 from MaKaC.common import mail
92 from MaKaC.i18n import _
93 from MaKaC.common.PickleJar import Updates
94 from MaKaC.schedule import ScheduleToJson
95 from MaKaC.webinterface import urlHandlers
97 from indico.core.logger import Logger
98 from MaKaC.common.contextManager import ContextManager
99 import zope.interface
101 from indico.modules.scheduler import Client, tasks
102 from indico.core import signals
103 from indico.util.date_time import utc_timestamp, format_datetime
104 from indico.core.index import IIndexableByStartDateTime, IUniqueIdProvider, Catalog
105 from indico.core.db import DBMgr
106 from indico.core.db.event import SupportInfo
107 from indico.core.config import Config
108 from indico.util.date_time import utc_timestamp
109 from indico.util.signals import values_from_signal
110 from indico.util.redis import write_client as redis_write_client
111 from indico.util.user import unify_user_args
112 from indico.util.redis import avatar_links
113 from indico.web.flask.util import url_for
116 class CoreObject(Persistent):
118 CoreObjects are Persistent objects that are employed by Indico's core
121 zope.interface.implements(IUniqueIdProvider,
122 IIndexableByStartDateTime)
124 def setModificationDate(self, date=None):
126 Method called to notify the current object has been modified.
128 if not date:
129 date = nowutc()
130 self._modificationDS = date
132 def __conform__(self, proto):
134 if proto == IIndexableByStartDateTime:
135 return utc_timestamp(self.getStartDate())
136 else:
137 return None
140 class Locatable:
142 Inherited by objects that imply a physical location:
143 * Conferences
144 * Sessions
145 * SessionSlots
146 * Contributions
147 * SubContributions
150 @property
151 def rb_room(self):
152 if not self.getLocation() or not self.getRoom():
153 return None
155 location = self.getLocation().getName()
156 room = self.getRoom().getName()
158 if not location or not room:
159 return None
161 return (Room.query
162 .join(Room.location)
163 .options(lazyload(Room.owner))
164 .filter(Room.name == fix_broken_string(room, True),
165 Location.name == fix_broken_string(location, True))
166 .first())
168 def getLocationParent(self):
170 Returns the object the location info should be inherited from
171 (Overridden)
173 raise Exception("Unimplemented method")
175 def getLocation(self):
176 if self.getOwnLocation():
177 return self.getOwnLocation()
178 return self.getInheritedLocation()
180 def getOwnLocation(self):
181 if len(self.places) > 0:
182 return self.places[0]
183 return None
185 def getInheritedLocation(self):
186 return self.getLocationParent().getLocation()
188 def getOwnRoom(self):
189 if len(self.rooms) > 0:
190 return self.rooms[0]
191 return None
193 def getRoom(self):
194 if self.getOwnRoom():
195 return self.getOwnRoom()
196 return self.getInheritedRoom()
198 def getInheritedRoom(self):
199 return self.getLocationParent().getRoom()
201 def setLocation(self, newLocation):
202 oldLocation = self.getOwnLocation()
203 if newLocation is None:
204 if len(self.places) > 0:
205 del self.places[0]
206 elif len(self.places) > 0:
207 self.places[0] = newLocation
208 else:
209 self.places.append(newLocation)
210 self.notifyModification()
212 def setRoom(self, newRoom):
213 oldRoom = self.getOwnRoom()
214 if newRoom is None:
215 if len(self.rooms) > 0:
216 del self.rooms[0]
217 elif len(self.rooms) > 0:
218 self.rooms[0] = newRoom
219 else:
220 self.rooms.append(newRoom)
221 self.notifyModification()
224 class CommonObjectBase(CoreObject, Fossilizable):
226 This class is for holding commonly used methods that are used by several classes.
227 It is inherited by the following classes:
228 * Category
229 * Conference
230 * Session
231 * Contribution
232 * SubContribution
233 * Material
234 * Resource
237 def getRecursiveManagerList(self):
238 av_set = set()
240 # Get the AccessProtectionLevel for this
241 apl = self.getAccessProtectionLevel()
243 if apl == -1:
244 pass
245 elif apl == 1:
246 for av in self.getManagerList():
247 av_set.add(av)
248 for av in self.getOwner().getRecursiveManagerList():
249 av_set.add(av)
250 else:
251 for av in self.getManagerList():
252 av_set.add(av)
254 if self.getOwner():
255 for av in self.getOwner().getRecursiveManagerList():
256 av_set.add(av)
258 return list(av_set)
260 def getRecursiveAllowedToAccessList(self, onlyManagers=False):
261 """Returns a set of Avatar resp. Group objects for those people resp.
262 e-groups allowed to access this object as well as all parent objects.
265 # Initialize set of avatars/groups: this will hold those
266 # people/groups explicitly
267 # allowed to access this object
268 av_set = set()
270 # Get the AccessProtectionLevel for this
271 apl = self.getAccessProtectionLevel()
273 # If this object is "absolutely public", then return an empty set
274 if apl == -1:
275 pass
277 # If this object is protected "all by itself", then get the list of
278 # people/groups allowed to access it, plus managers of owner(s)
279 elif apl == 1:
280 al = self.getAllowedToAccessList() + self.getManagerList() + \
281 self.getOwner().getRecursiveManagerList()
282 if al is not None:
283 for av in al:
284 av_set.add(av)
286 # If access settings are inherited (and PRIVATE) from its owners, look at those.
287 elif apl == 0 and self.isProtected():
288 # If event is protected, then get list of people/groups allowed
289 # to access, and add that to the set of avatars.
290 al = self.getAllowedToAccessList() + self.getManagerList()
291 if al is not None:
292 for av in al:
293 av_set.add(av)
295 # Add list of avatars/groups allowed to access parents objects.
296 owner = self.getOwner()
297 if owner is not None:
298 owner_al = owner.getRecursiveAllowedToAccessList(onlyManagers=True)
299 if owner_al is not None:
300 for av in owner_al:
301 av_set.add(av)
303 # return set containing whatever avatars/groups we may have collected
304 return av_set
306 def canIPAccess(self, ip):
307 domains = self.getAccessController().getAnyDomainProtection()
308 if domains:
309 return any(domain.belongsTo(ip) for domain in domains)
310 else:
311 return True
314 class CategoryManager(ObjectHolder):
315 idxName = "categories"
316 counterName = "CATEGORY"
318 def add(self, category):
319 ObjectHolder.add(self, category)
320 # Add category to the name index
321 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
322 nameIdx.index(category)
324 def remove(self, category):
325 ObjectHolder.remove(self, category)
326 # remove category from the name index
327 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
328 nameIdx.unindex(category)
329 Catalog.getIdx('categ_conf_sd').remove_category(category.getId())
331 def _newId(self):
333 returns a new id for the category
334 the id must not already exist in the collection
336 id = ObjectHolder._newId(self)
337 while self.hasKey(id):
338 id = ObjectHolder._newId(self)
339 return id
341 def getRoot(self):
342 root = DBMgr.getInstance().getDBConnection().root()
343 if not root.has_key("rootCategory"):
344 r = Category()
345 r.setName("Home")
346 self.add(r)
347 root["rootCategory"] = r
348 return root["rootCategory"]
350 def getDefaultConference(self):
351 dconf = HelperMaKaCInfo.getMaKaCInfoInstance().getDefaultConference()
352 if dconf == None:
353 return HelperMaKaCInfo.getMaKaCInfoInstance().setDefaultConference(DefaultConference())
354 else:
355 return dconf
358 class Category(CommonObjectBase):
359 fossilizes(ICategoryFossil)
361 def __init__(self):
363 self.id = ""
364 self.name = ""
365 self.description = ""
366 self.subcategories = {}
367 self.materials = {}
368 self.conferences = OOTreeSet()
369 self._numConferences = 0
370 self.owner = None
371 self._defaultStyle = {"simple_event": "", "meeting": ""}
372 self._order = 0
373 self.__ac = AccessController(self)
374 self.__confCreationRestricted = 1
375 self.__confCreators = []
376 self._visibility = 999
377 self._statistics = {"events": None, "contributions": None, "resources": None,
378 "updated": None}
379 self._icon = None
380 self.materials = {}
381 #self._materials = {}
382 #self.material ={}
383 self._tasksAllowed = False
384 self._tasks = {}
385 self._taskIdGenerator = 0
386 self._tasksPublic = True
387 self._tasksCommentPublic = True
388 self._tasksManagers = []
389 self._tasksCommentators = []
390 self._taskAccessList = []
391 self._timezone = ""
392 self.__materialGenerator = Counter()
393 self._notifyCreationList = ""
395 def __cmp__(self, other):
396 if type(self) is not type(other):
397 # This is actually dangerous and the ZODB manual says not to do this
398 # because it relies on memory order. However, this branch should never
399 # be taken anyway since we do not store different types in the same set
400 # or use them as keys.
401 return cmp(hash(self), hash(other))
402 return cmp(self.getId(), other.getId())
404 @return_ascii
405 def __repr__(self):
406 path = '/'.join(self.getCategoryPathTitles()[:-1])
407 return '<Category({0}, {1}, {2})>'.format(self.getId(), self.getName(), path)
409 @property
410 def url(self):
411 if self.isRoot():
412 return url_for('misc.index')
413 else:
414 return url_for('category.categoryDisplay', self)
416 def getAccessController(self):
417 return self.__ac
419 def updateNonInheritingChildren(self, elem, delete=False):
420 pass
422 def getNotifyCreationList(self):
423 """ self._notifyCreationList is a string containing the list of
424 email addresses to send an email to when a new event is created"""
425 try:
426 return self._notifyCreationList
427 except:
428 self._notifyCreationList = ""
429 return self._notifyCreationList
431 def setNotifyCreationList(self, value):
432 self._notifyCreationList = value
434 def getUniqueId(self):
435 return "cat%s" % self.getId()
437 def setPaper(self, newPaper):
438 if self.getPaper() is not None:
439 raise MaKaCError(_("The paper for this conference has already been set"), _("Conference"))
440 self.paper = newPaper
441 self.paper.setOwner(self)
442 self.notifyModification()
444 def removePaper(self):
445 if self.paper is None:
446 return
447 self.paper.delete()
448 self.paper.setOwner(None)
449 self.paper = None
450 self.notifyModification()
452 def recoverPaper(self, p):
453 self.setPaper(p)
454 p.recover()
456 def getPaper(self):
457 try:
458 if self.paper:
459 pass
460 except AttributeError:
461 self.paper = None
462 return self.paper
464 def setSlides(self, newSlides):
465 if self.getSlides() is not None:
466 raise MaKaCError(_("The slides for this conference have already been set"), _("Conference"))
467 self.slides = newSlides
468 self.slides.setOwner(self)
469 self.notifyModification()
471 def removeSlides(self):
472 if self.slides is None:
473 return
474 self.slides.delete()
475 self.slides.setOwner(None)
476 self.slides = None
477 self.notifyModification()
479 def recoverSlides(self, s):
480 self.setSlides(s)
481 s.recover()
483 def getSlides(self):
484 try:
485 if self.slides:
486 pass
487 except AttributeError:
488 self.slides = None
489 return self.slides
491 def setVideo(self, newVideo):
492 if self.getVideo() is not None:
493 raise MaKaCError(_("The video for this conference has already been set"), _("Conference"))
494 self.video = newVideo
495 self.video.setOwner(self)
496 self.notifyModification()
498 def removeVideo(self):
499 if self.getVideo() is None:
500 return
501 self.video.delete()
502 self.video.setOwner(None)
503 self.video = None
504 self.notifyModification()
506 def recoverVideo(self, v):
507 self.setVideo(v)
508 v.recover()
510 def getVideo(self):
511 try:
512 if self.video:
513 pass
514 except AttributeError:
515 self.video = None
516 return self.video
518 def setPoster(self, newPoster):
519 if self.getPoster() is not None:
520 raise MaKaCError(_("the poster for this conference has already been set"), _("Conference"))
521 self.poster = newPoster
522 self.poster.setOwner(self)
523 self.notifyModification()
525 def removePoster(self):
526 if self.getPoster() is None:
527 return
528 self.poster.delete()
529 self.poster.setOwner(None)
530 self.poster = None
531 self.notifyModification()
533 def recoverPoster(self, p):
534 self.setPoster(p)
535 p.recover()
537 def getPoster(self):
538 try:
539 if self.poster:
540 pass
541 except AttributeError:
542 self.poster = None
543 return self.poster
545 def setMinutes(self, newMinutes):
546 if self.getMinutes() is not None:
547 raise MaKaCError(_("The Minutes for this conference has already been set"))
548 self.minutes = newMinutes
549 self.minutes.setOwner(self)
550 self.notifyModification()
552 def createMinutes(self):
553 if self.getMinutes() is not None:
554 raise MaKaCError(_("The minutes for this conference have already been created"), _("Conference"))
555 self.minutes = Minutes()
556 self.minutes.setOwner(self)
557 self.notifyModification()
558 return self.minutes
560 def removeMinutes(self):
561 if self.getMinutes() is None:
562 return
563 self.minutes.delete()
564 self.minutes.setOwner(None)
565 self.minutes = None
566 self.notifyModification()
568 def recoverMinutes(self, min):
569 self.removeMinutes() # To ensure that the current minutes are put in
570 # the trash can.
571 self.minutes = min
572 self.minutes.setOwner(self)
573 min.recover()
574 self.notifyModification()
575 return self.minutes
577 def getMinutes(self):
578 #To be removed
579 try:
580 if self.minutes:
581 pass
582 except AttributeError, e:
583 self.minutes = None
584 return self.minutes
586 def addMaterial(self, newMat):
587 try:
588 newMat.setId(str(self.__materialGenerator.newCount()))
589 except:
590 self.__materialGenerator = Counter()
591 newMat.setId(self.__materialGenerator.newCount())
592 newMat.setOwner(self)
593 self.materials[newMat.getId()] = newMat
594 self.notifyModification()
596 def removeMaterial(self, mat):
597 if mat.getId() in self.materials.keys():
598 mat.delete()
599 self.materials[mat.getId()].setOwner(None)
600 del self.materials[mat.getId()]
601 self.notifyModification()
602 return "done: %s" % mat.getId()
603 elif mat.getId().lower() == 'minutes':
604 self.removeMinutes()
605 return "done: %s" % mat.getId()
606 elif mat.getId().lower() == 'paper':
607 self.removePaper()
608 return "done: %s" % mat.getId()
609 elif mat.getId().lower() == 'slides':
610 self.removeSlides()
611 return "done: %s" % mat.getId()
612 elif mat.getId().lower() == 'video':
613 self.removeVideo()
614 return "done: %s" % mat.getId()
615 elif mat.getId().lower() == 'poster':
616 self.removePoster()
617 return "done: %s" % mat.getId()
618 return "not done: %s" % mat.getId()
620 def recoverMaterial(self, recMat):
621 # Id must already be set in recMat.
622 recMat.setOwner(self)
623 self.materials[recMat.getId()] = recMat
624 recMat.recover()
625 self.notifyModification()
627 def getMaterialRegistry(self):
629 Return the correct material registry for this type
631 from MaKaC.webinterface.materialFactories import CategoryMFRegistry
632 return CategoryMFRegistry
634 def getMaterialById(self, matId):
635 if matId.lower() == 'paper':
636 return self.getPaper()
637 elif matId.lower() == 'slides':
638 return self.getSlides()
639 elif matId.lower() == 'video':
640 return self.getVideo()
641 elif matId.lower() == 'poster':
642 return self.getPoster()
643 elif matId.lower() == 'minutes':
644 return self.getMinutes()
645 elif self.materials.has_key(matId):
646 return self.materials[matId]
647 return None
649 def getMaterialList(self):
650 try:
651 return self.materials.values()
652 except:
653 self.materials = {}
654 return self.materials.values()
656 def getAllMaterialList(self):
657 l = self.getMaterialList()
658 if self.getPaper():
659 l.append(self.getPaper())
660 if self.getSlides():
661 l.append(self.getSlides())
662 if self.getVideo():
663 l.append(self.getVideo())
664 if self.getPoster():
665 l.append(self.getPoster())
666 if self.getMinutes():
667 l.append(self.getMinutes())
668 return l
670 def getTaskList(self):
671 try:
672 return self._tasks.values()
673 except:
674 self._tasks = {}
675 return self._tasks.values()
677 def getTitle(self):
678 return self.name
680 def getTasks(self):
681 try:
682 return self._tasks
683 except:
684 self._tasks = {}
685 return self._tasks
687 def getTask(self, taskId):
688 return self.getTasks().get(taskId, None)
690 def _getTasksAllowed(self):
691 try:
692 return self._tasksAllowed
693 except:
694 self._tasksAllowed = False
695 return self._tasksAllowed
697 def tasksAllowed(self):
698 if self.hasSubcategories():
699 return False
700 return self._getTasksAllowed()
702 def setTasksAllowed(self):
703 if self.hasSubcategories():
704 return False
705 self._getTasksAllowed()
706 self._tasksAllowed = True
707 self.notifyModification()
708 return True
710 def setTasksForbidden(self):
711 if len(self.getTaskList()) > 0:
712 return False
713 self._getTasksAllowed()
714 self._tasksAllowed = False
715 self.notifyModification()
716 return False
718 def _getNewTaskId(self):
719 try:
720 if self._taskIdGenerator:
721 pass
722 except:
723 self._taskIdGenerator = 0
724 self._taskIdGenerator = self._taskIdGenerator + 1
725 return self._taskIdGenerator
727 def newTask(self, user):
728 if user is None:
729 return None
730 newTask = task.Task(self, self._getNewTaskId(), user)
731 self.getTasks()["%s" % newTask.getId()] = newTask
732 self.notifyModification()
733 return newTask
735 def tasksPublic(self):
736 try:
737 return self._tasksPublic
738 except:
739 self._tasksPublic = True
740 return self._tasksPublic
742 def setTasksPublic(self):
743 self.tasksPublic()
744 self._tasksPublic = True
746 def setTasksPrivate(self):
747 self.tasksPublic()
748 self._tasksPublic = False
750 def tasksCommentPublic(self):
751 try:
752 return self._tasksCommentPublic
753 except:
754 self._tasksCommentPublic = True
755 return self._tasksCommentPublic
757 def setTasksCommentPublic(self):
758 self.tasksCommentPublic()
759 self._tasksCommentPublic = True
761 def setTasksCommentPrivate(self):
762 self.tasksCommentPublic()
763 self._tasksCommentPublic = False
765 def getTasksManagerList(self):
766 try:
767 return self._tasksManagers
768 except:
769 self._tasksManagers = []
770 self._p_changed = 1
771 return self._tasksManagers
773 def getTasksManager(self, index):
774 length = len(self.getTasksManagerList())
775 if index < 0 or index >= length:
776 return None
777 return self._tasksManagers[index]
779 def addTasksManager(self, user):
780 if user is None:
781 return False
782 self.getTasksManagerList().append(user)
783 self._p_changed = 1
784 return True
786 def removeTasksManager(self, index):
787 length = len(self.getTasksManagerList())
788 if index < 0 or index >= length:
789 return False
790 del self.getTasksManagerList()[index]
791 self._p_changed = 1
792 return True
794 def getTasksCommentatorList(self):
795 try:
796 return self._tasksCommentators
797 except:
798 self._tasksCommentators = []
799 self._p_changed = 1
800 return self._tasksCommentators
802 def getTasksCommentator(self, index):
803 length = len(self.getTasksCommentatorList())
804 if index < 0 or index >= length:
805 return None
806 return self._tasksCommentators[index]
808 def addTasksCommentator(self, user):
809 if user is None:
810 return False
811 self.getTasksCommentatorList().append(user)
812 self._p_changed = 1
813 return True
815 def removeTasksCommentator(self, index):
816 length = len(self.getTasksCommentatorList())
817 if index < 0 or index >= length:
818 return False
819 del self._tasksCommentators[index]
820 self._p_changed = 1
821 return True
823 def getTasksAccessList(self):
824 try:
825 return self._tasksAccessList
826 except:
827 self._tasksAccessList = []
828 self._p_changed = 1
829 return self._tasksAccessList
831 def getTasksAccessPerson(self, index):
832 length = len(self.getTasksAccessList())
833 if index < 0 or index >= length:
834 return None
835 return self._tasksAccessList[index]
837 def addTasksAccessPerson(self, user):
838 if user is None:
839 return False
840 self.getTasksAccessList().append(user)
841 self._p_changed = 1
842 return True
844 def removeTasksAccessPerson(self, index):
845 length = len(self.getTasksAccessList())
846 if index < 0 or index >= length:
847 return False
848 del self.getTasksAccessList()[index]
849 self._p_changed = 1
850 return True
852 def hasSubcategories(self):
853 return len(self.subcategories.values()) > 0
855 def getVisibility(self):
857 Returns category visibility, considering that it can be
858 restricted by parent categories
860 owner = self.getOwner()
861 visibility = int(self._visibility)
863 # visibility can be restricted by parent categories
864 if owner:
865 return max(0, min(visibility, owner.getVisibility() + 1))
866 else:
867 return visibility
869 def setVisibility(self, visibility=999):
870 self._visibility = int(visibility)
871 self._reindex()
873 def isSuggestionsDisabled(self):
874 try:
875 return self._suggestions_disabled
876 except AttributeError:
877 self._suggestions_disabled = False
878 return False
880 def setSuggestionsDisabled(self, value):
881 self._suggestions_disabled = value
883 def _reindex(self):
884 catIdx = indexes.IndexesHolder().getIndex('category')
885 catIdx.reindexCateg(self)
886 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
887 catDateIdx.reindexCateg(self)
888 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
889 catDateAllIdx.reindexCateg(self)
891 def isRoot(self):
892 #to be improved
893 return self.owner is None
895 def getDefaultStyle(self, type):
896 try:
897 return self._defaultStyle[type]
898 except:
899 return ""
901 def setDefaultStyle(self, type, style, subcatsStyle=False):
902 try:
903 self._defaultStyle[type] = style
904 except:
905 self._defaultStyle = {"simple_event": "", "meeting": ""}
906 self._defaultStyle[type] = style
907 self.notifyModification()
908 #raise str(subcatsStyle)
909 if subcatsStyle:
911 categ = self.getSubCategoryList()
913 for cat in categ:
914 cat.setDefaultStyle(type, style, subcatsStyle)
916 ##################################
917 # Fermi timezone awareness #
918 ##################################
919 def getTimezone(self):
920 try:
921 if self._timezone not in all_timezones:
922 self.setTimezone('UTC')
923 return self._timezone
924 except:
925 self.setTimezone('UTC')
926 return 'UTC'
928 def setTimezone(self, tz):
929 self._timezone = tz
931 def changeConfTimezones(self, tz):
932 for conference in self.getConferenceList():
933 conference.moveToTimezone(tz)
935 ##################################
936 # Fermi timezone awareness(end) #
937 ##################################
939 def getOrder(self):
940 try:
941 return self._order
942 except:
943 self._order = 0
944 return 0
946 def setOrder(self, order):
947 self._order = order
949 def getId(self):
950 return self.id
952 def setId(self, newId):
953 self.id = str(newId.strip())
955 def getLocator(self):
956 """Gives back (Locator) a globaly unique identification encapsulated
957 in a Locator object for the category instance """
958 d = Locator()
959 d["categId"] = self.getId()
960 return d
962 def getCategory(self):
963 return self
965 def getOwner(self):
966 return self.owner
968 def setOwner(self, newOwner):
969 if self.getOwner() is not None and newOwner is not None and self.getOwner() != newOwner:
970 self.move(newOwner)
971 else:
972 self.owner = newOwner
974 def getCategoryPath(self):
975 if self.isRoot():
976 return [self.getId()]
977 else:
978 l = self.getOwner().getCategoryPath()
979 l.append(self.getId())
980 return l
982 def iterParents(self):
983 categ = self
984 while not categ.isRoot():
985 categ = categ.getOwner()
986 yield categ
988 def getCategoryPathTitles(self):
989 # Breadcrumbs
990 breadcrumbs = []
991 cat = self
992 while cat:
993 breadcrumbs.insert(0, cat.getTitle())
994 cat = cat.getOwner()
995 return breadcrumbs
997 def delete(self, deleteConferences=0):
998 """removes completely a category (and all its sub-items) from the
999 system"""
1001 oldOwner = self.getOwner()
1003 if self.isRoot():
1004 raise MaKaCError(_("Root category cannot be deleted"), _("Category"))
1005 if not deleteConferences:
1006 if self.getNumConferences() > 0:
1007 raise MaKaCError(_("This category still contains some conferences, please remove them first"), _("Category"))
1008 for subcateg in self.getSubCategoryList():
1009 subcateg.delete(deleteConferences)
1010 for conference in self.getConferenceList():
1011 self.removeConference(conference, delete=True)
1012 self.getOwner()._removeSubCategory(self)
1013 CategoryManager().remove(self)
1014 for prin in self.__ac.getAccessList():
1015 if isinstance(prin, AvatarUserWrapper):
1016 prin.unlinkTo(self, "access")
1017 for prin in self.__ac.getModifierList():
1018 if isinstance(prin, AvatarUserWrapper):
1019 prin.unlinkTo(self, "manager")
1020 TrashCanManager().add(self)
1022 signals.category.deleted.send(self)
1024 return
1026 def move(self, newOwner):
1027 oldOwner = self.getOwner()
1028 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
1029 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
1031 catDateIdx.unindexCateg(self)
1032 catDateAllIdx.unindexCateg(self)
1034 self.getOwner()._removeSubCategory(self)
1035 newOwner._addSubCategory(self)
1036 self._reindex()
1037 catDateIdx.indexCateg(self)
1038 catDateAllIdx.indexCateg(self)
1040 signals.category.moved.send(self, old_parent=oldOwner, new_parent=newOwner)
1042 def getName(self):
1043 return self.name
1045 def setName(self, newName):
1046 oldName = self.name
1047 self.name = newName.strip()
1049 # Reindex when name changes
1050 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
1051 nameIdx.unindex(self)
1052 nameIdx.index(self)
1054 signals.category.title_changed.send(self, old=oldName, new=newName)
1056 def getDescription(self):
1057 return self.description
1059 def setDescription(self, newDesc):
1060 self.description = newDesc.strip()
1062 def moveConference(self, conf, toCateg):
1064 Moves a conference from this category to another one
1066 self.removeConference(conf)
1067 toCateg._addConference(conf)
1068 signals.event.moved.send(conf, old_parent=self, new_parent=toCateg)
1070 def _addSubCategory(self, newSc):
1071 #categories can only contain either conferences either other categories
1072 # but can never contain both. For the moment an exception is raised
1073 # but this could be replaced by the following policy: if a
1074 # sub-category is to be added to a category already containing
1075 # conferences then the conferes are moved into the new sub-category
1076 # and it is added to target category.
1077 #first, check that the category is registered if not raise an exception
1078 if len(self.conferences) > 0:
1079 for conf in self.getConferenceList():
1080 self.moveConference(conf, newSc)
1082 if len(self.conferences) > 0:
1083 raise MaKaCError(_("Cannot add subcategory: the current category already contains events"), _("Category"))
1084 newSc.setOwner(self)
1085 self.subcategories[newSc.getId()] = newSc
1086 self._incNumConfs(newSc.getNumConferences())
1088 def _removeSubCategory(self, sc):
1089 """if the given subcategory belongs to the current category it removes
1090 it from the subcategories list (don't use this method, use delete
1091 instead)
1093 if sc in self.getSubCategoryList():
1094 self._decNumConfs(sc.getNumConferences())
1095 del self.subcategories[sc.getId()]
1096 sc.setOwner(None)
1098 def newSubCategory(self, protection):
1099 cm = CategoryManager()
1100 sc = Category()
1101 cm.add(sc)
1103 # set the protection
1104 sc.setProtection(protection)
1106 Catalog.getIdx('categ_conf_sd').add_category(sc.getId())
1107 signals.category.created.send(sc, parent=self)
1109 self._addSubCategory(sc)
1110 sc.setOrder(self.getSubCategoryList()[-1].getOrder() + 1)
1112 return sc
1114 def _incNumConfs(self, num=1):
1115 """Increases the number of conferences for the current category in a given number.
1116 WARNING: Only Categories must use this method!!!"""
1117 self._numConferences = self.getNumConferences()
1118 self._numConferences += num
1119 if self.getOwner() is not None:
1120 self.getOwner()._incNumConfs(num)
1122 def _decNumConfs(self, num=1):
1123 """Decreases the number of conferences for the current category in a given number.
1124 WARNING: Only Categories must use this method!!!"""
1125 self._numConferences = self.getNumConferences()
1126 self._numConferences -= num
1127 if self.getOwner() is not None:
1128 self.getOwner()._decNumConfs(num)
1130 def _addConference(self, newConf):
1131 if len(self.subcategories) > 0:
1132 raise MaKaCError(_("Cannot add event: the current category already contains some sub-categories"), _("Category"))
1133 if newConf.getId() == "":
1134 raise MaKaCError(_("Cannot add to a category an event which is not registered"), _("Category"))
1135 self.conferences.insert(newConf)
1136 newConf.addOwner(self)
1137 self._incNumConfs(1)
1138 self.indexConf(newConf)
1140 def getAccessKey(self):
1141 return ""
1143 def getModifKey(self):
1144 return ""
1146 def indexConf(self, conf):
1147 # Specific for category changes, calls Conference.indexConf()
1148 # (date-related indexes)
1149 catIdx = indexes.IndexesHolder().getIndex('category')
1150 catIdx.indexConf(conf)
1151 conf.indexConf()
1153 def unindexConf(self, conf):
1154 catIdx = indexes.IndexesHolder().getIndex('category')
1155 catIdx.unindexConf(conf)
1156 conf.unindexConf()
1158 def newConference(self, creator, id="", creationDate=None, modificationDate=None):
1159 conf = Conference(creator, id, creationDate, modificationDate)
1160 ConferenceHolder().add(conf)
1161 self._addConference(conf)
1162 conf.linkCreator()
1164 signals.event.created.send(conf, parent=self)
1166 return conf
1168 def removeConference(self, conf, notify=True, delete=False):
1169 if not (conf in self.conferences):
1170 return
1172 self.unindexConf(conf)
1174 self.conferences.remove(conf)
1175 if delete:
1176 conf.delete()
1177 conf.removeOwner(self, notify)
1178 self._decNumConfs(1)
1180 def getSubCategoryList(self):
1181 subcategs = self.subcategories.values()
1182 cl = []
1183 for categ in subcategs:
1184 cl.append("%04s%s-%s" % (categ.getOrder(), categ.getName().replace("-", ""), categ.getId()))
1185 cl.sort()
1186 res = []
1187 for c in cl:
1188 id = c.split("-")[1]
1189 res.append(self.subcategories[id])
1190 return res
1192 def iteritems(self, *args):
1193 return self.conferences.iteritems(*args)
1195 def itervalues(self, *args):
1196 return self.conferences.itervalues(*args)
1198 def getConferenceList(self, sortType=1):
1199 """returns the list of conferences included in the current category.
1200 Thanks to the used structure the list is sorted by date.
1201 We can choose other sorting types:
1203 sortType=1--> By date
1204 sortType=2--> Alphabetically
1205 sortType=3--> Alphabetically - Reversed
1208 res = sorted(self.conferences, cmp=Conference._cmpByDate)
1210 if sortType == 2:
1211 res.sort(Conference._cmpTitle)
1212 elif sortType == 3:
1213 res.sort(Conference._cmpTitle)
1214 res = reversed(res)
1215 return res
1217 def iterConferences(self):
1218 """returns the iterator for conferences.
1220 return self.conferences
1222 def iterAllConferences(self):
1223 """returns the iterator for conferences in all subcategories.
1225 for conf in self.conferences:
1226 yield conf
1228 for subcateg in self.subcategories.itervalues():
1229 for conf in subcateg.iterAllConferences():
1230 yield conf
1232 def getAllConferenceList(self):
1233 """returns the list of all conferences included in the current category
1234 and in all its subcategories"""
1235 res = self.getConferenceList()
1236 subcategs = self.getSubCategoryList()
1237 if subcategs != []:
1238 for subcateg in subcategs:
1239 res.extend(subcateg.getAllConferenceList())
1240 return res
1242 def getRelativeEvent(self, which, conf=None):
1243 index = Catalog.getIdx('categ_conf_sd').getCategory(self.getId())
1244 if which == 'first':
1245 return list(index[index.minKey()])[0]
1246 elif which == 'last':
1247 return list(index[index.maxKey()])[-1]
1248 elif which in ('next', 'prev'):
1249 categIter = index.itervalues()
1250 if conf:
1251 prev = None
1252 for c in categIter:
1253 if c == conf:
1254 break
1255 prev = c
1256 nextEvt = next(categIter, None)
1257 if which == 'next':
1258 return nextEvt
1259 else:
1260 return prev
1261 else:
1262 raise AttributeError("'conf' parameter missing")
1263 else:
1264 raise AttributeError("Unknown argument value: '%s'" % which)
1266 def _setNumConferences(self):
1267 self._numConferences = 0
1268 if self.conferences:
1269 self._incNumConfs(len(self.conferences))
1270 else:
1271 for sc in self.getSubCategoryList():
1272 self._incNumConfs(sc.getNumConferences())
1274 def getNumConferences(self):
1275 """returns the total number of conferences contained in the current
1276 category and all its sub-categories (if any)"""
1277 #this new approach will speed up considerably the counting of category
1278 # conferences. However, it will give non accurate results for
1279 # conferences within many categories (a conference will be counted
1280 # twice in parent categories).
1281 # Besides this approach will generate much more conflict errors. This
1282 # can be reduced by simply isolating the counter in a separate object.
1283 try:
1284 if self._numConferences:
1285 pass
1286 except AttributeError:
1287 self._setNumConferences()
1288 return self._numConferences
1290 def _getRepository(self):
1291 dbRoot = DBMgr.getInstance().getDBConnection().root()
1292 try:
1293 fr = dbRoot["local_repositories"]["main"]
1294 except KeyError, e:
1295 fr = fileRepository.MaterialLocalRepository()
1296 dbRoot["local_repositories"] = OOBTree()
1297 dbRoot["local_repositories"]["main"] = fr
1298 return fr
1300 def removeResource(self, res):
1301 pass
1303 def setIcon(self, iconFile):
1304 iconFile.setOwner(self)
1305 iconFile.setId("icon")
1306 iconFile.archive(self._getRepository())
1307 iconFile.setProtection(-1)
1308 if self.getIcon() is not None:
1309 self._icon.delete()
1310 self._icon = iconFile
1311 self.notifyModification()
1313 def getIcon(self):
1314 try:
1315 if self._icon:
1316 pass
1317 except AttributeError, e:
1318 self._icon = None
1319 return self._icon
1321 def getIconURL(self):
1322 if self.getIcon() is None:
1323 return ""
1324 return self._icon.getURL()
1326 def removeIcon(self):
1327 if self.getIcon() is None:
1328 return
1329 self._icon.delete()
1330 self._icon = None
1331 self.notifyModification()
1333 def recoverIcon(self, icon):
1334 icon.setOwner(self)
1335 if self.getIcon() is not None:
1336 self._icon.delete()
1337 self._icon = icon
1338 icon.recover()
1339 self.notifyModification()
1341 def getManagerList(self):
1342 return self.__ac.getModifierList()
1344 def grantModification(self, prin):
1345 self.__ac.grantModification(prin)
1346 if isinstance(prin, AvatarUserWrapper):
1347 prin.linkTo(self, "manager")
1349 def revokeModification(self, prin):
1350 self.__ac.revokeModification(prin)
1351 if isinstance(prin, AvatarUserWrapper):
1352 prin.unlinkTo(self, "manager")
1354 def canModify(self, aw_or_user):
1355 if hasattr(aw_or_user, 'getUser'):
1356 aw_or_user = aw_or_user.getUser()
1357 return self.canUserModify(aw_or_user)
1359 def canUserModify(self, av):
1360 inherited = 0
1361 if self.getOwner() is not None:
1362 inherited = self.getOwner().canUserModify(av)
1363 return inherited or self.__ac.canModify(av)
1365 def getAllowedToAccessList(self):
1366 return self.__ac.getAccessList()
1368 def canKeyAccess(self, aw):
1369 # Categories don't allow access keys
1370 return False
1372 def isProtected(self):
1373 return self.__ac.isProtected()
1375 def getAccessProtectionLevel(self):
1376 return self.__ac.getAccessProtectionLevel()
1378 def isItselfProtected(self):
1379 return self.__ac.isItselfProtected()
1381 def hasAnyProtection(self):
1382 if self.__ac.isProtected() or len(self.getDomainList()) > 0:
1383 return True
1384 if self.getAccessProtectionLevel() == -1: # PUBLIC
1385 return False
1386 if self.getOwner() is not None:
1387 return self.getOwner().hasAnyProtection()
1388 return False
1390 def setProtection(self, private):
1392 Allows to change the category's access protection
1395 oldProtection = 1 if self.isProtected() else -1
1397 self.__ac.setProtection(private)
1398 if oldProtection != private:
1399 signals.category.protection_changed.send(self, old=oldProtection, new=private)
1401 def hasProtectedOwner(self):
1402 return self.__ac._getFatherProtection()
1404 def isAllowedToAccess(self, av):
1405 """Says whether an avatar can access a category independently of it is
1406 or not protected or domain filtered
1408 if self.__ac.canUserAccess(av) or self.canUserModify(av):
1409 return True
1410 if not self.isItselfProtected() and self.getOwner():
1411 return self.getOwner().isAllowedToAccess(av)
1413 def canView(self, aw):
1414 if self.canAccess(aw):
1415 return True
1416 for conf in self.getConferenceList():
1417 if conf.canView(aw):
1418 return True
1419 for subcateg in self.getSubCategoryList():
1420 if subcateg.canView(aw):
1421 return True
1422 return False
1424 def canAccess(self, aw):
1425 if not self.hasAnyProtection():
1426 return True
1427 if not self.isProtected():
1428 # domain checking only triggered if the category is PUBLIC
1429 return self.canIPAccess(request.remote_addr) or \
1430 self.isAllowedToCreateConference(aw.getUser()) or \
1431 self.isAllowedToAccess(aw.getUser())
1432 return self.isAllowedToCreateConference(aw.getUser()) or \
1433 self.isAllowedToAccess(aw.getUser())
1435 def grantAccess(self, prin):
1436 self.__ac.grantAccess(prin)
1437 if isinstance(prin, AvatarUserWrapper):
1438 prin.linkTo(self, "access")
1440 def revokeAccess(self, prin):
1441 self.__ac.revokeAccess(prin)
1442 if isinstance(prin, AvatarUserWrapper):
1443 prin.unlinkTo(self, "access")
1445 def isConferenceCreationRestricted(self):
1446 return self.__confCreationRestricted
1448 def restrictConferenceCreation(self):
1449 self.__confCreationRestricted = 1
1451 def allowConferenceCreation(self):
1452 self.__confCreationRestricted = 0
1454 def grantConferenceCreation(self, prin):
1455 if prin not in self.__confCreators:
1456 self.__confCreators.append(prin)
1457 if isinstance(prin, AvatarUserWrapper):
1458 prin.linkTo(self, "creator")
1459 self._p_changed = 1
1461 def revokeConferenceCreation(self, prin):
1462 if prin in self.__confCreators:
1463 self.__confCreators.remove(prin)
1464 if isinstance(prin, AvatarUserWrapper):
1465 prin.unlinkTo(self, "creator")
1466 self._p_changed = 1
1468 def getConferenceCreatorList(self):
1469 return self.__confCreators
1471 def isAllowedToCreateConference(self, av):
1473 if self.canUserModify(av):
1474 return 1
1476 # Avatar is directly in the list
1477 if av in self.__confCreators:
1478 return 1
1480 # Otherwise, if it is a member of one of the groups in the list...
1481 for group in self.__confCreators:
1482 if isinstance(group, GroupWrapper):
1483 if group.containsUser(av):
1484 return 1
1485 return 0
1487 def canCreateConference(self, av):
1488 if not self.isConferenceCreationRestricted():
1489 return 1
1490 return self.isAllowedToCreateConference(av)
1492 def requireDomain(self, dom):
1493 self.__ac.requireDomain(dom)
1494 signals.category.domain_access_granted.send(self, domain=dom)
1496 def freeDomain(self, dom):
1497 self.__ac.freeDomain(dom)
1498 signals.category.domain_access_revoked.send(self, domain=dom)
1501 def getDomainList(self):
1502 return self.__ac.getRequiredDomainList()
1504 def getStatistics(self):
1505 try:
1506 if self._statistics:
1507 pass
1508 except AttributeError, e:
1509 self._statistics = {}
1510 return self._statistics
1512 def notifyModification(self, raiseEvent=True):
1513 """Method called to notify the current category has been modified.
1515 if raiseEvent:
1516 signals.category.data_changed.send(self)
1517 self._p_changed = 1
1520 class CustomLocation(Persistent):
1522 def __init__(self, **locationData):
1523 self.name = ""
1524 self.address = ""
1525 self.room = ""
1527 def setValues(self, data):
1528 self.setName(data.get("name", ""))
1529 self.setAddress(data.get("address", ""))
1530 self.setRoom(data.get("room", ""))
1532 def getValues(self):
1533 d = {}
1534 d["name"] = self.getName()
1535 d["address"] = self.getAddress()
1536 d["room"] = self.getRoom()
1537 return d
1539 def clone(self):
1540 newCL = CustomLocation()
1541 newCL.setValues(self.getValues())
1542 return newCL
1544 def setName(self, newName):
1545 self.name = newName
1547 def getName(self):
1548 return self.name
1550 def setAddress(self, newAddress):
1551 self.address = newAddress
1553 def getAddress(self):
1554 return self.address
1556 def setRoom(self, newRoom):
1557 self.room = newRoom
1559 def getRoom(self):
1560 return self.room
1563 class CustomRoom(Persistent):
1565 def __init__(self):
1566 self.name = ""
1568 def setValues(self, data):
1569 self.setName(data.get("name", ""))
1570 self.setFullName(data.get("fullName"))
1572 def getValues(self):
1573 d = {}
1574 d["name"] = self.getName()
1575 d["fullName"] = self.getFullName()
1576 return d
1578 def getId(self):
1579 return "Custom"
1581 def clone(self):
1582 newCR = CustomRoom()
1583 newCR.setValues(self.getValues())
1584 return newCR
1586 def setName(self, newName):
1587 self.name = newName.strip()
1589 def getName(self):
1590 return self.name
1592 def retrieveFullName(self, location):
1593 if not location:
1594 return
1595 room = Room.find_first(Room.name == fix_broken_string(self.name, True),
1596 Location.name == fix_broken_string(location, True),
1597 _join=Room.location)
1598 self.fullName = room.full_name if room else None
1600 def setFullName(self, newFullName):
1601 self.fullName = newFullName
1603 def getFullName(self):
1604 if not hasattr(self, 'fullName'):
1605 self.fullName = None
1606 return self.fullName
1609 class ConferenceParticipation(Persistent, Fossilizable):
1611 fossilizes(IConferenceParticipationFossil, IConferenceParticipationMinimalFossil)
1613 def __init__(self):
1614 self._firstName=""
1615 self._surName=""
1616 self._email=""
1617 self._affiliation=""
1618 self._address=""
1619 self._phone=""
1620 self._title=""
1621 self._fax=""
1623 def _notifyModification( self ):
1624 pass
1626 def setValues(self, data):
1627 self.setFirstName(data.get("firstName", ""))
1628 self.setFamilyName(data.get("familyName",""))
1629 self.setAffiliation(data.get("affilation",""))
1630 self.setAddress(data.get("address",""))
1631 self.setEmail(data.get("email",""))
1632 self.setFax(data.get("fax",""))
1633 self.setTitle(data.get("title",""))
1634 self.setPhone(data.get("phone",""))
1635 self._notifyModification()
1637 def getValues(self):
1638 data={}
1639 data["firstName"]=self.getFirstName()
1640 data["familyName"]=self.getFamilyName()
1641 data["affilation"]=self.getAffiliation()
1642 data["address"]=self.getAddress()
1643 data["email"]=self.getEmail()
1644 data["fax"]=self.getFax()
1645 data["title"]=self.getTitle()
1646 data["phone"]=self.getPhone()
1647 return data
1649 def setId(self, newId):
1650 self._id = newId
1652 def getId( self ):
1653 return self._id
1655 def setDataFromAvatar(self,av):
1656 # av is an Avatar object.
1657 if av is None:
1658 return
1659 self.setFirstName(av.getName())
1660 self.setFamilyName(av.getSurName())
1661 self.setEmail(av.getEmail())
1662 self.setAffiliation(av.getOrganisation())
1663 self.setAddress(av.getAddress())
1664 self.setPhone(av.getTelephone())
1665 self.setTitle(av.getTitle())
1666 self.setFax(av.getFax())
1667 self._notifyModification()
1669 def setDataFromOtherCP(self,cp):
1670 # cp is a ConferenceParticipation object.
1671 if cp is None:
1672 return
1673 self.setFirstName(cp.getFirstName())
1674 self.setFamilyName(cp.getFamilyName())
1675 self.setEmail(cp.getEmail())
1676 self.setAffiliation(cp.getAffiliation())
1677 self.setAddress(cp.getAddress())
1678 self.setPhone(cp.getPhone())
1679 self.setTitle(cp.getTitle())
1680 self.setFax(cp.getFax())
1681 self._notifyModification()
1683 def delete( self ):
1684 TrashCanManager().add(self)
1686 def recover(self):
1687 TrashCanManager().remove(self)
1689 @Updates (['MaKaC.conference.ConferenceParticipation',
1690 'MaKaC.conference.SessionChair',
1691 'MaKaC.conference.SlotChair'], 'firstName')
1692 def setFirstName(self,newName):
1693 tmp=newName.strip()
1694 if tmp==self._firstName:
1695 return
1696 self._firstName=tmp
1697 self._notifyModification()
1699 def getFirstName( self ):
1700 return self._firstName
1702 @Updates (['MaKaC.conference.ConferenceParticipation',
1703 'MaKaC.conference.SessionChair',
1704 'MaKaC.conference.SlotChair'], 'familyName')
1705 def setFamilyName(self,newName):
1706 tmp=newName.strip()
1707 if tmp==self._surName:
1708 return
1709 self._surName=tmp
1710 self._notifyModification()
1712 def getFamilyName( self ):
1713 return self._surName
1715 @Updates (['MaKaC.conference.ConferenceParticipation',
1716 'MaKaC.conference.SessionChair',
1717 'MaKaC.conference.SlotChair'], 'email')
1718 def setEmail(self,newMail):
1719 tmp=newMail.strip()
1720 if tmp==self._email:
1721 return
1722 self._email=newMail.strip()
1723 self._notifyModification()
1725 def getEmail( self ):
1726 return self._email
1728 @Updates (['MaKaC.conference.ConferenceParticipation',
1729 'MaKaC.conference.SessionChair',
1730 'MaKaC.conference.SlotChair'], 'affiliation')
1731 def setAffiliation(self,newAffil):
1732 self._affiliation=newAffil.strip()
1733 self._notifyModification()
1735 def getAffiliation(self):
1736 return self._affiliation
1738 @Updates (['MaKaC.conference.ConferenceParticipation',
1739 'MaKaC.conference.SessionChair',
1740 'MaKaC.conference.SlotChair'], 'address')
1741 def setAddress(self,newAddr):
1742 self._address=newAddr.strip()
1743 self._notifyModification()
1745 def getAddress(self):
1746 return self._address
1748 @Updates (['MaKaC.conference.ConferenceParticipation',
1749 'MaKaC.conference.SessionChair',
1750 'MaKaC.conference.SlotChair'], 'phone')
1751 def setPhone(self,newPhone):
1752 self._phone=newPhone.strip()
1753 self._notifyModification()
1755 def getPhone(self):
1756 return self._phone
1758 @Updates (['MaKaC.conference.ConferenceParticipation',
1759 'MaKaC.conference.SessionChair',
1760 'MaKaC.conference.SlotChair'], 'title')
1761 def setTitle(self,newTitle):
1762 self._title=newTitle.strip()
1763 self._notifyModification()
1765 def getTitle(self):
1766 return self._title
1768 @Updates (['MaKaC.conference.ConferenceParticipation',
1769 'MaKaC.conference.SessionChair',
1770 'MaKaC.conference.SlotChair'], 'fax')
1771 def setFax(self,newFax):
1772 self._fax=newFax.strip()
1773 self._notifyModification()
1775 def getFax(self):
1776 return self._fax
1778 def getFullName( self ):
1779 res = self.getFamilyName()
1780 if self.getFirstName() != "":
1781 if res.strip() != "":
1782 res = "%s, %s"%( res, self.getFirstName() )
1783 else:
1784 res = self.getFirstName()
1785 if self.getTitle() != "":
1786 res = "%s %s"%( self.getTitle(), res )
1787 return res
1789 def getFullNameNoTitle( self ):
1790 res = self.getFamilyName()
1791 if self.getFirstName() != "":
1792 if res.strip() != "":
1793 res = "%s, %s"%( res, self.getFirstName() )
1794 else:
1795 res = self.getFirstName()
1796 return res
1798 def getDirectFullName( self ):
1799 res = "%s %s"%( self.getFirstName(), self.getFamilyName() )
1800 res=res.strip()
1801 if self.getTitle() != "":
1802 res = "%s %s"%( self.getTitle(), res )
1803 return res
1805 def getAbrName(self):
1806 res = self.getFamilyName()
1807 if self.getFirstName():
1808 if res:
1809 res = "%s, " % res
1810 res = "%s%s." % (res, safe_upper(safe_slice(self.getFirstName(), 0, 1)))
1811 return res
1813 @staticmethod
1814 def _cmpFamilyName( cp1, cp2 ):
1815 o1 = "%s %s"%(cp1.getFamilyName(), cp1.getFirstName())
1816 o2 = "%s %s"%(cp2.getFamilyName(), cp2.getFirstName())
1817 o1=o1.lower().strip()
1818 o2=o2.lower().strip()
1819 return cmp( o1, o2 )
1822 class ConferenceChair(ConferenceParticipation, Fossilizable):
1824 fossilizes(IConferenceParticipationFossil)
1826 def __init__(self):
1827 self._conf=None
1828 self._id=""
1829 ConferenceParticipation.__init__(self)
1831 def _notifyModification( self ):
1832 if self._conf != None:
1833 self._conf.notifyModification()
1835 def clone(self):
1836 newCC=ConferenceChair()
1837 newCC.setValues(self.getValues())
1838 return newCC
1840 def getConference(self):
1841 return self._conf
1843 def getId(self):
1844 return self._id
1846 def includeInConference(self,conf,id):
1847 if self.getConference()==conf and self.getId()==id.strip():
1848 return
1849 self._conf=conf
1850 self._id=id
1852 def delete( self ):
1853 self._conf=None
1854 ConferenceParticipation.delete(self)
1856 def getLocator(self):
1857 if self.getConference() is None:
1858 return None
1859 loc=self.getConference().getLocator()
1860 loc["chairId"]=self.getId()
1861 return loc
1863 class SubmitterIndex(Persistent):
1864 """Index for contribution submitters.
1866 This class allows to index users with submission privileges over the
1867 conference contributions so the owner can answer optimally to the query
1868 if a user has any submission privilege over any contribution
1869 of the conference.
1870 It is implemented by simply using a BTree where the Avatar id is used
1871 as key (because it is unique and non variable) and a list of
1872 contributions over which he has submission privileges is kept as values.
1873 It is the responsability of the index owner (conference contributions)
1874 to keep it up-to-date i.e. notify conference sumitters additions and
1875 removals.
1878 def __init__( self ):
1879 self._idx = OOBTree()
1880 self._idxEmail = OOBTree()
1882 def _getIdxEmail(self):
1883 try:
1884 return self._idxEmail
1885 except:
1886 self._idxEmail = OOBTree()
1887 return self._idxEmail
1889 def getContributions(self,av):
1890 """Gives a list with the contributions over which a user has
1891 coordination privileges
1893 if av == None:
1894 return []
1895 ret = self._idx.get(av.getId(),[])
1896 if not ret:
1897 self._moveEmailtoId(av)
1898 ret = self._idx.get(av.getId(),[])
1899 return ret
1901 def index(self,av,contrib):
1902 """Registers in the index a submitter of a contribution.
1904 if av==None or contrib==None:
1905 return
1906 if not self._idx.has_key(av.getId()):
1907 l=[]
1908 self._idx[av.getId()]=l
1909 else:
1910 l=self._idx[av.getId()]
1911 if contrib not in l:
1912 l.append(contrib)
1913 self._idx[av.getId()]=l
1915 def indexEmail(self, email, contrib):
1916 if not email or not contrib:
1917 return
1918 if not self._getIdxEmail().has_key(email):
1919 l = [contrib]
1920 self._getIdxEmail()[email] = l
1921 else:
1922 l = self._getIdxEmail()[email]
1923 if not contrib in l:
1924 l.append(contrib)
1925 self._getIdxEmail()[email] = l
1928 def unindex(self,av,contrib):
1929 if av==None or contrib==None:
1930 return
1931 l=self._idx.get(av.getId(),[])
1932 if contrib in l:
1933 l.remove(contrib)
1934 self._idx[av.getId()]=l
1936 def unindexEmail(self, email, contrib):
1937 if not email or not contrib:
1938 return
1939 if self._getIdxEmail().has_key(email):
1940 l = self._getIdxEmail()[email]
1941 if contrib in l:
1942 l.remove(contrib)
1943 if l == []:
1944 del self._getIdxEmail()[email]
1945 else:
1946 self._getIdxEmail()[email] = l
1948 def _moveEmailtoId(self, av):
1949 id = av.getId()
1950 email = av.getEmail()
1951 if not self._idx.has_key(id):
1952 if self._getIdxEmail().has_key(email):
1953 self._idx[id] = self._getIdxEmail()[email]
1954 del self._getIdxEmail()[email]
1957 class ReportNumberHolder(Persistent):
1959 def __init__(self, owner):
1960 self._owner=owner
1961 self._reports={}
1963 def getOwner(self):
1964 return self._owner
1966 def addReportNumber(self, system, number):
1967 if system in self.getReportNumberKeys() or system in Config.getInstance().getReportNumberSystems().keys():
1968 try:
1969 if not number in self._reports[system]:
1970 self._reports[system].append(number)
1971 except:
1972 self._reports[system]=[ number ]
1973 self.notifyModification()
1975 def removeReportNumber(self, system, number):
1976 if self.hasReportNumbersBySystem(system):
1977 if number in self._reports[system]:
1978 self._reports[system].remove(number)
1979 self.notifyModification()
1981 def removeReportNumberById(self, id):
1982 try:
1983 rn = self.listReportNumbers()[int(id)]
1984 self.removeReportNumber(rn[0], rn[1])
1985 except:
1986 pass
1988 def hasReportNumbersBySystem(self, system):
1989 return self._reports.has_key(system)
1991 def getReportNumbersBySystem(self, system):
1992 if self.hasReportNumbersBySystem(system):
1993 return self._reports[system]
1994 return None
1996 def getReportNumberKeys(self):
1997 return self._reports.keys()
1999 def listReportNumbersOnKey(self, key):
2000 reports=[]
2001 if key in self._reports.keys():
2002 # compatibility with previous versions
2003 if type(self._reports[key]) is str:
2004 self._reports[key] = [ self._reports[key] ]
2005 for number in self._reports[key]:
2006 reports.append([key, number])
2007 return reports
2009 def hasReportNumberOnSystem(self, system, number):
2010 if self.hasReportNumbersBySystem(system):
2011 if number in self._reports[system]:
2012 return True
2013 return False
2015 def listReportNumbers(self):
2016 reports=[]
2017 keys = self._reports.keys()
2018 keys.sort()
2019 for key in keys:
2020 # compatibility with previous versions
2021 if type(self._reports[key]) is str:
2022 self._reports[key] = [ self._reports[key] ]
2023 for number in self._reports[key]:
2024 reports.append([key, number])
2025 return reports
2027 def clone(self, owner):
2028 newR=ReportNumberHolder(owner)
2029 for key in self._reports.keys():
2030 for number in self._reports[key]:
2031 newR.addReportNumber(key, number)
2032 return newR
2034 def notifyModification(self):
2035 self._p_changed=1
2036 if self.getOwner() != None:
2037 self.getOwner().notifyModification()
2040 class Conference(CommonObjectBase, Locatable):
2041 """This class represents the real world conferences themselves. Objects of
2042 this class will contain basic data about the confence and will provide
2043 access to other objects representing certain parts of the conferences
2044 (ex: contributions, sessions, ...).
2047 fossilizes(IConferenceFossil, IConferenceMinimalFossil, IConferenceEventInfoFossil)
2049 def __init__(self, creator, id="", creationDate = None, modificationDate = None):
2050 """Class constructor. Initialise the class attributes to the default
2051 values.
2052 Params:
2053 confData -- (Dict) Contains the data the conference object has to
2054 be initialised to.
2056 #IndexedObject.__init__(self)
2057 if creator == None:
2058 raise MaKaCError( _("A creator must be specified when creating a new Event"), _("Event"))
2059 self.__creator = creator
2060 self.id = id
2061 self.title = ""
2062 self.description = ""
2063 self.places = []
2064 self.rooms = []
2065 ###################################
2066 # Fermi timezone awareness #
2067 ###################################
2068 self.startDate = nowutc()
2069 self.endDate = nowutc()
2070 self.timezone = ""
2071 ###################################
2072 # Fermi timezone awareness(end) #
2073 ###################################
2074 self._screenStartDate = None
2075 self._screenEndDate = None
2076 self.contactInfo =""
2077 self.chairmanText = ""
2078 self.chairmans = []
2079 self._chairGen=Counter()
2080 self._chairs=[]
2081 self.sessions = {}
2082 self.__sessionGenerator = Counter() # Provides session unique
2083 # identifiers for this conference
2084 self.contributions = {}
2085 self.__contribGenerator = Counter() # Provides contribution unique
2086 # identifiers for this conference
2087 self.programDescription = ""
2088 self.program = []
2089 self.__programGenerator = Counter() # Provides track unique
2090 # identifiers for this conference
2091 self.__ac = AccessController(self)
2092 self.materials = {}
2093 self.__materialGenerator = Counter() # Provides material unique
2094 # identifiers for this conference
2095 self.paper = None
2096 self.slides = None
2097 self.video = None
2098 self.poster = None
2099 self.minutes=None
2100 self.__schedule=None
2101 self.__owners = []
2102 if creationDate:
2103 self._creationDS = creationDate
2104 else:
2105 self._creationDS = nowutc() #creation timestamp
2106 if modificationDate:
2107 self._modificationDS = modificationDate
2108 else:
2109 self._modificationDS = nowutc() #modification timestamp
2111 self.alarmList = {}
2112 self.__alarmCounter = Counter()
2114 self.abstractMgr = review.AbstractMgr(self)
2115 self._logo = None
2116 self._trackCoordinators = TCIndex() #index for the track coordinators
2117 self._supportInfo = SupportInfo(self, "Support")
2118 self._contribTypes = {}
2119 self.___contribTypeGenerator = Counter()
2120 self._authorIdx=AuthorIndex()
2121 self._speakerIdx=AuthorIndex()
2122 self._primAuthIdx=_PrimAuthIdx(self)
2123 self._sessionCoordinators=SCIndex()
2124 self._sessionCoordinatorRights = []
2125 self._submitterIdx=SubmitterIndex()
2126 self._boa=BOAConfig(self)
2127 self._registrationForm = registration.RegistrationForm(self)
2128 self._evaluationCounter = Counter()
2129 self._evaluations = [Evaluation(self)]
2130 self._registrants = {} #key=registrantId; value=Registrant
2131 self._bookings = {}
2132 self._registrantGenerator = Counter()
2133 self._accessKey=""
2134 self._modifKey=""
2135 self._closed = False
2136 self._visibility = 999
2137 self._pendingQueuesMgr=pendingQueues.ConfPendingQueuesMgr(self)
2138 self._sections = []
2139 self._participation = Participation(self)
2140 self._logHandler = LogHandler()
2141 self._reportNumberHolder=ReportNumberHolder(self)
2142 self._enableSessionSlots = False
2143 self._enableSessions = False
2144 self._autoSolveConflict = True
2145 self.__badgeTemplateManager = BadgeTemplateManager(self)
2146 self.__posterTemplateManager = PosterTemplateManager(self)
2147 self._keywords = ""
2148 self._confPaperReview = ConferencePaperReview(self)
2149 self._confAbstractReview = ConferenceAbstractReview(self)
2150 self._orgText = ""
2151 self._comments = ""
2152 self._sortUrlTag = ""
2154 self._observers = []
2156 @return_ascii
2157 def __repr__(self):
2158 return '<Conference({0}, {1}, {2})'.format(self.getId(), self.getTitle(), self.getStartDate())
2160 @property
2161 def has_legacy_id(self):
2162 """Returns True if the event has a broken legacy ID.
2164 These IDs are not compatible with new code since they are not
2165 numeric or have a leading zero, resulting in different events
2166 with the same numeric event id.
2168 return is_legacy_id(self.id)
2170 @property
2171 def all_manager_emails(self):
2172 """Returns the emails of all managers, including the creator"""
2173 emails = {self.getCreator().getEmail()} | {u.getEmail() for u in self.getManagerList()}
2174 return {e for e in emails if e}
2176 @staticmethod
2177 def _cmpByDate(self, toCmp):
2178 res = cmp(self.getStartDate(), toCmp.getStartDate())
2179 if res != 0:
2180 return res
2181 else:
2182 return cmp(self, toCmp)
2184 def __cmp__(self, toCmp):
2185 if isinstance(toCmp, Conference):
2186 return cmp(self.getId(), toCmp.getId())
2187 else:
2188 return cmp(hash(self), hash(toCmp))
2190 def __eq__(self, toCmp):
2191 return self is toCmp
2193 def __ne__(self, toCmp):
2194 return not(self is toCmp)
2196 def setUrlTag(self, tag):
2197 self._sortUrlTag = tag
2199 def getUrlTag(self):
2200 try:
2201 return self._sortUrlTag
2202 except:
2203 self._sortUrlTag = ""
2204 return self._sortUrlTag
2206 def setComments(self,comm=""):
2207 self._comments = comm.strip()
2209 def getComments(self):
2210 try:
2211 if self._comments:
2212 pass
2213 except AttributeError,e:
2214 self.setComments()
2215 return self._comments
2217 def getConfPaperReview(self):
2218 if not hasattr(self, "_confPaperReview"):
2219 self._confPaperReview = ConferencePaperReview(self)
2220 return self._confPaperReview
2222 def getConfAbstractReview(self):
2223 if not hasattr(self, "_confAbstractReview"):
2224 self._confAbstractReview = ConferenceAbstractReview(self)
2225 return self._confAbstractReview
2227 def getOrgText( self ):
2228 try:
2229 return self._orgText
2230 except:
2231 self.setOrgText()
2232 return ""
2234 def setOrgText( self, org="" ):
2235 self._orgText = org
2237 def cleanCache( self ):
2238 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
2239 ScheduleToJson.cleanConferenceCache(self)
2240 ContextManager.set('clean%s'%self.getUniqueId(), True)
2242 def updateNonInheritingChildren(self, elem, delete=False):
2243 self.getAccessController().updateNonInheritingChildren(elem, delete)
2245 def getKeywords(self):
2246 try:
2247 return self._keywords
2248 except:
2249 self._keywords = ""
2250 return ""
2252 def setKeywords(self, keywords):
2253 self._keywords = keywords
2255 # Room booking related ===================================================
2257 def getRoomBookingList(self):
2258 """Returns list of bookings for this conference."""
2259 # In case anyone wonders why this method is still here: Various fossils expect/use it.
2260 if not self.getId().isdigit():
2261 return []
2262 return Reservation.find_all(event_id=int(self.getId()))
2264 # ========================================================================
2266 def getParticipation(self):
2267 try :
2268 if self._participation :
2269 pass
2270 except AttributeError :
2271 self._participation = Participation(self)
2272 return self._participation
2274 def getType( self ):
2275 import MaKaC.webinterface.webFactoryRegistry as webFactoryRegistry
2276 wr = webFactoryRegistry.WebFactoryRegistry()
2277 wf = wr.getFactory(self)
2278 if wf != None:
2279 type = wf.getId()
2280 else:
2281 type = "conference"
2282 return type
2284 def getVerboseType( self ):
2285 # Like getType, but returns "Lecture" instead of "simple_type"
2286 type = self.getType()
2287 if type == "simple_event":
2288 type = "lecture"
2289 return type.capitalize()
2292 def getLogHandler(self):
2293 try :
2294 if self._logHandler:
2295 pass
2296 except AttributeError :
2297 self._logHandler = LogHandler()
2298 return self._logHandler
2301 def getEnableSessionSlots(self):
2302 #try :
2303 # if self._enableSessionSlots :
2304 # pass
2305 #except AttributeError :
2306 # self._enableSessionSlots = True
2307 #if self.getType() == "conference":
2308 # return True
2309 #return self._enableSessionSlots
2310 return True
2312 def getEnableSessions(self):
2313 try :
2314 if self._enableSessions :
2315 pass
2316 except AttributeError :
2317 self._enableSessions = True
2318 if self.getType() == "conference":
2319 return True
2320 return self._enableSessions
2322 def enableSessionSlots(self):
2323 self._enableSessionSlots = True
2325 def disableSessionSlots(self):
2326 self._enableSessionSlots = False
2328 def enableSessions(self):
2329 self._enableSessions = True
2331 def disableSessions(self):
2332 self._enableSessions = False
2334 def setValues(self, confData):
2336 Sets SOME values of the current conference object from a dictionary
2337 containing the following key-value pairs:
2338 visibility-(str)
2339 title-(str)
2340 description-(str)
2341 supportEmail-(str)
2342 contactInfo-(str)
2343 locationName-(str) => name of the location, if not specified
2344 it will be set to the conference location name.
2345 locationAddress-(str)
2346 roomName-(str) => name of the room, if not specified it will
2347 be set to the conference room name.
2348 Please, note that this method sets SOME values which means that if
2349 needed it can be completed to set more values. Also note that if
2350 the given dictionary doesn't contain all the values, the missing
2351 ones will be set to the default values.
2353 self.setVisibility(confData.get("visibility", "999"))
2354 self.setTitle(confData.get("title", _("NO TITLE ASSIGNED")))
2355 self.setDescription(confData.get("description", ""))
2356 self.getSupportInfo().setEmail(confData.get("supportEmail", ""))
2357 self.setContactInfo(confData.get("contactInfo", ""))
2358 if confData.get("locationName", "").strip() == "":
2359 self.setLocation(None)
2360 else:
2361 #if the location name is defined we must set a new location (or
2362 # modify the existing one) for the conference
2363 loc = self.getLocation()
2364 if not loc:
2365 loc = CustomLocation()
2366 self.setLocation(loc)
2367 loc.setName(confData["locationName"])
2368 loc.setAddress(confData.get("locationAddress", ""))
2369 #same as for the location
2370 if confData.get("roomName", "").strip() == "":
2371 self.setRoom(None)
2372 else:
2373 room = self.getRoom()
2374 if not room:
2375 room = CustomRoom()
2376 self.setRoom(room)
2377 room.setName(confData["roomName"])
2378 self.notifyModification()
2380 def getVisibility ( self ):
2381 try:
2382 return int(self._visibility)
2383 except:
2384 self._visibility = 999
2385 return 999
2387 def getFullVisibility( self ):
2388 return max(0,min(self.getVisibility(), self.getOwnerList()[0].getVisibility()))
2390 def setVisibility( self, visibility=999 ):
2391 self._visibility = int(visibility)
2392 catIdx = indexes.IndexesHolder().getIndex('category')
2393 catIdx.reindexConf(self)
2394 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2395 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2396 catDateIdx.reindexConf(self)
2397 catDateAllIdx.reindexConf(self)
2399 def isClosed( self ):
2400 try:
2401 return self._closed
2402 except:
2403 self._closed = False
2404 return False
2406 def setClosed( self, closed=True ):
2407 self._closed = closed
2409 def indexConf( self ):
2410 # called when event dates change
2411 # see also Category.indexConf()
2413 calIdx = indexes.IndexesHolder().getIndex('calendar')
2414 calIdx.indexConf(self)
2415 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2416 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2417 catDateIdx.indexConf(self)
2418 catDateAllIdx.indexConf(self)
2419 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
2420 nameIdx.index(self)
2422 Catalog.getIdx('categ_conf_sd').index_obj(self)
2424 def unindexConf( self ):
2425 calIdx = indexes.IndexesHolder().getIndex('calendar')
2426 calIdx.unindexConf(self)
2427 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2428 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2429 catDateIdx.unindexConf(self)
2430 catDateAllIdx.unindexConf(self)
2431 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
2432 nameIdx.unindex(self)
2434 Catalog.getIdx('categ_conf_sd').unindex_obj(self)
2436 def __generateNewContribTypeId( self ):
2437 """Returns a new unique identifier for the current conference sessions
2439 try:
2440 return str(self.___contribTypeGenerator.newCount())
2441 except:
2442 self.___contribTypeGenerator = Counter()
2443 return str(self.___contribTypeGenerator.newCount())
2445 def addContribType(self, ct):
2446 try:
2447 if self._contribTypes:
2448 pass
2449 except:
2450 self._contribTypes = {}
2451 if ct in self._contribTypes.values():
2452 return
2453 id = ct.getId()
2454 if id == "":
2455 id = self.__generateNewContribTypeId()
2456 ct.setId(id)
2457 self._contribTypes[id] = ct
2458 self.notifyModification()
2460 def newContribType(self, name, description):
2461 ct = ContributionType(name, description, self)
2462 self.addContribType(ct)
2463 return ct
2465 def getContribTypeList(self):
2466 try:
2467 return self._contribTypes.values()
2468 except:
2469 self._contribTypes = {}
2470 self.notifyModification()
2471 return self._contribTypes.values()
2473 def getContribTypeById(self, id):
2474 try:
2475 if self._contribTypes:
2476 pass
2477 except:
2478 self._contribTypes = {}
2479 self.notifyModification()
2480 if id in self._contribTypes.keys():
2481 return self._contribTypes[id]
2482 return None
2484 def removeContribType(self, ct):
2485 try:
2486 if self._contribTypes:
2487 pass
2488 except:
2489 self._contribTypes = {}
2490 if not ct in self._contribTypes.values():
2491 return
2492 del self._contribTypes[ct.getId()]
2493 for cont in self.getContributionList():
2494 if cont.getType() == ct:
2495 cont.setType(None)
2496 ct.delete()
2497 self.notifyModification()
2499 def recoverContribType(self, ct):
2500 ct.setConference(self)
2501 self.addContribType(ct)
2502 ct.recover()
2504 def _getRepository( self ):
2505 dbRoot = DBMgr.getInstance().getDBConnection().root()
2506 try:
2507 fr = dbRoot["local_repositories"]["main"]
2508 except KeyError, e:
2509 fr = fileRepository.MaterialLocalRepository()
2510 dbRoot["local_repositories"] = OOBTree()
2511 dbRoot["local_repositories"]["main"] = fr
2512 return fr
2514 def removeResource( self, res ):
2515 pass
2517 def getURL(self):
2518 cid = self.getUrlTag()
2519 if not cid:
2520 cid = self.getId()
2521 return Config.getInstance().getShortEventURL() + cid
2523 def setLogo( self, logoFile ):
2524 logoFile.setOwner( self )
2525 logoFile.setId( "logo" )
2526 logoFile.archive( self._getRepository() )
2527 if self._logo != None:
2528 self._logo.delete()
2529 self._logo = logoFile
2530 self.notifyModification()
2532 def getLogo( self ):
2533 return self._logo
2535 def getLogoURL( self ):
2536 try:
2537 if self._logo == None:
2538 return ""
2539 return self._logo.getURL()
2540 except AttributeError:
2541 self._logo = None
2542 return ""
2544 def removeLogo(self):
2545 if self._logo is None:
2546 return
2547 self._logo.delete()
2548 self._logo = None
2549 self.notifyModification()
2551 def recoverLogo(self, logo):
2552 logo.setOwner(self)
2553 if self._logo != None:
2554 self._logo.delete()
2555 self._logo = logo
2556 logo.recover()
2557 self.notifyModification()
2559 def getSession(self):
2560 return None
2562 def getContribution(self):
2563 return None
2565 def getSubContribution(self):
2566 return None
2568 def getAbstractMgr(self):
2569 return self.abstractMgr
2571 def notifyModification( self, date = None, raiseEvent = True):
2572 """Method called to notify the current conference has been modified.
2574 self.setModificationDate()
2576 if raiseEvent:
2577 signals.event.data_changed.send(self, attr=None, old=None, new=None)
2579 self.cleanCache()
2580 self._p_changed=1
2582 def getModificationDate( self ):
2583 """Returns the date in which the conference was last modified"""
2584 return self._modificationDS
2586 def getAdjustedModificationDate( self, tz ):
2587 """Returns the date in which the conference was last modified"""
2588 return self._modificationDS.astimezone(timezone(tz))
2590 def getCreationDate( self ):
2591 """Returns the date in which the conference was created"""
2592 return self._creationDS
2594 def getAdjustedCreationDate( self, tz ):
2595 """Returns the date in which the conference was created"""
2596 return self._creationDS.astimezone(timezone(tz))
2598 def getCreator( self ):
2599 return self.__creator
2601 def setCreator( self, creator):
2602 if self.__creator:
2603 self.__creator.unlinkTo(self, "creator")
2604 creator.linkTo(self, "creator")
2605 self.__creator = creator
2607 def linkCreator(self):
2608 self.__creator.linkTo(self, "creator")
2610 def getId( self ):
2611 """returns (string) the unique identifier of the conference"""
2612 return self.id
2614 def getUniqueId( self ):
2615 """returns (string) the unique identiffier of the item"""
2616 """used mainly in the web session access key table"""
2617 return "a%s" % self.id
2619 def setId(self, newId):
2620 """changes the current unique identifier of the conference to the
2621 one which is specified"""
2622 self.id = str(newId)
2624 def getLocator( self ):
2625 """Gives back (Locator) a globaly unique identification encapsulated in
2626 a Locator object for the conference instance """
2627 d = Locator()
2628 d["confId"] = self.getId()
2629 return d
2631 def getOwner( self ):
2632 if self.getOwnerList() == []:
2633 return None
2634 return self.getOwnerList()[0]
2636 def getOwnerList( self ):
2637 return self.__owners
2639 def getOwnerPath( self ):
2640 l=[]
2641 owner = self.getOwnerList()[0]
2642 while owner != None and owner.getId() != "0":
2643 l.append(owner)
2644 owner = owner.getOwner()
2645 return l
2647 def getOwnerById( self, key ):
2648 """Returns one specific category which contains the conference.
2649 Params:
2650 - key: The "id" of the category.
2652 for owner in self.__owners:
2653 if key == owner.getId():
2654 return owner
2655 return None
2657 def addOwner( self, newOwner ):
2658 if newOwner == None:
2659 return
2660 self.__owners.append( newOwner )
2661 self.notifyModification()
2663 def removeOwner( self, owner, notify=True ):
2664 if not (owner in self.__owners):
2665 return
2666 self.__owners.remove( owner )
2667 owner.removeConference( self )
2668 if notify:
2669 self.notifyModification()
2671 def getCategoriesPath(self):
2672 return [self.getOwnerList()[0].getCategoryPath()]
2674 def notifyContributions(self):
2676 for c in self.getContributionList():
2677 # take care of subcontributions
2678 for sc in c.getSubContributionList():
2679 signals.event.subcontribution_deleted.send(sc, parent=c)
2681 signals.event.contribution_deleted.send(c, parent=self)
2683 def delete(self, user=None):
2684 """deletes the conference from the system.
2686 signals.event.deleted.send(self, user=user)
2688 self.notifyContributions()
2690 # will have to remove it from all the owners (categories) and the
2691 # conference registry
2692 ConferenceHolder().remove(self)
2693 for owner in self.__owners:
2694 owner.removeConference(self, notify=False)
2696 self.removeAllEvaluations()
2698 for alarm in self.getAlarmList():
2699 if not alarm.getEndedOn():
2700 self.removeAlarm(alarm)
2702 # For each conference we have a list of managers. If we delete the conference but we don't delete
2703 # the link in every manager to the conference then, when the manager goes to his "My profile" he
2704 # will see a link to a conference that doesn't exist. Therefore, we need to delete that link as well
2705 for manager in self.getManagerList():
2706 if isinstance(manager, AvatarUserWrapper):
2707 manager.unlinkTo(self, "manager")
2709 creator = self.getCreator()
2710 creator.unlinkTo(self, "creator")
2712 # Remove all links in redis
2713 if redis_write_client:
2714 avatar_links.delete_event(self)
2716 # Remote short URL mappings
2717 ShortURLMapper().remove(self)
2719 TrashCanManager().add(self)
2721 def getConference( self ):
2722 return self
2724 def getObservers(self):
2725 if not hasattr(self, "_observers"):
2726 self._observers = []
2727 return self._observers
2729 def setDates( self, sDate, eDate=None, check=1, moveEntries=0):
2731 Set the start/end date for a conference
2734 oldStartDate = self.getStartDate()
2735 oldEndDate = self.getEndDate()
2737 # do some checks first
2738 if sDate > eDate:
2739 # obvious case
2740 raise FormValuesError(_("Start date cannot be after the end date"), _("Event"))
2742 elif sDate == oldStartDate and eDate == oldEndDate:
2743 # if there's nothing to do (yet another obvious case)
2744 return
2746 # if we reached this point, it means either the start or
2747 # the end date (or both) changed
2748 # If only the end date was changed, moveEntries = 0
2749 if sDate == oldStartDate:
2750 moveEntries = 0
2752 # Pre-check for moveEntries
2753 if moveEntries == 1:
2754 # in case the entries are to be simply shifted
2755 # we should make sure the interval is big enough
2756 # just store the old values for later
2758 oldInterval = oldEndDate - oldStartDate
2759 newInterval = eDate - sDate
2761 entries = self.getSchedule().getEntries()
2762 if oldInterval > newInterval and entries:
2763 eventInterval = entries[-1].getEndDate() - entries[0].getStartDate()
2764 diff = entries[0].getStartDate() - oldStartDate
2765 if sDate + diff + eventInterval > eDate:
2766 raise TimingError(
2767 _("The start/end dates were not changed since the selected "
2768 "timespan is not large enough to accomodate the contained "
2769 "timetable entries and spacings."),
2770 explanation=_("You should try using a larger timespan."))
2772 # so, we really need to try changing something
2774 self.unindexConf()
2776 # set the dates
2777 self.setStartDate(sDate, check=0, moveEntries = moveEntries, index=False, notifyObservers = False)
2778 self.setEndDate(eDate, check=0, index=False, notifyObservers = False)
2780 # sanity check
2781 self._checkInnerSchedule()
2783 # reindex the conference
2784 self.indexConf()
2786 # notify observers
2787 old_data = (oldStartDate, oldEndDate)
2788 new_data = (self.getStartDate(), self.getEndDate())
2789 if old_data != new_data:
2790 signals.event.data_changed.send(self, attr='dates', old=old_data, new=new_data)
2792 def _checkInnerSchedule( self ):
2793 self.getSchedule().checkSanity()
2795 def setStartDate(self, sDate, check = 1, moveEntries = 0, index = True, notifyObservers = True):
2796 """ Changes the current conference starting date/time to the one specified by the parameters.
2798 if not sDate.tzname():
2799 raise MaKaCError("date should be timezone aware")
2800 if sDate == self.getStartDate():
2801 return
2802 ###################################
2803 # Fermi timezone awareness #
2804 ###################################
2805 if not indexes.BTREE_MIN_UTC_DATE <= sDate <= indexes.BTREE_MAX_UTC_DATE:
2806 raise FormValuesError(_("The start date must be between {} and {}.").format(
2807 format_datetime(indexes.BTREE_MIN_UTC_DATE),
2808 format_datetime(indexes.BTREE_MAX_UTC_DATE)))
2809 ###################################
2810 # Fermi timezone awareness #
2811 ###################################
2812 if check != 0:
2813 self.verifyStartDate(sDate)
2814 oldSdate = self.getStartDate()
2815 diff = sDate - oldSdate
2817 if index:
2818 self.unindexConf()
2819 self.startDate = sDate
2820 if moveEntries and diff is not None:
2821 # If the start date changed, we move entries inside the timetable
2822 self.getSchedule()._startDate=None
2823 self.getSchedule()._endDate=None
2824 #if oldSdate.date() != sDate.date():
2825 # entries = self.getSchedule().getEntries()[:]
2826 #else:
2827 # entries = self.getSchedule().getEntriesOnDay(sDate.astimezone(timezone(self.getTimezone())))[:]
2828 entries = self.getSchedule().getEntries()[:]
2829 self.getSchedule().moveEntriesBelow(diff, entries, check=check)
2830 #datetime object is non-mutable so we must "force" the modification
2831 # otherwise ZODB won't be able to notice the change
2832 self.notifyModification()
2833 if index:
2834 self.indexConf()
2836 # update the time for the alarms to be sent
2837 self._updateAlarms()
2839 # Update redis link timestamp
2840 if redis_write_client:
2841 avatar_links.update_event_time(self)
2843 #if everything went well, we notify the observers that the start date has changed
2844 if notifyObservers:
2845 if oldSdate != sDate:
2846 signals.event.data_changed.send(self, attr='start_date', old=oldSdate, new=sDate)
2848 def _updateAlarms(self):
2849 c = Client()
2850 # are there any alarms? if so, update the relative ones
2851 for alarm in self.getAlarmList():
2852 tbef = alarm.getTimeBefore()
2853 if tbef:
2854 # only relative alarms
2855 c.moveTask(alarm, self.getStartDate() - tbef)
2857 def verifyStartDate(self, sdate, check=1):
2858 if sdate>self.getEndDate():
2859 raise MaKaCError( _("End date cannot be before the Start date"), _("Event"))
2861 def setStartTime(self, hours=0, minutes=0, notifyObservers = True):
2862 """ Changes the current conference starting time (not date) to the one specified by the parameters.
2865 sdate = self.getStartDate()
2866 self.startDate = datetime( sdate.year, sdate.month, sdate.day,
2867 int(hours), int(minutes) )
2868 self.verifyStartDate(self.startDate)
2869 self.notifyModification()
2871 def getStartDate(self):
2872 """returns (datetime) the starting date of the conference"""
2873 return self.startDate
2875 def getUnixStartDate(self):
2876 return datetimeToUnixTimeInt(self.startDate)
2878 ###################################
2879 # Fermi timezone awareness #
2880 ###################################
2882 def getAdjustedStartDate(self,tz=None):
2883 if not tz:
2884 tz = self.getTimezone()
2885 if tz not in all_timezones:
2886 tz = 'UTC'
2887 return self.getStartDate().astimezone(timezone(tz))
2889 ###################################
2890 # Fermi timezone awareness(end) #
2891 ###################################
2893 def setScreenStartDate(self, date):
2894 if date == self.getStartDate():
2895 date = None
2896 self._screenStartDate = date
2897 self.notifyModification()
2899 def getScreenStartDate(self):
2900 try:
2901 date = self._screenStartDate
2902 except:
2903 date = self._screenStartDate = None
2904 if date != None:
2905 return date
2906 else:
2907 return self.getStartDate()
2909 def getAdjustedScreenStartDate(self, tz=None):
2910 if not tz:
2911 tz = self.getTimezone()
2912 return self.getScreenStartDate().astimezone(timezone(tz))
2914 def calculateDayStartTime(self, day):
2915 """returns (date) the start date of the conference on a given day
2916 day is a tz aware datetime"""
2917 if self.getStartDate().astimezone(day.tzinfo).date() == day.date():
2918 return self.getStartDate().astimezone(day.tzinfo)
2919 return self.getSchedule().calculateDayStartDate(day)
2921 def verifyEndDate(self, edate):
2922 if edate<self.getStartDate():
2923 raise TimingError( _("End date cannot be before the start date"), _("Event"))
2924 if self.getSchedule().hasEntriesAfter(edate):
2925 raise TimingError(_("Cannot change end date to %s: some entries in the timetable would be outside this date (%s)") % (edate,self.getSchedule().getEntries()[-1].getStartDate()), _("Event"))
2927 def setEndDate(self, eDate, check = 1, index = True, notifyObservers = True):
2928 """ Changes the current conference end date/time to the one specified by the parameters.
2930 if not eDate.tzname():
2931 raise MaKaCError("date should be timezone aware")
2932 if eDate == self.getEndDate():
2933 return
2934 if not indexes.BTREE_MIN_UTC_DATE <= eDate <= indexes.BTREE_MAX_UTC_DATE:
2935 raise FormValuesError(_("The end date must be between {} and {}.").format(
2936 format_datetime(indexes.BTREE_MIN_UTC_DATE),
2937 format_datetime(indexes.BTREE_MAX_UTC_DATE)))
2938 if check != 0:
2939 self.verifyEndDate(eDate)
2940 if index:
2941 self.unindexConf()
2943 oldEdate = self.endDate
2944 self.endDate = eDate
2945 #datetime object is non-mutable so we must "force" the modification
2946 # otherwise ZODB won't be able to notice the change
2947 self.notifyModification()
2948 if index:
2949 self.indexConf()
2951 #if everything went well, we notify the observers that the start date has changed
2952 if notifyObservers:
2953 if oldEdate != eDate:
2954 signals.event.data_changed.send(self, attr='end_date', old=oldEdate, new=eDate)
2956 def setEndTime(self, hours = 0, minutes = 0, notifyObservers = True):
2957 """ Changes the current conference end time (not date) to the one specified by the parameters.
2959 edate = self.getEndDate()
2960 self.endDate = datetime( edate.year, edate.month, edate.day, int(hours), int(minutes) )
2961 self.verifyEndDate(self.endDate)
2962 self.notifyModification()
2965 def getEndDate(self):
2966 """returns (datetime) the ending date of the conference"""
2967 return self.endDate
2969 ##################################
2970 # Fermi timezone awareness #
2971 ##################################
2973 def getAdjustedEndDate(self,tz=None):
2974 if not tz:
2975 tz = self.getTimezone()
2976 if tz not in all_timezones:
2977 tz = 'UTC'
2978 return self.getEndDate().astimezone(timezone(tz))
2980 ##################################
2981 # Fermi timezone awareness(end) #
2982 ##################################
2984 def setScreenEndDate(self, date):
2985 if date == self.getEndDate():
2986 date = None
2987 self._screenEndDate = date
2988 self.notifyModification()
2990 def getScreenEndDate(self):
2991 try:
2992 date = self._screenEndDate
2993 except:
2994 date = self._screenEndDate = None
2995 if date != None:
2996 return date
2997 else:
2998 return self.getEndDate()
3000 def getAdjustedScreenEndDate(self, tz=None):
3001 if not tz:
3002 tz = self.getTimezone()
3003 return self.getScreenEndDate().astimezone(timezone(tz))
3005 def isEndDateAutoCal( self ):
3006 """Says whether the end date has been explicitely set for the session
3007 or it must be calculated automatically
3009 return self._endDateAutoCal
3011 ####################################
3012 # Fermi timezone awareness #
3013 ####################################
3014 def setTimezone(self, tz):
3015 try:
3016 oldTimezone = self.timezone
3017 except AttributeError:
3018 oldTimezone = tz
3019 self.timezone = tz
3021 def getTimezone(self):
3022 try:
3023 return self.timezone
3024 except:
3025 return 'UTC'
3027 def moveToTimezone(self, tz):
3028 if self.getTimezone() == tz:
3029 return
3030 sd=self.getAdjustedStartDate()
3031 ed=self.getAdjustedEndDate()
3032 self.setTimezone(tz)
3033 try:
3034 sDate = timezone(tz).localize(datetime(sd.year, \
3035 sd.month, \
3036 sd.day, \
3037 sd.hour, \
3038 sd.minute))
3039 eDate = timezone(tz).localize(datetime(ed.year, \
3040 ed.month, \
3041 ed.day, \
3042 ed.hour, \
3043 ed.minute))
3044 except ValueError,e:
3045 raise MaKaCError("Error moving the timezone: %s"%e)
3046 self.setDates( sDate.astimezone(timezone('UTC')), \
3047 eDate.astimezone(timezone('UTC')),
3048 moveEntries=1)
3052 ####################################
3053 # Fermi timezone awareness(end) #
3054 ####################################
3056 def getTitle(self):
3057 """returns (String) the title of the conference"""
3058 return self.title
3060 def setTitle(self, title):
3061 """changes the current title of the conference to the one specified"""
3062 oldTitle = self.title
3064 self.title = title
3065 self.notifyModification()
3067 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
3068 nameIdx.unindex(self)
3069 nameIdx.index(self)
3071 if oldTitle != title:
3072 signals.event.data_changed.send(self, attr='title', old=oldTitle, new=title)
3074 def getDescription(self):
3075 """returns (String) the description of the conference"""
3076 return self.description
3078 def setDescription(self, desc):
3079 """changes the current description of the conference"""
3080 oldDescription = self.description
3081 self.description = desc
3082 if oldDescription != desc:
3083 signals.event.data_changed.send(self, attr='description', old=oldDescription, new=desc)
3084 self.notifyModification()
3086 def getSupportInfo(self):
3087 if not hasattr(self, "_supportInfo"):
3088 self._supportInfo = SupportInfo(self, "Support")
3089 return self._supportInfo
3091 def setSupportInfo(self, supportInfo):
3092 self._supportInfo = supportInfo
3094 def getChairmanText( self ):
3095 try:
3096 if self.chairmanText:
3097 pass
3098 except AttributeError, e:
3099 self.chairmanText = ""
3100 return self.chairmanText
3102 def setChairmanText( self, newText ):
3103 self.chairmanText = newText.strip()
3105 def appendChairmanText( self, newText ):
3106 self.setChairmanText( "%s, %s"%(self.getChairmanText(), newText.strip()) )
3107 self._chairGen=Counter()
3108 self._chairs=[]
3110 def _resetChairs(self):
3111 try:
3112 if self._chairs:
3113 return
3114 except AttributeError:
3115 self._chairs=[]
3116 for oc in self.chairmans:
3117 newChair=ConferenceChair()
3118 newChair.setDataFromAvatar(oc)
3119 self._addChair(newChair)
3121 def getChairList(self):
3122 """Method returning a list of the conference chairmans (Avatars)
3124 self._resetChairs()
3125 return self._chairs
3127 def _addChair(self,newChair):
3128 for chair in self._chairs:
3129 if newChair.getEmail() != "" and newChair.getEmail() == chair.getEmail():
3130 return
3131 try:
3132 if self._chairGen:
3133 pass
3134 except AttributeError:
3135 self._chairGen=Counter()
3136 id = newChair.getId()
3137 if id == "":
3138 id=int(self._chairGen.newCount())
3139 if isinstance(newChair,ConferenceChair):
3140 newChair.includeInConference(self,id)
3141 self._chairs.append(newChair)
3142 if isinstance(newChair, AvatarUserWrapper):
3143 newChair.linkTo(self, "chair")
3144 self.notifyModification()
3146 def addChair(self,newChair):
3147 """includes the specified user in the list of conference
3148 chairs"""
3149 self._resetChairs()
3150 self._addChair(newChair)
3152 def removeChair(self,chair):
3153 """removes the specified user from the list of conference
3154 chairs"""
3155 self._resetChairs()
3156 if chair not in self._chairs:
3157 return
3158 self._chairs.remove(chair)
3159 if isinstance(chair, AvatarUserWrapper):
3160 chair.unlinkTo(self, "chair")
3161 chair.delete()
3162 self.notifyModification()
3164 def recoverChair(self, ch):
3165 self.addChair(ch)
3166 ch.recover()
3168 def getChairById(self,id):
3169 id=int(id)
3170 for chair in self._chairs:
3171 if chair.getId()==id:
3172 return chair
3173 return None
3175 def getAllSessionsConvenerList(self) :
3176 dictionary = {}
3177 for session in self.getSessionList() :
3178 for convener in session.getConvenerList() :
3179 key = convener.getEmail()+" "+convener.getFirstName().lower()+" "+convener.getFamilyName().lower()
3180 dictionary.setdefault(key, set()).add(convener)
3181 for slot in session.getSlotList():
3182 for convener in slot.getConvenerList() :
3183 key = convener.getEmail()+" "+convener.getFirstName().lower()+" "+convener.getFamilyName().lower()
3184 dictionary.setdefault(key, set()).add(convener)
3186 return dictionary
3188 def getContactInfo(self):
3189 return self.contactInfo
3191 def setContactInfo(self, contactInfo):
3192 self.contactInfo = contactInfo
3193 self.notifyModification()
3195 def getLocationParent( self ):
3197 Returns the object from which the room/location
3198 information should be inherited.
3199 For Conferences, it's None, since they can't inherit
3200 from anywhere else.
3202 return None
3204 def getLocation( self ):
3205 return self.getOwnLocation()
3207 def getAddress( self ):
3208 if self.getOwnLocation():
3209 return self.getOwnLocation().getAddress()
3210 else:
3211 return None
3213 def getRoom( self ):
3214 return self.getOwnRoom()
3216 def getLocationList(self):
3217 """Method returning a list of "location" objects which contain the
3218 information about the different places the conference is gonna
3219 happen
3221 return self.places
3223 def getFavoriteRooms(self):
3224 roomList = []
3225 roomList.extend(self.getRoomList())
3226 #roomList.extend(map(lambda x: x._getName(), self.getBookedRooms()))
3228 return roomList
3230 def addLocation(self, newPlace):
3231 self.places.append( newPlace )
3232 self.notifyModification()
3234 def setAccessKey(self, accessKey=""):
3235 """sets the access key of the conference"""
3236 self._accessKey = accessKey
3237 self.notifyModification()
3239 def getAccessKey(self):
3240 try:
3241 return self._accessKey
3242 except AttributeError:
3243 self._accessKey = ""
3244 return self._accessKey
3246 def setModifKey(self, modifKey=""):
3247 """sets the modification key of the conference"""
3248 self._modifKey = modifKey
3249 self.notifyModification()
3251 def getModifKey(self):
3252 try:
3253 return self._modifKey
3254 except AttributeError:
3255 self._modifKey = ""
3256 return self._modifKey
3258 def __generateNewSessionId( self ):
3259 """Returns a new unique identifier for the current conference sessions
3261 return str(self.__sessionGenerator.newCount())
3263 def addSession(self,newSession, check = 2, id = None):
3264 """Adds a new session object to the conference taking care of assigning
3265 a new unique id to it
3267 """check parameter:
3268 0: no check at all
3269 1: check and raise error in case of problem
3270 2: check and adapt the owner dates"""
3272 if self.hasSession(newSession):
3273 return
3274 if self.getSchedule().isOutside(newSession):
3275 if check == 1:
3276 MaKaCError( _("Cannot add this session (Start:%s - End:%s) Outside of the event's time table(Start:%s - End:%s)") % (newSession.getStartDate(),newSession.getEndDate(),self.getSchedule().getStartDate(), self.getSchedule().getEndDate()),"Event")
3277 elif check == 2:
3278 if self.getSchedule().getStartDate() > newSession.getStartDate():
3279 self.setStartDate(newSession.getStartDate())
3280 if self.getSchedule().getEndDate() < newSession.getEndDate():
3281 self.setEndDate(newSession.getEndDate())
3282 if id!=None:
3283 sessionId = id
3284 # Keep ID counter up to date
3285 self.__sessionGenerator.newCount()
3286 else:
3287 sessionId=self.__generateNewSessionId()
3288 self.sessions[sessionId]=newSession
3289 newSession.includeInConference(self,sessionId)
3290 #keep the session coordinator index updated
3291 for sc in newSession.getCoordinatorList():
3292 self.addSessionCoordinator(newSession,sc)
3293 self.notifyModification()
3295 def hasSession(self,session):
3296 if session != None and session.getConference()==self and \
3297 self.sessions.has_key(session.getId()):
3298 return True
3299 return False
3301 def removeSession(self,session, deleteContributions=False):
3302 if self.hasSession(session):
3303 for sc in session.getCoordinatorList():
3304 self.removeSessionCoordinator(session,sc)
3306 if deleteContributions:
3307 for contrib in session.getContributionList():
3308 contrib.delete()
3310 del self.sessions[session.getId()]
3312 session.delete()
3313 self.notifyModification()
3315 def recoverSession(self, session, check, isCancelled):
3316 self.addSession(session, check, session.getId())
3317 session.recover(isCancelled)
3319 def getSessionById( self, sessionId ):
3320 """Returns the session from the conference list corresponding to the
3321 unique session id specified
3323 return self.sessions.get(sessionId,None)
3325 def getRoomList(self):
3326 roomList =[]
3327 for session in self.sessions.values():
3328 if session.getRoom()!=None:
3329 roomname = session.getRoom().getName()
3330 if roomname not in roomList:
3331 roomList.append(roomname)
3332 return roomList
3334 def getSessionList( self ):
3335 """Retruns a list of the conference session objects
3337 return self.sessions.values()
3339 def getSessionListSorted( self ):
3340 """Retruns a sorted list of the conference sessions
3342 res=[]
3343 for entry in self.getSchedule().getEntries():
3344 if isinstance(entry,LinkedTimeSchEntry) and \
3345 isinstance(entry.getOwner(),SessionSlot):
3346 session=entry.getOwner().getSession()
3347 if session not in res:
3348 res.append(session)
3349 return res
3351 def getSessionSlotList(self):
3352 return [slot for session in self.sessions.values() for slot in session.getSlotList()]
3354 def getNumberOfSessions(self):
3355 return len(self.sessions)
3357 def _generateNewContributionId(self):
3358 """Returns a new unique identifier for the current conference
3359 contributions
3361 return str(self.__contribGenerator.newCount())
3363 def genNewAbstractId(self):
3364 return str(self.__contribGenerator.newCount())
3366 def syncContribCounter(self):
3367 self.__contribGenerator.sync(self.getAbstractMgr()._getOldAbstractCounter())
3368 return self.__contribGenerator._getCount()
3370 def addContribution(self,newContrib,id=""):
3371 """Adds a new contribution object to the conference taking care of
3372 assigning a new unique id to it
3374 if self.hasContribution(newContrib):
3375 return
3376 if isinstance(newContrib.getCurrentStatus(),ContribStatusWithdrawn):
3377 raise MaKaCError( _("Cannot add a contribution which has been withdrawn"), _("Event"))
3378 if id is None or id=="":
3379 contribId=self._generateNewContributionId()
3380 while self.contributions.has_key(contribId):
3381 contribId=self._generateNewContributionId()
3382 else:
3383 contribId=str(id)
3384 if self.contributions.has_key(contribId):
3385 raise MaKaCError( _("Cannot add this contribution id:(%s) as it has already been used")%contribId, _("Event"))
3386 newContrib.includeInConference(self,contribId)
3387 self.contributions[contribId]=newContrib
3388 for auth in newContrib.getAuthorList():
3389 self.indexAuthor(auth)
3390 for spk in newContrib.getSpeakerList():
3391 self.indexSpeaker(spk)
3392 for sub in newContrib.getSubmitterList():
3393 self.addContribSubmitter(newContrib,sub)
3395 signals.event.contribution_created.send(newContrib, parent=self)
3396 self.notifyModification()
3398 def hasContribution(self,contrib):
3399 return contrib.getConference()==self and \
3400 self.contributions.has_key(contrib.getId())
3402 def removeContribution( self, contrib, callDelete=True ):
3403 if not self.contributions.has_key( contrib.getId() ):
3404 return
3405 for sub in contrib.getSubmitterList()[:]:
3406 self.removeContribSubmitter(contrib,sub)
3407 for auth in contrib.getPrimaryAuthorList()[:]:
3408 contrib.removePrimaryAuthor(auth)
3409 for auth in contrib.getCoAuthorList()[:]:
3410 contrib.removeCoAuthor(auth)
3411 for spk in contrib.getSpeakerList()[:]:
3412 contrib.removeSpeaker(spk)
3413 del self.contributions[ contrib.getId() ]
3414 if callDelete:
3415 contrib.delete()
3416 #else:
3417 # contrib.unindex()
3418 self.notifyModification()
3420 def recoverContribution(self, contrib):
3421 self.addContribution(contrib, contrib.getId())
3422 contrib.recover()
3424 # Note: this kind of factories should never be used as they only allow to
3425 # create a single type of contributions
3426 def newContribution( self, id=None ):
3427 """Creates and returns a new contribution object already added to the
3428 conference list (all its data is set to the default)
3430 c = Contribution()
3431 self.addContribution( c, id )
3432 return c
3434 def getOwnContributionById( self, id ):
3435 """Returns the contribution from the conference list corresponding to
3436 the unique contribution id specified
3438 if self.contributions.has_key( id ):
3439 return self.contributions[ id ]
3440 return None
3442 def getContributionById( self, id ):
3443 """Returns the contribution corresponding to the id specified
3445 return self.contributions.get(str(id).strip(),None)
3447 def getContributionList(self):
3448 """Returns a list of the conference contribution objects
3450 return self.contributions.values()
3452 def iterContributions(self):
3453 return self.contributions.itervalues()
3455 def getContributionListWithoutSessions(self):
3456 """Returns a list of the conference contribution objects which do not have a session
3458 return [c for c in self.contributions.values() if not c.getSession()]
3461 def getContributionListSorted(self, includeWithdrawn=True, key="id"):
3462 """Returns a list of the conference contribution objects, sorted by key provided
3464 contributions = self.contributions.values()
3465 if not includeWithdrawn:
3466 contributions = filter(lambda c: not isinstance(c.getCurrentStatus(), ContribStatusWithdrawn), contributions)
3467 contributions.sort(key = lambda c: getattr(c, key))
3468 return contributions
3470 def getNumberOfContributions(self, only_scheduled=False):
3471 if only_scheduled:
3472 return len(filter(lambda c: c.isScheduled(), self.contributions.itervalues()))
3473 else:
3474 return len(self.contributions)
3476 def hasSomethingOnWeekend(self, day):
3477 """Checks if the event has a session or contribution on the weekend indicated by `day`.
3479 `day` must be either a saturday or a sunday"""
3480 if day.weekday() == 5:
3481 weekend = (day, day + timedelta(days=1))
3482 elif day.weekday() == 6:
3483 weekend = (day, day - timedelta(days=1))
3484 else:
3485 raise ValueError('day must be on a weekend')
3486 return (any(c.startDate.date() in weekend and not isinstance(c.getCurrentStatus(), ContribStatusWithdrawn)
3487 for c in self.contributions.itervalues() if c.startDate is not None) or
3488 any(s.startDate.date() in weekend for s in self.sessions.itervalues() if s.startDate is not None))
3490 def getProgramDescription(self):
3491 try:
3492 return self.programDescription
3493 except:
3494 self.programDescription = ""
3495 return self.programDescription
3497 def setProgramDescription(self, txt):
3498 self.programDescription = txt
3500 def _generateNewTrackId( self ):
3503 return str(self.__programGenerator.newCount())
3505 def addTrack( self, newTrack ):
3508 #XXX: The conference program shoul be isolated in a separated object
3509 if newTrack in self.program:
3510 return
3512 trackId = newTrack.getId()
3513 if trackId == "not assigned":
3514 trackId = self._generateNewTrackId()
3515 self.program.append( newTrack )
3516 newTrack.setConference( self )
3517 newTrack.setId( trackId )
3518 self.notifyModification()
3520 def removeTrack( self, track ):
3521 if track in self.program:
3522 track.delete()
3523 if track in self.program:
3524 self.program.remove( track )
3525 self.notifyModification()
3527 def recoverTrack(self, track):
3528 self.addTrack(track)
3529 track.recover()
3531 def newTrack( self ):
3534 t = Track()
3535 self.addTrack( t )
3536 return t
3538 def getTrackById( self, id ):
3541 for track in self.program:
3542 if track.getId() == id.strip():
3543 return track
3544 return None
3546 def getTrackList( self ):
3549 return self.program
3551 def isLastTrack(self,track):
3554 return self.getTrackPos(track)==(len(self.program)-1)
3556 def isFirstTrack(self,track):
3559 return self.getTrackPos(track)==0
3561 def getTrackPos(self,track):
3564 return self.program.index(track)
3566 def moveTrack(self,track,newPos):
3569 self.program.remove(track)
3570 self.program.insert(newPos,track)
3571 self.notifyModification()
3573 def moveUpTrack(self,track):
3576 if self.isFirstTrack(track):
3577 return
3578 newPos=self.getTrackPos(track)-1
3579 self.moveTrack(track,newPos)
3581 def moveDownTrack(self,track):
3584 if self.isLastTrack(track):
3585 return
3586 newPos = self.getTrackPos(track) + 1
3587 self.moveTrack(track, newPos)
3589 def _cmpTracks(self, t1, t2):
3590 o1 = self.program.index(t1)
3591 o2 = self.program.index(t2)
3592 return cmp(o1, o2)
3594 def sortTrackList(self, l):
3595 """Sorts out a list of tracks according to the current programme order.
3597 if len(l) == 0:
3598 return []
3599 elif len(l) == 1:
3600 return [l[0]]
3601 else:
3602 res = []
3603 for i in l:
3604 res.append(i)
3605 res.sort(self._cmpTracks)
3606 return res
3608 def requireDomain(self, dom):
3609 self.__ac.requireDomain(dom)
3610 signals.event.domain_access_granted.send(self, domain=dom)
3612 def freeDomain(self, dom):
3613 self.__ac.freeDomain(dom)
3614 signals.event.domain_access_revoked.send(self, domain=dom)
3616 def getDomainList(self):
3617 return self.__ac.getRequiredDomainList()
3619 def isProtected(self):
3620 """Tells whether a conference is protected for accessing or not
3622 return self.__ac.isProtected()
3624 def getAccessProtectionLevel( self ):
3625 return self.__ac.getAccessProtectionLevel()
3627 def isItselfProtected( self ):
3628 return self.__ac.isItselfProtected()
3630 def hasAnyProtection( self ):
3631 """Tells whether a conference has any kind of protection over it:
3632 access or domain protection.
3634 if self.isProtected():
3635 return True
3636 if self.getDomainList():
3637 return True
3639 if self.getAccessProtectionLevel() == -1:
3640 return False
3642 for owner in self.getOwnerList():
3643 if owner.hasAnyProtection():
3644 return True
3646 return False
3648 def hasProtectedOwner( self ):
3649 return self.__ac._getFatherProtection()
3651 def setProtection( self, private ):
3653 Allows to change the conference access protection
3656 oldValue = 1 if self.isProtected() else -1
3658 self.getAccessController().setProtection( private )
3660 if oldValue != private:
3661 # notify listeners
3662 signals.event.protection_changed.send(self, old=oldValue, new=private)
3664 def grantAccess( self, prin ):
3665 self.__ac.grantAccess( prin )
3666 if isinstance(prin, AvatarUserWrapper):
3667 prin.linkTo(self, "access")
3669 def revokeAccess( self, prin ):
3670 self.__ac.revokeAccess( prin )
3671 if isinstance(prin, AvatarUserWrapper):
3672 prin.unlinkTo(self, "access")
3674 def canView( self, aw ):
3675 """tells whether the specified access wrappers has access to the current
3676 object or any of its parts"""
3677 if self.canAccess( aw ):
3678 return True
3679 for session in self.getSessionList():
3680 if session.canView( aw ):
3681 return True
3682 for contrib in self.getContributionList():
3683 if contrib.canView( aw ):
3684 return True
3685 return False
3687 def isAllowedToAccess( self, av):
3688 """tells if a user has privileges to access the current conference
3689 (independently that it is protected or not)
3691 if not av:
3692 return False
3693 if (av in self.getChairList()) or (self.__ac.canUserAccess( av )) or (self.canUserModify( av )):
3694 return True
3696 # if the conference is not protected by itself
3697 if not self.isItselfProtected():
3698 # then inherit behavior from parent category
3699 for owner in self.getOwnerList():
3700 if owner.isAllowedToAccess( av ):
3701 return True
3703 # track coordinators are also allowed to access the conference
3704 for track in self.getTrackList():
3705 if track.isCoordinator( av ):
3706 return True
3708 # paper reviewing team should be also allowed to access
3709 if self.getConfPaperReview().isInReviewingTeam(av):
3710 return True
3712 return False
3714 def canAccess( self, aw ):
3715 """Tells whether an access wrapper is allowed to access the current
3716 conference: when the conference is protected, only if the user is a
3717 chair or is granted to access the conference, when the client ip is
3718 not restricted.
3721 # Allow harvesters (Invenio, offline cache) to access
3722 # protected pages
3723 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
3724 return True
3726 # Managers have always access
3727 if self.canModify(aw):
3728 return True
3730 if self.isProtected():
3731 if self.isAllowedToAccess( aw.getUser() ):
3732 return True
3733 else:
3734 return self.canKeyAccess(aw)
3735 else:
3736 # Domain control is triggered just for PUBLIC events
3737 return self.canIPAccess(request.remote_addr)
3739 def canKeyAccess(self, aw, key=None):
3740 accessKey = self.getAccessKey()
3741 if not accessKey:
3742 return False
3743 return key == accessKey or session.get('accessKeys', {}).get(self.getUniqueId()) == accessKey
3745 def canKeyModify(self):
3746 modifKey = self.getModifKey()
3747 if not modifKey:
3748 return False
3749 return session.get('modifKeys', {}).get(self.id) == modifKey
3751 def grantModification( self, prin, sendEmail=True ):
3752 email = None
3753 if isinstance(prin, ConferenceChair):
3754 email = prin.getEmail()
3755 elif isinstance(prin, str):
3756 email = prin
3757 if email != None:
3758 if email == "":
3759 return
3760 ah = AvatarHolder()
3761 results=ah.match({"email":email}, exact=1)
3762 #No registered user in Indico with that email
3763 if len(results) == 0:
3764 self.__ac.grantModificationEmail(email)
3765 self.getConference().getPendingQueuesMgr().addPendingConfManager(prin, False)
3766 if sendEmail and isinstance(prin, ConferenceChair):
3767 notif = pendingQueues._PendingConfManagerNotification( [prin] )
3768 mail.GenericMailer.sendAndLog( notif, self.getConference() )
3769 #The user is registered in Indico and is activated as well
3770 elif len(results) == 1 and results[0] is not None and results[0].isActivated():
3771 self.__ac.grantModification(results[0])
3772 results[0].linkTo(self, "manager")
3773 else:
3774 self.__ac.grantModification( prin )
3775 if isinstance(prin, AvatarUserWrapper):
3776 prin.linkTo(self, "manager")
3778 def revokeModification( self, prin ):
3779 self.__ac.revokeModification( prin )
3780 if isinstance(prin, AvatarUserWrapper):
3781 prin.unlinkTo(self, "manager")
3783 @unify_user_args(legacy=True)
3784 def canUserModify( self, av ):
3785 if av == None:
3786 return False
3787 if ( av == self.getCreator()) or self.getAccessController().canModify( av ):
3788 return True
3789 for owner in self.getOwnerList():
3790 if owner.canUserModify( av ):
3791 return True
3792 return False
3794 @unify_user_args(legacy=True)
3795 def canModify(self, aw_or_user):
3796 """Tells whether an access wrapper is allowed to modify the current
3797 conference: only if the user is granted to modify the conference and
3798 he is accessing from an IP address which is not restricted.
3800 if hasattr(aw_or_user, 'getUser'):
3801 aw_or_user = aw_or_user.getUser()
3802 return self.canUserModify(aw_or_user) or self.canKeyModify()
3804 def getManagerList( self ):
3805 return self.__ac.getModifierList()
3807 def addToRegistrars(self, av):
3808 self.getRegistrarList().append(av)
3809 self.notifyModification()
3810 if isinstance(av, AvatarUserWrapper):
3811 av.linkTo(self, "registrar")
3813 def removeFromRegistrars(self, av):
3814 self.getRegistrarList().remove(av)
3815 self.notifyModification()
3816 if isinstance(av, AvatarUserWrapper):
3817 av.unlinkTo(self, "registrar")
3819 def isRegistrar(self, av):
3820 if av is None:
3821 return False
3822 try:
3823 return any(principal.containsUser(av) for principal in self.getRegistrarList())
3824 except AttributeError:
3825 return False
3827 def getRegistrarList(self):
3828 try:
3829 return self.__registrars
3830 except AttributeError:
3831 self.__registrars = []
3832 return self.__registrars
3834 def canManageRegistration(self, av):
3835 return self.isRegistrar(av) or self.canUserModify(av)
3837 def getAllowedToAccessList( self ):
3838 return self.__ac.getAccessList()
3840 def addMaterial( self, newMat ):
3841 newMat.setId( str(self.__materialGenerator.newCount()) )
3842 newMat.setOwner( self )
3843 self.materials[ newMat.getId() ] = newMat
3844 self.notifyModification()
3846 def removeMaterial( self, mat ):
3847 if mat.getId() in self.materials.keys():
3848 mat.delete()
3849 self.materials[mat.getId()].setOwner(None)
3850 del self.materials[ mat.getId() ]
3851 self.notifyModification()
3852 elif mat.getId().lower() == 'paper':
3853 self.removePaper()
3854 self.notifyModification()
3855 elif mat.getId().lower() == 'slides':
3856 self.removeSlides()
3857 self.notifyModification()
3858 elif mat.getId().lower() == 'minutes':
3859 self.removeMinutes()
3860 self.notifyModification()
3861 elif mat.getId().lower() == 'video':
3862 self.removeVideo()
3863 self.notifyModification()
3864 elif mat.getId().lower() == 'poster':
3865 self.removePoster()
3866 self.notifyModification()
3868 def recoverMaterial(self, recMat):
3869 # Id must already be set in recMat.
3870 recMat.setOwner(self)
3871 self.materials[recMat.getId()] = recMat
3872 recMat.recover()
3873 self.notifyModification()
3875 def getMaterialRegistry(self):
3877 Return the correct material registry for this type
3879 from MaKaC.webinterface.materialFactories import ConfMFRegistry
3880 return ConfMFRegistry
3882 def getMaterialById( self, matId ):
3883 if matId.lower() == 'paper':
3884 return self.getPaper()
3885 elif matId.lower() == 'slides':
3886 return self.getSlides()
3887 elif matId.lower() == 'video':
3888 return self.getVideo()
3889 elif matId.lower() == 'poster':
3890 return self.getPoster()
3891 elif matId.lower() == 'minutes':
3892 return self.getMinutes()
3893 elif self.materials.has_key(matId):
3894 return self.materials[ matId ]
3895 return None
3897 def getMaterialList( self ):
3898 return self.materials.values()
3900 def getAllMaterialList(self, sort=True):
3901 l = self.getMaterialList()
3902 if self.getPaper():
3903 l.append( self.getPaper() )
3904 if self.getSlides():
3905 l.append( self.getSlides() )
3906 if self.getVideo():
3907 l.append( self.getVideo() )
3908 if self.getPoster():
3909 l.append( self.getPoster() )
3910 if self.getMinutes():
3911 l.append( self.getMinutes() )
3912 if sort:
3913 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
3914 return l
3916 def _getMaterialFiles(self, material):
3918 Adaption of _getMaterialFiles in WPTPLConferenceDisplay for desired format, objects
3919 seemed mutually exclusive hence use of similar logic here specific to Conference.
3921 files = []
3922 processed = []
3924 for res in material.getResourceList():
3925 try:
3926 ftype = res.getFileType()
3927 fname = res.getFileName()
3928 furl = urlHandlers.UHFileAccess.getURL(res)
3930 if fname in processed:
3931 fname = "%s - %s" % (fname, processed.count(fname))
3933 processed.append(res.getFileName())
3934 except:
3935 # If we are here then the resource is a Link object.
3936 fname, ftype, furl = str(res.getURL()), "link", str(res.getURL())
3937 fdesc = res.getDescription()
3938 files.append({'title': fname,
3939 'description': fdesc,
3940 'type': ftype,
3941 'url': furl})
3942 return files
3944 def getAllMaterialDict(self, child=None):
3946 This method iterates through the children of the conference, creating
3947 a dictionary which maps type to material link URLs.
3950 child = self if child is None else child
3952 node = {}
3953 node['title'] = child.getTitle()
3955 try:
3956 node['type'] = child.getType()
3957 except:
3958 # If we land here, it's a session which doesn't have 'getType'
3959 node['type'] = 'session'
3961 node['children'] = []
3962 node['material'] = []
3964 if node['type'] in ['conference', 'meeting']:
3965 for session in child.getSessionList():
3966 node['children'].append(self.getAllMaterialDict(session))
3968 for contrib in child.getContributionList():
3969 node['children'].append(self.getAllMaterialDict(contrib))
3971 for material in child.getAllMaterialList():
3972 files = self._getMaterialFiles(material)
3974 for f in files:
3975 materialNode = {}
3976 materialNode['type'] = 'material'
3977 materialNode['title'] = material.getTitle()
3979 if material.getTitle() != 'Minutes':
3980 materialNode['title'] += ' - ' + f['title']
3982 materialNode['materialType'] = f['type']
3983 materialNode['url'] = str(f['url'])
3985 node['material'].append(materialNode)
3987 return node
3989 def setPaper( self, newPaper ):
3990 if self.getPaper() != None:
3991 raise MaKaCError( _("The paper for this conference has already been set"), _("Conference"))
3992 self.paper=newPaper
3993 self.paper.setOwner( self )
3994 self.notifyModification()
3996 def removePaper( self ):
3997 if self.paper is None:
3998 return
3999 self.paper.delete()
4000 self.paper.setOwner(None)
4001 self.paper = None
4002 self.notifyModification()
4004 def recoverPaper(self, p):
4005 self.setPaper(p)
4006 p.recover()
4008 def getPaper( self ):
4009 try:
4010 if self.paper:
4011 pass
4012 except AttributeError:
4013 self.paper = None
4014 return self.paper
4016 def setSlides( self, newSlides ):
4017 if self.getSlides() != None:
4018 raise MaKaCError( _("The slides for this conference have already been set"), _("Conference"))
4019 self.slides=newSlides
4020 self.slides.setOwner( self )
4021 self.notifyModification()
4023 def removeSlides( self ):
4024 if self.slides is None:
4025 return
4026 self.slides.delete()
4027 self.slides.setOwner( None )
4028 self.slides= None
4029 self.notifyModification()
4031 def recoverSlides(self, s):
4032 self.setSlides(s)
4033 s.recover()
4035 def getSlides( self ):
4036 try:
4037 if self.slides:
4038 pass
4039 except AttributeError:
4040 self.slides = None
4041 return self.slides
4043 def setVideo( self, newVideo ):
4044 if self.getVideo() != None:
4045 raise MaKaCError( _("The video for this conference has already been set"), _("Conference"))
4046 self.video=newVideo
4047 self.video.setOwner( self )
4048 self.notifyModification()
4050 def removeVideo( self ):
4051 if self.getVideo() is None:
4052 return
4053 self.video.delete()
4054 self.video.setOwner(None)
4055 self.video = None
4056 self.notifyModification()
4058 def recoverVideo(self, v):
4059 self.setVideo(v)
4060 v.recover()
4062 def getVideo( self ):
4063 try:
4064 if self.video:
4065 pass
4066 except AttributeError:
4067 self.video = None
4068 return self.video
4070 def setPoster( self, newPoster ):
4071 if self.getPoster() != None:
4072 raise MaKaCError( _("the poster for this conference has already been set"), _("Conference"))
4073 self.poster=newPoster
4074 self.poster.setOwner( self )
4075 self.notifyModification()
4077 def removePoster( self ):
4078 if self.getPoster() is None:
4079 return
4080 self.poster.delete()
4081 self.poster.setOwner(None)
4082 self.poster = None
4083 self.notifyModification()
4085 def recoverPoster(self, p):
4086 self.setPoster(p)
4087 p.recover()
4089 def getPoster( self ):
4090 try:
4091 if self.poster:
4092 pass
4093 except AttributeError:
4094 self.poster = None
4095 return self.poster
4097 def setMinutes( self, newMinutes ):
4098 if self.getMinutes() != None:
4099 raise MaKaCError( _("The Minutes for this conference has already been set"))
4100 self.minutes=newMinutes
4101 self.minutes.setOwner( self )
4102 self.notifyModification()
4104 def createMinutes( self ):
4105 if self.getMinutes() != None:
4106 raise MaKaCError( _("The minutes for this conference have already been created"), _("Conference"))
4107 self.minutes = Minutes()
4108 self.minutes.setOwner( self )
4109 self.notifyModification()
4110 return self.minutes
4112 def removeMinutes( self ):
4113 if self.getMinutes() is None:
4114 return
4115 self.minutes.delete()
4116 self.minutes.setOwner( None )
4117 self.minutes = None
4118 self.notifyModification()
4120 def recoverMinutes(self, min):
4121 self.removeMinutes() # To ensure that the current minutes are put in
4122 # the trash can.
4123 self.minutes = min
4124 self.minutes.setOwner( self )
4125 min.recover()
4126 self.notifyModification()
4127 return self.minutes
4129 def getMinutes( self ):
4130 #To be removed
4131 try:
4132 if self.minutes:
4133 pass
4134 except AttributeError, e:
4135 self.minutes = None
4136 return self.minutes
4138 def _setSchedule( self, sch=None ):
4139 self.__schedule=ConferenceSchedule(self)
4140 for session in self.getSessionList():
4141 for slot in session.getSlotList():
4142 self.__schedule.addEntry(slot.getConfSchEntry())
4144 def getSchedule( self ):
4145 try:
4146 if not self.__schedule:
4147 self._setSchedule()
4148 except AttributeError, e:
4149 self._setSchedule()
4150 return self.__schedule
4152 def fit( self ):
4153 sch = self.getSchedule()
4155 sDate = sch.calculateStartDate()
4156 eDate = sch.calculateEndDate()
4157 self.setStartDate(sDate)
4158 self.setEndDate(eDate)
4160 def fitSlotsOnDay( self, day ):
4161 for entry in self.getSchedule().getEntriesOnDay(day) :
4162 if isinstance(entry.getOwner(), SessionSlot) :
4163 entry.getOwner().fit()
4165 def getDisplayMgr(self):
4167 Return the display manager for the conference
4169 from MaKaC.webinterface import displayMgr
4170 return displayMgr.ConfDisplayMgrRegistery().getDisplayMgr(self)
4172 def getDefaultStyle( self ):
4173 return self.getDisplayMgr().getDefaultStyle()
4175 def clone( self, startDate, options, eventManager=None, userPerformingClone = None ):
4176 # startDate must be in the timezone of the event (to avoid problems with daylight-saving times)
4177 cat = self.getOwnerList()[0]
4178 managing = options.get("managing",None)
4179 if managing is not None:
4180 creator = managing
4181 else:
4182 creator = self.getCreator()
4183 conf = cat.newConference(creator)
4184 if managing is not None :
4185 conf.grantModification(managing)
4186 conf.setTitle(self.getTitle())
4187 conf.setDescription(self.getDescription())
4188 conf.setTimezone(self.getTimezone())
4189 for loc in self.getLocationList():
4190 if loc is not None:
4191 conf.addLocation(loc.clone())
4192 if self.getRoom() is not None:
4193 conf.setRoom(self.getRoom().clone())
4194 startDate = timezone(self.getTimezone()).localize(startDate).astimezone(timezone('UTC'))
4195 timeDelta = startDate - self.getStartDate()
4196 endDate = self.getEndDate() + timeDelta
4197 conf.setDates( startDate, endDate, moveEntries=1 )
4198 conf.setContactInfo(self.getContactInfo())
4199 conf.setChairmanText(self.getChairmanText())
4200 conf.setVisibility(self.getVisibility())
4201 conf.setSupportInfo(self.getSupportInfo().clone(self))
4202 conf.setReportNumberHolder(self.getReportNumberHolder().clone(self))
4203 for ch in self.getChairList():
4204 conf.addChair(ch.clone())
4205 ContextManager.setdefault("clone.unique_id_map", {})[self.getUniqueId()] = conf.getUniqueId()
4206 # Display Manager
4207 from MaKaC.webinterface import displayMgr
4208 selfDispMgr=displayMgr.ConfDisplayMgrRegistery().getDisplayMgr(self)
4209 selfDispMgr.clone(conf)
4210 # Contribution Types' List (main detailes of the conference)
4211 for t in self.getContribTypeList() :
4212 conf.addContribType(t.clone(conf))
4213 if options.get("sessions", False):
4214 for entry in self.getSchedule().getEntries():
4215 if isinstance(entry,BreakTimeSchEntry):
4216 conf.getSchedule().addEntry(entry.clone(conf))
4217 db_root = DBMgr.getInstance().getDBConnection().root()
4218 if db_root.has_key( "webfactoryregistry" ):
4219 confRegistry = db_root["webfactoryregistry"]
4220 else:
4221 confRegistry = OOBTree.OOBTree()
4222 db_root["webfactoryregistry"] = confRegistry
4223 meeting=False
4224 # if the event is a meeting or a lecture
4225 if confRegistry.get(str(self.getId()), None) is not None :
4226 meeting=True
4227 confRegistry[str(conf.getId())] = confRegistry[str(self.getId())]
4228 # if it's a conference, no web factory is needed
4229 # Tracks in a conference
4230 if options.get("tracks",False) :
4231 for tr in self.getTrackList() :
4232 conf.addTrack(tr.clone(conf))
4233 # Meetings' and conferences' sessions cloning
4234 if options.get("sessions",False) :
4235 for s in self.getSessionList() :
4236 newSes = s.clone(timeDelta, conf, options, session_id=s.getId())
4237 ContextManager.setdefault("clone.unique_id_map", {})[s.getUniqueId()] = newSes.getUniqueId()
4238 conf.addSession(newSes)
4239 # Materials' cloning
4240 if options.get("materials",False) :
4241 for m in self.getMaterialList() :
4242 conf.addMaterial(m.clone(conf))
4243 if self.getPaper() is not None:
4244 conf.setPaper(self.getPaper().clone(conf))
4245 if self.getSlides() is not None:
4246 conf.setSlides(self.getSlides().clone(conf))
4247 if self.getVideo() is not None:
4248 conf.setVideo(self.getVideo().clone(conf))
4249 if self.getPoster() is not None:
4250 conf.setPoster(self.getPoster().clone(conf))
4251 if self.getMinutes() is not None:
4252 conf.setMinutes(self.getMinutes().clone(conf))
4253 # access and modification keys
4254 if options.get("keys", False) :
4255 conf.setAccessKey(self.getAccessKey())
4256 conf.setModifKey(self.getModifKey())
4257 # Access Control cloning
4258 if options.get("access",False) :
4259 conf.setProtection(self.getAccessController()._getAccessProtection())
4260 for mgr in self.getManagerList() :
4261 conf.grantModification(mgr)
4262 for user in self.getAllowedToAccessList() :
4263 conf.grantAccess(user)
4264 for right in self.getSessionCoordinatorRights():
4265 conf.addSessionCoordinatorRight(right)
4266 for domain in self.getDomainList():
4267 conf.requireDomain(domain)
4268 # conference's registration form
4269 if options.get("registration",False) :
4270 conf.setRegistrationForm(self.getRegistrationForm().clone(conf))
4272 # conference's evaluation
4273 if options.get("evaluation",False) :
4274 #Modify this, if you have now many evaluations.
4275 #You will have to clone every evaluations of this conference.
4276 conf.setEvaluations([self.getEvaluation().clone(conf)])
4278 #conference's abstracts
4279 if options.get("abstracts",False) :
4280 conf.abstractMgr = self.abstractMgr.clone(conf)
4281 # conference's alerts
4282 if options.get("alerts",False) :
4283 for alarm in self.getAlarmList() :
4284 # Indico does not clone absoulte alarms
4285 if alarm._relative is not None:
4286 # .clone takes care of enqueuing it
4287 alarm.clone(conf)
4288 # Meetings' and conferences' contributions cloning
4289 if options.get("contributions",False) :
4290 sch = conf.getSchedule()
4291 for cont in self.getContributionList():
4292 if cont.getSession() is None :
4293 if not meeting:
4294 nc = cont.clone(conf, options, timeDelta)
4295 conf.addContribution(nc)
4296 if cont.isScheduled() :
4297 sch.addEntry(nc.getSchEntry())
4298 ContextManager.setdefault("clone.unique_id_map", {})[cont.getUniqueId()] = nc.getUniqueId()
4299 elif cont.isScheduled():
4300 # meetings...only scheduled
4301 nc = cont.clone(conf, options, timeDelta)
4302 conf.addContribution(nc)
4303 sch.addEntry(nc.getSchEntry())
4304 ContextManager.setdefault("clone.unique_id_map", {})[cont.getUniqueId()] = nc.getUniqueId()
4305 # Participants' module settings and list cloning
4306 if options.get("participants",False) :
4307 self.getParticipation().clone(conf, options, eventManager)
4308 conf.notifyModification()
4310 #we inform the plugins in case they want to add anything to the new conference
4311 EventCloner.clone_event(self, conf)
4312 return conf
4314 def newAlarm(self, when, enqueue=True):
4316 if type(when) == timedelta:
4317 relative = when
4318 dtStart = None
4319 else:
4320 relative = None
4321 dtStart = when
4323 confRelId = self._getNextAlarmId()
4324 al = tasks.AlarmTask(self, confRelId,
4325 startDateTime=dtStart,
4326 relative=relative)
4328 self.addAlarm(al, enqueue)
4329 return al
4331 def removeAlarm(self, alarm):
4332 confRelId = alarm.getConfRelativeId()
4334 if confRelId in self.alarmList:
4335 del self.alarmList[confRelId]
4336 self._p_changed = 1
4338 tl = Client()
4339 tl.dequeue(alarm)
4340 else:
4341 raise Exception("alarm not in list!")
4343 def _getNextAlarmId(self):
4344 return self.__alarmCounter.newCount()
4346 def addAlarm(self, alarm, enqueue = True):
4347 if enqueue:
4348 tl = Client()
4349 tl.enqueue(alarm)
4351 self.alarmList[alarm.getConfRelativeId()] = alarm
4352 self._p_changed = 1
4354 def recoverAlarm(self, alarm):
4355 self.addAlarm(alarm)
4356 alarm.conf = self
4357 alarm.recover()
4359 def getAlarmList(self):
4360 return self.alarmList.values()
4362 def getAlarmById(self, id):
4363 """For given id returns corresponding Alarm or None if not found."""
4364 return self.alarmList.get(id, None)
4366 def getCoordinatedTracks( self, av ):
4367 """Returns a list with the tracks for which a user is coordinator.
4369 try:
4370 if self._trackCoordinators:
4371 pass
4372 except AttributeError:
4373 self._trackCoordinators = TCIndex()
4374 self.notifyModification()
4375 return self._trackCoordinators.getTracks( av )
4377 def addTrackCoordinator( self, track, av ):
4378 """Makes a user become coordinator for a track.
4380 try:
4381 if self._trackCoordinators:
4382 pass
4383 except AttributeError:
4384 self._trackCoordinators = TCIndex()
4385 self.notifyModification()
4386 if track in self.program:
4387 track.addCoordinator( av )
4388 self._trackCoordinators.indexCoordinator( av, track )
4389 self.notifyModification()
4391 def removeTrackCoordinator( self, track, av ):
4392 """Removes a user as coordinator for a track.
4394 try:
4395 if self._trackCoordinators:
4396 pass
4397 except AttributeError:
4398 self._trackCoordinators = TCIndex()
4399 self.notifyModification()
4400 if track in self.program:
4401 track.removeCoordinator( av )
4402 self._trackCoordinators.unindexCoordinator( av, track )
4403 self.notifyModification()
4405 def _rebuildAuthorIndex(self):
4406 self._authorIdx=AuthorIndex()
4407 for contrib in self.getContributionList():
4408 if not isinstance(contrib.getCurrentStatus(),ContribStatusWithdrawn):
4409 for auth in contrib.getAuthorList():
4410 self._authorIdx.index(auth)
4412 def getAuthorIndex(self):
4413 try:
4414 if self._authorIdx:
4415 pass
4416 except AttributeError:
4417 self._rebuildAuthorIndex()
4418 return self._authorIdx
4420 def indexAuthor(self,auth):
4421 c=auth.getContribution()
4422 if c.isAuthor(auth):
4423 if not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4424 self.getAuthorIndex().index(auth)
4425 if c.isPrimaryAuthor(auth):
4426 self._getPrimAuthIndex().index(auth)
4428 def unindexAuthor(self,auth):
4429 c=auth.getContribution()
4430 if c.isAuthor(auth):
4431 self.getAuthorIndex().unindex(auth)
4432 if c.isPrimaryAuthor(auth):
4433 self._getPrimAuthIndex().unindex(auth)
4435 def _rebuildSpeakerIndex(self):
4436 self._speakerIdx=AuthorIndex()
4437 for contrib in self.getContributionList():
4438 if not isinstance(contrib.getCurrentStatus(),ContribStatusWithdrawn):
4439 for auth in contrib.getSpeakerList():
4440 self._speakerIdx.index(auth)
4441 for subcontrib in contrib.getSubContributionList():
4442 for auth in subcontrib.getSpeakerList():
4443 self._speakerIdx.index(auth)
4445 def getSpeakerIndex(self):
4446 try:
4447 if self._speakerIdx:
4448 pass
4449 except AttributeError:
4450 self._rebuildSpeakerIndex()
4451 return self._speakerIdx
4453 def indexSpeaker(self,auth):
4454 c=auth.getContribution()
4455 if not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4456 self.getSpeakerIndex().index(auth)
4458 def unindexSpeaker(self,auth):
4459 c=auth.getContribution()
4460 if c and not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4461 self.getSpeakerIndex().unindex(auth)
4463 def getRegistrationForm(self):
4464 try:
4465 if self._registrationForm is None:
4466 self._registrationForm = registration.RegistrationForm(self)
4467 except AttributeError,e:
4468 self._registrationForm = registration.RegistrationForm(self)
4469 return self._registrationForm
4471 def setRegistrationForm(self,rf):
4472 self._registrationForm = rf
4473 rf.setConference(self)
4475 def removeRegistrationForm(self):
4476 try:
4477 self._registrationForm.delete()
4478 self._registrationForm.setConference(None)
4479 self._registrationForm = None
4480 except AttributeError:
4481 self._registrationForm = None
4483 def recoverRegistrationForm(self, rf):
4484 self.setRegistrationForm(rf)
4485 rf.recover()
4487 def getEvaluation(self, id=0):
4488 ############################################################################
4489 #For the moment only one evaluation per conference is used. #
4490 #In the future if there are more than one evaluation, modify this function.#
4491 ############################################################################
4492 """ Return the evaluation given by its ID or None if nothing found.
4493 Params:
4494 id -- id of the wanted evaluation
4496 for evaluation in self.getEvaluations():
4497 if str(evaluation.getId()) == str(id) :
4498 return evaluation
4499 if Config.getInstance().getDebug():
4500 raise Exception(_("Error with id: expected '%s', found '%s'.")%(id, self.getEvaluations()[0].getId()))
4501 else:
4502 return self.getEvaluations()[0]
4504 def getEvaluations(self):
4505 if not hasattr(self, "_evaluations"):
4506 self._evaluations = [Evaluation(self)]
4507 return self._evaluations
4509 def setEvaluations(self, evaluationsList):
4510 self._evaluations = evaluationsList
4511 for evaluation in self._evaluations:
4512 evaluation.setConference(self)
4514 def removeEvaluation(self, evaluation):
4515 """remove the given evaluation from its evaluations."""
4516 evaluations = self.getEvaluations()
4517 if evaluations.count(evaluation)>0:
4518 evaluations.remove(evaluation)
4519 evaluation.removeReferences()
4520 self.notifyModification()
4522 def removeAllEvaluations(self):
4523 for evaluation in self.getEvaluations():
4524 evaluation.removeReferences()
4525 self._evaluations = []
4526 self.notifyModification()
4528 def _getEvaluationCounter(self):
4529 if not hasattr(self, "_evaluationCounter"):
4530 self._evaluationCounter = Counter()
4531 return self._evaluationCounter
4533 ## Videoconference bookings related
4534 def getBookings(self):
4535 try:
4536 if self._bookings:
4537 pass
4538 except AttributeError, e:
4539 self._bookings = {}
4540 self.notifyModification()
4541 return self._bookings
4543 def getBookingsList(self, sort = False):
4544 bl = self.getBookings().values()
4545 if sort:
4546 bl.sort()
4547 return bl
4549 def _getBookingGenerator(self):
4550 try:
4551 return self._bookingGenerator
4552 except AttributeError, e:
4553 self._bookingGenerator = Counter()
4554 return self._bookingGenerator
4556 def getNewBookingId(self):
4557 return str(self._getBookingGenerator().newCount())
4559 def addBooking(self, bp):
4560 if (bp.getId() == ""):
4561 bp.setId(self.getNewBookingId())
4562 self.getBookings()[bp.getId()] = bp
4563 self.notifyModification()
4565 def hasBooking(self,booking):
4566 return booking.getConference()==self and \
4567 self.getBookings().has_key(booking.getId())
4569 def removeBooking(self, booking):
4570 if self.hasBooking(booking):
4571 deletion= booking.deleteBooking()
4572 if deletion[0] != 1:
4573 del self.getBookings()[booking.getId()]
4574 self.notifyModification()
4575 return deletion
4577 def getBookingByType(self, type):
4578 if self.getBookings().has_key(type):
4579 return self.getBookings()[type]
4580 return None
4582 def getBookingById(self, id):
4583 if self.getBookings().has_key(id):
4584 return self.getBookings()[id]
4585 return None
4587 ## End of Videoconference bookings related
4589 def getRegistrants(self):
4590 try:
4591 if self._registrants:
4592 pass
4593 except AttributeError, e:
4594 self._registrants = {}
4595 self.notifyModification()
4596 return self._registrants
4598 def getRegistrantsByEmail(self, email=None):
4600 Returns the index of registrants by email OR a specific registrant if an email address
4601 is passed as argument.
4603 try:
4604 if self._registrantsByEmail:
4605 pass
4606 except AttributeError, e:
4607 self._registrantsByEmail = self._createRegistrantsByEmail()
4608 self.notifyModification()
4609 if email:
4610 return self._registrantsByEmail.get(email)
4611 return self._registrantsByEmail
4613 def _createRegistrantsByEmail(self):
4614 dicByEmail = {}
4615 for r in self.getRegistrantsList():
4616 dicByEmail[r.getEmail()] = r
4617 return dicByEmail
4619 def getRegistrantsList(self, sort = False):
4620 rl = self.getRegistrants().values()
4621 if sort:
4622 rl.sort(registration.Registrant._cmpFamilyName)
4623 return rl
4625 def _getRegistrantGenerator(self):
4626 try:
4627 return self._registrantGenerator
4628 except AttributeError, e:
4629 self._registrantGenerator = Counter()
4630 return self._registrantGenerator
4632 def addRegistrant(self, rp, user):
4633 rp.setId( str(self._getRegistrantGenerator().newCount()) )
4634 rp.setOwner( self )
4635 self.getRegistrants()[rp.getId()] = rp
4636 signals.event.registrant_changed.send(self, user=user, registrant=rp, action='added')
4637 self.notifyModification()
4639 def updateRegistrantIndexByEmail(self, rp, newEmail):
4640 oldEmail = rp.getEmail()
4641 if oldEmail != newEmail:
4642 if self.getRegistrantsByEmail().has_key(oldEmail):
4643 del self.getRegistrantsByEmail()[oldEmail]
4644 self.getRegistrantsByEmail()[newEmail] = rp
4645 self.notifyModification()
4647 def hasRegistrant(self,rp):
4648 return rp.getConference()==self and \
4649 self.getRegistrants().has_key(rp.getId())
4651 def hasRegistrantByEmail(self, email):
4652 # Return true if there is someone with the email of the param "email"
4653 return self.getRegistrantsByEmail().has_key(email)
4655 def removeRegistrant(self, id):
4656 part = self.getRegistrants()[id]
4657 self._registrationForm.notifyRegistrantRemoval(self.getRegistrants()[id])
4658 del self.getRegistrantsByEmail()[self.getRegistrantById(id).getEmail()]
4659 del self.getRegistrants()[id]
4660 signals.event.registrant_changed.send(self, user=part.getAvatar(), registrant=part, action='removed')
4661 TrashCanManager().add(part)
4662 self.notifyModification()
4664 def getRegistrantById(self, id):
4665 if self.getRegistrants().has_key(id):
4666 return self.getRegistrants()[id]
4667 return None
4669 def _getPrimAuthIndex(self):
4670 try:
4671 if self._primAuthIdx:
4672 pass
4673 except AttributeError:
4674 self._primAuthIdx=_PrimAuthIdx(self)
4675 return self._primAuthIdx
4677 def getContribsMatchingAuth(self,query,onlyPrimary=True):
4678 if str(query).strip()=="":
4679 return self.getContributionList()
4680 res=self._getPrimAuthIndex().match(query)
4681 return [self.getContributionById(id) for id in res]
4683 def getCoordinatedSessions( self, av ):
4684 """Returns a list with the sessions for which a user is coordinator.
4686 try:
4687 if self._sessionCoordinators:
4688 pass
4689 except AttributeError:
4690 self._sessionCoordinators = SCIndex()
4691 sessions = self._sessionCoordinators.getSessions( av )
4692 for session in self.getSessionList():
4693 if session not in sessions and av != None:
4694 for email in av.getEmails():
4695 if email in session.getCoordinatorEmailList():
4696 sessions.append(session)
4697 break
4698 return sessions
4700 def getManagedSession( self, av ):
4701 ls = []
4702 for session in self.getSessionList():
4703 pending = False
4704 if av != None:
4705 for email in av.getEmails():
4706 if email in session.getAccessController().getModificationEmail():
4707 pending = True
4708 break
4709 if av in session.getManagerList() or pending:
4710 ls.append(session)
4711 return ls
4713 def addSessionCoordinator(self,session,av):
4714 """Makes a user become coordinator for a session.
4716 try:
4717 if self._sessionCoordinators:
4718 pass
4719 except AttributeError:
4720 self._sessionCoordinators = SCIndex()
4721 if self.sessions.has_key(session.getId()):
4722 session.addCoordinator(av)
4723 self._sessionCoordinators.index(av,session)
4724 session._addCoordinatorEmail(av.getEmail())
4726 def removeSessionCoordinator( self, session, av ):
4727 """Removes a user as coordinator for a session.
4729 try:
4730 if self._sessionCoordinators:
4731 pass
4732 except AttributeError:
4733 self._sessionCoordinators = SCIndex()
4734 if self.sessions.has_key(session.getId()):
4735 session.removeCoordinator( av )
4736 self._sessionCoordinators.unindex(av,session)
4737 session.removeCoordinatorEmail(av.getEmail())
4739 def _getSubmitterIdx(self):
4740 try:
4741 return self._submitterIdx
4742 except AttributeError:
4743 self._submitterIdx=SubmitterIndex()
4744 return self._submitterIdx
4746 def addContribSubmitter(self,contrib,av):
4747 self._getSubmitterIdx().index(av,contrib)
4749 def removeContribSubmitter(self,contrib,av):
4750 self._getSubmitterIdx().unindex(av,contrib)
4752 def getContribsForSubmitter(self,av):
4753 return self._getSubmitterIdx().getContributions(av)
4755 def getBOAConfig(self):
4756 try:
4757 if self._boa:
4758 pass
4759 except AttributeError:
4760 self._boa=BOAConfig(self)
4761 return self._boa
4763 def getSessionCoordinatorRights(self):
4764 try:
4765 if self._sessionCoordinatorRights:
4766 pass
4767 except AttributeError, e:
4768 self._sessionCoordinatorRights = []
4769 self.notifyModification()
4770 return self._sessionCoordinatorRights
4772 def hasSessionCoordinatorRight(self, right):
4773 return right in self.getSessionCoordinatorRights()
4775 def addSessionCoordinatorRight(self, right):
4776 if SessionCoordinatorRights().hasRight(right) and not self.hasSessionCoordinatorRight(right):
4777 self._sessionCoordinatorRights.append(right)
4778 self.notifyModification()
4780 def removeSessionCoordinatorRight(self, right):
4781 if SessionCoordinatorRights().hasRight(right) and self.hasSessionCoordinatorRight(right):
4782 self._sessionCoordinatorRights.remove(right)
4783 self.notifyModification()
4785 def hasEnabledSection(self, section):
4786 # This hack is there since there is no more enable/disable boxes
4787 # in the conference managment area corresponding to those features.
4788 # Until the managment area is improved to get a more user-friendly
4789 # way of enabling/disabling those features, we always make them
4790 # available for the time being, but we keep the previous code for
4791 # further improvements
4792 return True
4794 def getPendingQueuesMgr(self):
4795 try:
4796 if self._pendingQueuesMgr:
4797 pass
4798 except AttributeError, e:
4799 self._pendingQueuesMgr=pendingQueues.ConfPendingQueuesMgr(self)
4800 return self._pendingQueuesMgr
4802 def getAccessController(self):
4803 return self.__ac
4805 def _cmpTitle( c1, c2 ):
4806 o1 = c1.getTitle().lower().strip()
4807 o2 = c2.getTitle().lower().strip()
4808 return cmp( o1, o2 )
4809 _cmpTitle=staticmethod(_cmpTitle)
4811 def getReportNumberHolder(self):
4812 try:
4813 if self._reportNumberHolder:
4814 pass
4815 except AttributeError, e:
4816 self._reportNumberHolder=ReportNumberHolder(self)
4817 return self._reportNumberHolder
4819 def setReportNumberHolder(self, rnh):
4820 self._reportNumberHolder=rnh
4822 def getBadgeTemplateManager(self):
4823 try:
4824 if self.__badgeTemplateManager:
4825 pass
4826 except AttributeError:
4827 self.__badgeTemplateManager = BadgeTemplateManager(self)
4828 return self.__badgeTemplateManager
4830 def setBadgeTemplateManager(self, badgeTemplateManager):
4831 self.__badgeTemplateManager = badgeTemplateManager
4833 def getPosterTemplateManager(self):
4834 try:
4835 if self.__posterTemplateManager:
4836 pass
4837 except AttributeError:
4838 self.__posterTemplateManager = PosterTemplateManager(self)
4840 return self.__posterTemplateManager
4842 def setPosterTemplateManager(self, posterTemplateManager):
4843 self.__posterTemplateManager = posterTemplateManager
4845 class DefaultConference(Conference):
4846 """ 'default' conference, which stores the
4847 default templates for posters and badges
4850 def indexConf(self):
4851 pass
4853 def __init__(self):
4854 admin = User.find_first(is_admin=True)
4855 if admin is None:
4856 raise MaKaCError(_("""There are no admin users. The "default" conference that stores the template cannot be created.
4857 Please add at least 1 user to the admin list."""))
4858 Conference.__init__(self, admin.as_avatar, "default")
4861 class ConferenceHolder( ObjectHolder ):
4862 """Specialised ObjectHolder dealing with conference objects. It gives a
4863 common entry point and provides simple methods to access and
4864 maintain the collection of stored conferences (DB).
4866 idxName = "conferences"
4867 counterName = "CONFERENCE"
4869 def _newId( self ):
4870 id = ObjectHolder._newId( self )
4871 return "%s"%id
4873 def getById( self, id, quiet=False ):
4874 """returns an object from the index which id corresponds to the one
4875 which is specified.
4878 if (id == "default"):
4879 return CategoryManager().getDefaultConference()
4881 if type(id) is int:
4882 id = str(id)
4883 if self._getIdx().has_key(str(id)):
4884 return self._getIdx()[str(id)]
4885 elif quiet:
4886 return None
4887 else:
4888 raise NotFoundError(_("The event with id '{}' does not exist or has been deleted").format(id),
4889 title=_("Event not found"))
4892 class Observer(object):
4893 """ Base class for Observer objects.
4894 Inheriting classes should overload the following boolean class attributes:
4895 _shouldBeTitleNotified
4896 _shouldBeDateChangeNotified
4897 _shouldBeLocationChangeNotified
4898 _shouldBeDeletionNotified
4899 And set them to True if they want to be notified of the corresponding event.
4900 In that case, they also have to implement the corresponding methods:
4901 _notifyTitleChange (for title notification)
4902 _notifyEventDateChanges and _notifyTimezoneChange (for date / timezone notification)
4903 _shouldBeLocationChangeNotified (for location notification)
4904 _notifyDeletion (for deletion notification).
4905 The interface for those methods is also specified in this class. If the corresponding
4906 class attribute is set to False but the method is not implemented, an exception will be thrown.
4908 _shouldBeTitleNotified = False
4909 _shouldBeDateChangeNotified = False
4910 _shouldBeLocationChangeNotified = False
4911 _shouldBeDeletionNotified = False
4913 def getObserverName(self):
4914 name = "'Observer of class" + self.__class__.__name__
4915 try:
4916 conf = self.getOwner()
4917 name = name + " of event " + conf.getId() + "'"
4918 except AttributeError:
4919 pass
4920 return name
4922 def notifyTitleChange(self, oldTitle, newTitle):
4923 if self._shouldBeTitleNotified:
4924 self._notifyTitleChange(oldTitle, newTitle)
4926 def notifyEventDateChanges(self, oldStartDate = None, newStartDate = None, oldEndDate = None, newEndDate = None):
4927 if self._shouldBeDateChangeNotified:
4928 self._notifyEventDateChanges(oldStartDate, newStartDate, oldEndDate, newEndDate)
4930 def notifyTimezoneChange(self, oldTimezone, newTimezone):
4931 if self._shouldBeDateChangeNotified:
4932 self._notifyTimezoneChange(oldTimezone, newTimezone)
4934 def notifyLocationChange(self, newLocation):
4935 if self._shouldBeLocationChangeNotified:
4936 self._notifyLocationChange(newLocation)
4938 def notifyDeletion(self):
4939 if self._shouldBeDeletionNotified:
4940 self._notifyDeletion()
4942 def _notifyTitleChange(self, oldTitle, newTitle):
4943 """ To be implemented by inheriting classes
4944 Notifies the observer that the Conference object's title has changed
4946 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method _notifyTitleChange")
4948 def _notifyEventDateChanges(self, oldStartDate, newStartDate, oldEndDate, newEndDate):
4949 """ To be implemented by inheriting classes
4950 Notifies the observer that the start and / or end dates of the object it is attached to has changed.
4951 If the observer finds any problems during whatever he needs to do as a consequence of
4952 the event dates changing, he should write strings describing the problems
4953 in the 'dateChangeNotificationProblems' context variable (which is a list of strings).
4955 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyStartDateChange")
4957 def _notifyTimezoneChange(self, oldTimezone, newTimezone):
4958 """ To be implemented by inheriting classes.
4959 Notifies the observer that the end date of the object it is attached to has changed.
4960 This method has to return a list of strings describing problems encountered during
4961 whatever the DateChangeObserver object does as a consequence of the notification.
4962 If there are no problems, the DateChangeObserver should return an empty list.
4964 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyTimezoneChange")
4966 def _notifyLocationChange(self):
4967 """ To be implemented by inheriting classes
4968 Notifies the observer that the location of the object it is attached to has changed.
4970 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyLocationChange")
4972 def _notifyDeletion(self):
4973 """ To be implemented by inheriting classes
4974 Notifies the observer that the Conference object it is attached to has been deleted
4976 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyDeletion")
4978 class TitleChangeObserver(Observer):
4979 """ Base class for objects who want to be notified of a Conference object being deleted.
4980 Inheriting classes have to implement the notifyTitleChange method, and probably the __init__ method too.
4983 def notifyTitleChange(self, oldTitle, newTitle):
4984 """ To be implemented by inheriting classes
4985 Notifies the observer that the Conference object's title has changed
4987 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyTitleChange")
4990 class SessionChair(ConferenceParticipation):
4992 def __init__(self):
4993 self._session=None
4994 self._id=""
4995 ConferenceParticipation.__init__(self)
4997 def _notifyModification( self ):
4998 if self._session != None:
4999 self._session.notifyModification()
5001 def clone(self):
5002 chair = SessionChair()
5003 chair.setValues(self.getValues())
5004 return chair
5006 def getSession(self):
5007 return self._session
5009 def getConference(self):
5010 s=self.getSession()
5011 if s is None:
5012 return None
5013 return s.getConference()
5015 def includeInSession(self,session,id):
5016 if self.getSession()==session and self.getId()==id.strip():
5017 return
5018 self._session=session
5019 self._id=id
5021 def delete( self ):
5022 self._session=None
5023 ConferenceParticipation.delete(self)
5025 def getLocator(self):
5026 if self.getSession() is None:
5027 return None
5028 loc=self.getSession().getLocator()
5029 loc["convId"]=self.getId()
5030 return loc
5032 def isSessionManager(self):
5033 # pendings managers
5034 if self.getEmail() in self._session.getAccessController().getModificationEmail():
5035 return True
5036 # managers list
5037 for manager in self._session.getManagerList():
5038 if self.getEmail() == manager.getEmail():
5039 return True
5040 return False
5042 def isSessionCoordinator(self):
5043 # pendings coordinators
5044 if self.getEmail() in self._session.getConference().getPendingQueuesMgr().getPendingCoordinatorsKeys():
5045 return True
5046 # coordinator list
5047 for coord in self._session.getCoordinatorList():
5048 if self.getEmail() == coord.getEmail():
5049 return True
5050 return False
5053 class SlotChair(ConferenceParticipation):
5055 def __init__(self):
5056 self._slot=None
5057 self._id=""
5058 ConferenceParticipation.__init__(self)
5060 def _notifyModification( self ):
5061 if self._slot != None:
5062 self._slot.notifyModification()
5064 def clone(self):
5065 chair = SlotChair()
5066 chair.setValues(self.getValues())
5067 return chair
5069 def getSlot(self):
5070 return self._slot
5072 def getSession(self):
5073 s=self.getSlot()
5074 if s is None:
5075 return None
5076 return s.getSession()
5078 def getConference(self):
5079 s=self.getSlot()
5080 if s is None:
5081 return None
5082 return s.getConference()
5084 def includeInSlot(self,slot,id):
5085 if self.getSlot()==slot and self.getId()==id.strip():
5086 return
5087 self._slot=slot
5088 self._id=id
5090 def delete( self ):
5091 self._slot=None
5092 ConferenceParticipation.delete(self)
5094 def getLocator(self):
5095 if self.getSlot() is None:
5096 return None
5097 loc=self.getSlot().getLocator()
5098 loc["convId"]=self.getId()
5099 return loc
5101 class SessionCoordinatorRights:
5103 def __init__(self):
5104 self._rights = {"modifContribs": "Modify the contributions",
5105 "unrestrictedSessionTT": "Unrestricted session timetable management"
5108 def hasRight(self, r):
5109 return self._rights.has_key(r)
5111 def getRights(self):
5112 return self._rights
5114 def getRightList(self, sort=False):
5115 l=self._rights.values()
5116 if sort:
5117 l.sort()
5118 return l
5120 def getRightKeys(self):
5121 return self._rights.keys()
5123 def getRight(self, id):
5124 if self._rights.has_key(id):
5125 return self._rights[id]
5126 return None
5128 class SCIndex(Persistent):
5129 """Index for conference session coordinators.
5131 This class allows to index conference session coordinators so the owner
5132 can answer optimally to the query if a user is coordinating
5133 any conference session.
5134 It is implemented by simply using a BTree where the Avatar id is used
5135 as key (because it is unique and non variable) and a list of
5136 coordinated sessions is kept as keys. It is the responsability of the
5137 index owner (conference) to keep it up-to-date i.e. notify session
5138 coordinator additions and removals.
5141 def __init__( self ):
5142 self._idx=OOBTree()
5145 def getSessions(self,av):
5146 """Gives a list with the sessions a user is coordinating.
5148 if av == None:
5149 return []
5150 return self._idx.get(av.getId(),[])
5152 def index(self,av,session):
5153 """Registers in the index a coordinator of a session.
5155 if av == None or session == None:
5156 return
5157 if not self._idx.has_key(av.getId()):
5158 l=[]
5159 self._idx[av.getId()]=l
5160 else:
5161 l=self._idx[av.getId()]
5162 if session not in l:
5163 l.append(session)
5164 self.notifyModification()
5166 def unindex(self,av,session):
5167 if av==None or session==None:
5168 return
5169 l=self._idx.get(av.getId(),[])
5170 if session in l:
5171 l.remove(session)
5172 self.notifyModification()
5174 def notifyModification(self):
5175 self._idx._p_changed=1
5178 class Session(CommonObjectBase, Locatable):
5179 """This class implements a conference session, being the different parts
5180 in which the conference can be divided and the contributions can be
5181 organised in. The class contains necessary attributes to store session
5182 basic data and provides the operations related to sessions. In
5183 principle, a session has no sense to exist without being related to a
5184 conference but it is allowed for flexibility.
5187 fossilizes(ISessionFossil)
5190 def __init__(self, **sessionData):
5191 """Class constructor. Initialise the class attributes to the default
5192 values.
5193 Params:
5194 sessionData -- (Dict) Contains the data the session object has to
5195 be initialised to.
5197 self.conference=None
5198 self.id="not assigned"
5199 self.title=""
5200 self.description=""
5201 #################################
5202 # Fermi timezone awareness #
5203 #################################
5204 self.startDate = nowutc()
5205 #################################
5206 # Fermi timezone awareness(end) #
5207 #################################
5209 self.duration=timedelta(minutes=1)
5210 self.places=[]
5211 self.rooms=[]
5212 self.conveners=[] # This attribute must not be used and should disappear someday
5213 self._conveners=[]
5214 self._convenerGen=Counter()
5215 self.convenerText=""
5216 self.contributions={}
5217 self._contributionDuration=timedelta(minutes=20)
5218 self.__ac=AccessController(self)
5219 self.materials={}
5220 self.__materialGenerator=Counter()
5221 self.minutes=None
5222 self._comments = ""
5223 self.slots={}
5224 self.__slotGenerator=Counter()
5225 self._setSchedule()
5226 self._coordinators=OOBTree()
5227 self._coordinatorsEmail = []
5228 self._code=""
5229 self._color="#e3f2d3"
5230 self._textColor="#202020"
5231 self._textColorToLinks=False
5232 self._ttType=SlotSchTypeFactory.getDefaultId()
5233 self._closed = False
5234 self._registrationSession = None
5235 self._creationDS = nowutc()
5236 self._modificationDS = nowutc()
5237 self._keywords = ""
5239 def __cmp__(self, other):
5240 if type(self) is not type(other):
5241 # This is actually dangerous and the ZODB manual says not to do this
5242 # because it relies on memory order. However, this branch should never
5243 # be taken anyway since we do not store different types in the same set
5244 # or use them as keys.
5245 return cmp(hash(self), hash(other))
5246 if self.getConference() == other.getConference():
5247 return cmp(self.getId(), other.getId())
5248 return cmp(self.getConference(), other.getConference())
5250 def getVerboseType(self):
5251 return 'Session'
5253 def getTimezone( self ):
5254 return self.getConference().getTimezone()
5256 def updateNonInheritingChildren(self, elem, delete=False, propagate=True):
5257 self.getAccessController().updateNonInheritingChildren(elem, delete)
5258 if propagate == True:
5259 self.notify_protection_to_owner(elem, delete)
5261 def notify_protection_to_owner(self, elem, delete=False):
5262 """ This methods notifies the owner that the protection has been changed,
5263 so it can update its list of non inheriting children """
5264 self.getOwner().updateNonInheritingChildren(elem, delete)
5266 def getKeywords(self):
5267 try:
5268 return self._keywords
5269 except:
5270 self._keywords = ""
5271 return ""
5273 def setKeywords(self, keywords):
5274 self._keywords = keywords
5276 def notifyModification( self, raiseEvent = True, date = None, cleanCache = True ):
5277 """Method called to notify the current session has been modified.
5279 self.setModificationDate(date)
5281 parent = self.getConference()
5282 if parent:
5283 parent.setModificationDate(date)
5284 if cleanCache:
5285 for slot in self.getSlotList():
5286 slot.cleanCache()
5287 self._p_changed=1
5289 def getModificationDate( self ):
5290 """Returns the date in which the session was last modified"""
5291 try:
5292 return self._modificationDS
5293 except:
5294 self._modificationDS = nowutc()
5295 return self._modificationDS
5297 def getCreationDate( self ):
5298 """Returns the date in which the session was created"""
5299 try:
5300 return self._creationDS
5301 except:
5302 self._creationDS = nowutc()
5303 return self._creationDS
5305 def getLogInfo(self):
5306 data = {}
5307 data["subject"] = self.title
5308 data["session id"] = self.id
5309 data["session code"] = self._code
5310 data["title"] = self.title
5311 data["description"] = self.description
5312 data["start date"] = self.startDate
5313 data["duration"] = self.duration
5314 for p in self.places :
5315 data["place"] = p.getName()
5316 for r in self.rooms :
5317 data["room"] = r.getName()
5318 for sc in self.getConvenerList() :
5319 data["convener %s"%sc.getId()] = sc.getFullName()
5320 for co in self.getCoordinatorList() :
5321 data["coordinators %s"%co.getId()] = co.getFullName()
5323 return data
5325 def getEnableSessionSlots(self):
5326 try:
5327 return self.getConference().getEnableSessionSlots()
5328 except:
5329 return True
5331 def cmpSessionByTitle(session1, session2):
5332 return cmp(session1.getTitle(), session2.getTitle())
5333 cmpSessionByTitle = staticmethod(cmpSessionByTitle)
5335 def hasRegistrationSession(self):
5336 return self.getRegistrationSession() is not None
5338 def getRegistrationSession(self):
5339 try:
5340 if self._registrationSession:
5341 pass
5342 except AttributeError, e:
5343 self._registrationSession = None
5344 return self._registrationSession
5346 def setRegistrationSession(self, rs):
5347 self._registrationSession = rs
5349 def isClosed( self ):
5350 if self.getConference().isClosed():
5351 return True
5352 try:
5353 return self._closed
5354 except:
5355 self._closed = False
5356 return False
5358 def setClosed( self, closed=True ):
5359 self._closed = closed
5360 self.notifyModification(cleanCache = False)
5362 def includeInConference(self,conf,newId):
5363 self.conference=conf
5364 self.id=newId
5365 for slot in self.getSlotList():
5366 conf.getSchedule().addEntry(slot.getConfSchEntry(),2)
5367 self.getConference().addSession(self)
5368 self.notifyModification()
5370 def delete(self):
5371 while len(self.getConvenerList()) > 0:
5372 self.removeConvener(self.getConvenerList()[0])
5373 while len(self.getMaterialList()) > 0:
5374 self.removeMaterial(self.getMaterialList()[0])
5375 self.removeMinutes()
5376 for c in self.getCoordinatorList()[:]:
5377 self.removeCoordinator(c)
5378 while len(self.contributions.values())>0:
5379 self.removeContribution(self.contributions.values()[0])
5380 while len(self.slots.values())>0:
5381 self._removeSlot(self.slots.values()[0])
5382 if self.getConference() is not None:
5383 self.getConference().removeSession(self)
5384 if self.hasRegistrationSession():
5385 self.getConference().getRegistrationForm().getSessionsForm().removeSession(self.getId())
5386 self.getRegistrationSession().setRegistrationForm(None)
5387 TrashCanManager().add(self.getRegistrationSession())
5388 self.notify_protection_to_owner(self, delete=True)
5389 self.conference=None
5390 TrashCanManager().add(self)
5392 def recover(self, isCancelled):
5393 if self.hasRegistrationSession():
5394 if not isCancelled:
5395 self.getRegistrationSession().setRegistrationForm(self.getConference().getRegistrationForm())
5396 self.getConference().getRegistrationForm().getSessionsForm().addSession(self.getRegistrationSession())
5397 TrashCanManager().remove(self.getRegistrationSession())
5398 TrashCanManager().remove(self)
5400 def getLocator( self ):
5401 """Gives back a globaly unique identification encapsulated in a Locator
5402 object for the session instance
5404 if self.conference == None:
5405 return Locator()
5406 lconf = self.conference.getLocator()
5407 lconf["sessionId"] = self.getId()
5408 return lconf
5410 def getConference( self ):
5411 return self.conference
5413 def getSession( self ):
5414 return self
5416 def getOwner( self ):
5417 return self.getConference()
5419 def getId( self ):
5420 return self.id
5422 def getUniqueId( self ):
5423 """returns (string) the unique identiffier of the item"""
5424 """used mainly in the web session access key table"""
5425 return "%ss%s" % (self.getConference().getUniqueId(),self.id)
5427 def getModifKey( self ):
5428 return self.getConference().getModifKey()
5430 def getAccessKey( self ):
5431 return self.getConference().getAccessKey()
5433 def getContribDuration(self):
5434 try:
5435 return self._contributionDuration
5436 except:
5437 self._contributionDuration = timedelta(minutes=20)
5438 return self._contributionDuration
5440 def setContribDuration(self, hour=0, min=20, dur=None):
5441 if dur is not None:
5442 self._contributionDuration=dur
5443 else:
5444 self._contributionDuration = timedelta(hours=hour,minutes=min)
5446 def fit(self):
5447 #if not self.getConference().getEnableSessionSlots():
5448 # self.getSlotList()[0].fit()
5449 self.setStartDate(self.getMinSlotStartDate(),0,0)
5450 self.setEndDate(self.getMaxSlotEndDate(),0)
5452 def addSlot(self,newSlot):
5453 id = newSlot.getId()
5454 if id == "not assigned":
5455 newSlot.setId(str(self.__slotGenerator.newCount()))
5456 self.slots[newSlot.getId()]=newSlot
5457 self.fit()
5458 self.getSchedule().addEntry(newSlot.getSessionSchEntry(),2)
5459 if self.getConference() is not None:
5460 self.getConference().getSchedule().addEntry(newSlot.getConfSchEntry(),2)
5461 self.notifyModification()
5463 def _removeSlot(self,slot):
5464 del self.slots[slot.getId()]
5465 self.getSchedule().removeEntry(slot.getSessionSchEntry())
5466 if self.getConference() is not None:
5467 self.getConference().getSchedule().removeEntry(slot.getConfSchEntry())
5468 slot.delete()
5470 def removeSlot(self, slot, force=False):
5471 if self.slots.has_key(slot.getId()):
5472 if len(self.slots)==1 and not force:
5473 raise MaKaCError( _("A session must have at least one slot"), _("Session"))
5474 self._removeSlot(slot)
5475 self.fit()
5476 self.notifyModification()
5478 def recoverSlot(self, slot):
5479 self.addSlot(slot)
5480 slot.recover()
5482 def getSlotById(self,slotId):
5483 return self.slots.get(slotId,None)
5485 def getSlotList(self):
5486 return self.slots.values()
5488 def getSortedSlotList(self):
5489 sl = self.getSlotList()
5490 sl.sort(utils.sortSlotByDate)
5491 return sl
5493 def getMinSlotStartTime(self):
5494 min = (25,61)
5495 for slot in self.getSlotList():
5496 if slot.isMoreThanDay():
5497 return (0,0)
5498 shour = slot.getStartDate().hour
5499 smin = slot.getStartDate().minute
5500 if (shour, smin) < min:
5501 min = (shour, smin)
5502 return min
5504 def getMaxSlotEndTime(self):
5505 max = (-1,-1)
5506 for slot in self.getSlotList():
5507 if slot.isMoreThanDay():
5508 return (23, 59)
5509 endDate = slot.getEndDate()
5510 if (endDate.hour, endDate.minute) > max:
5511 newEndDate = endDate - timedelta(0, 0, 0)
5512 max = (newEndDate.hour, newEndDate.minute)
5513 return max
5515 def getMinSlotStartDate(self):
5516 slotList = self.getSlotList()
5517 if len(slotList)==0:
5518 return self.getStartDate()
5519 else:
5520 sDate = self.getEndDate()
5521 for slot in slotList:
5522 if slot.getStartDate() < sDate:
5523 sDate = slot.getStartDate()
5524 return sDate
5526 def getMaxSlotEndDate(self):
5527 slotList = self.getSlotList()
5528 if len(slotList)==0:
5529 return self.getEndDate()
5530 else:
5531 eDate = self.getStartDate()
5532 for slot in slotList:
5533 if slot.getEndDate() > eDate:
5534 eDate = slot.getEndDate()
5535 return eDate
5537 def _getCorrectColor(self, color):
5538 if not color.startswith("#"):
5539 color = "#%s"%color
5540 m = re.match("^#[0-9A-Fa-f]{6}$", color)
5541 if m:
5542 return color
5543 return None
5545 def _getCorrectBgColor(self, color):
5546 color=self._getCorrectColor(color)
5547 if color is None:
5548 return self._color
5549 return color
5551 def _getCorrectTextColor(self, color):
5552 color=self._getCorrectColor(color)
5553 if color is None:
5554 return self._textColor
5555 return color
5557 def setValues( self, sessionData,check=2,moveEntries=0 ):
5558 """Sets all the values of the current session object from a dictionary
5559 containing the following key-value pairs:
5560 title-(str)
5561 description-(str)
5562 locationName-(str) => name of the location, if not specified
5563 it will be set to the conference location name.
5564 locationAddress-(str)
5565 roomName-(str) => name of the room, if not specified it will
5566 be set to the conference room name.
5567 sDate - (datetime) => starting date of the session, if not
5568 specified it will be set to now.
5569 eDate - (datetime) => ending date of the session, if not
5570 specified the end date will be set to the start one
5571 durHour - (int) => hours of duration for each entry in the session
5572 by default.
5573 durMin - (int) => hours of duration for each entry in the session
5574 by default.
5575 _conveners - (str)
5576 check parameter:
5577 0: no check at all
5578 1: check and raise error in case of problem
5579 2: check and adapt the owner dates
5580 Please, note that this method sets ALL values which means that if
5581 the given dictionary doesn't contain any of the keys the value
5582 will set to a default value.
5585 self.setTitle( sessionData.get("title", "NO TITLE ASSIGNED") )
5586 self.setDescription( sessionData.get("description", "") )
5587 code = sessionData.get("code", "")
5588 if code.strip() == "":
5589 if self.getId()=="not assigned":
5590 self.setCode("no code")
5591 else:
5592 self.setCode(self.getId())
5593 else:
5594 self.setCode(code)
5595 bgcolor = sessionData.get("backgroundColor", "")
5596 if bgcolor.strip() != "":
5597 self.setColor(self._getCorrectBgColor(bgcolor))
5598 textcolor = sessionData.get("textColor", "")
5599 if textcolor.strip() != "":
5600 if sessionData.has_key("autotextcolor"):
5601 self.setTextColor(utils.getTextColorFromBackgroundColor(self.getColor()))
5602 else:
5603 self.setTextColor(self._getCorrectTextColor(textcolor))
5604 self.setTextColorToLinks(sessionData.has_key("textcolortolinks"))
5606 if "locationName" in sessionData:
5607 loc = self.getOwnLocation()
5608 if not loc:
5609 loc = CustomLocation()
5610 self.setLocation( loc )
5611 loc.setName( sessionData["locationName"] )
5612 loc.setAddress( sessionData.get("locationAddress", "") )
5613 else:
5614 self.setLocation(None)
5616 #same as for the location
5617 if "roomName" in sessionData:
5618 room = self.getOwnRoom()
5619 if not room:
5620 room = CustomRoom()
5621 self.setRoom( room )
5622 room.setName( sessionData["roomName"] )
5623 else:
5624 self.setRoom(None)
5626 if sessionData.get("sDate",None) is not None:
5627 self.setStartDate(sessionData["sDate"],check,moveEntries=moveEntries)
5628 if sessionData.get("eDate",None) is not None:
5629 self.setEndDate(sessionData["eDate"],check)
5630 self._checkInnerSchedule()
5631 if sessionData.get("contribDuration","")!="":
5632 self._contributionDuration = sessionData.get("contribDuration")
5633 else:
5634 self._contributionDuration = timedelta(hours=int(sessionData.get("durHour",0)), minutes=int(sessionData.get("durMin",20)))
5635 self.notifyModification()
5637 def move(self, sDate):
5639 Move a session from the old start date to a new start date, and
5640 it moves all the entries of the session as well, without date validations.
5642 if sDate is not None:
5643 oldStartDate=self.startDate
5644 self.startDate=copy.copy(sDate)
5645 diff=self.startDate-oldStartDate
5646 # Check date to not be prior conference start date and to not surpass conference end date
5647 # The schedule is returning the datetime object as timezone aware relative to the conference
5648 # timezone. Must adjust the startdate accordingly for comparison. JMF
5649 conftz = self.getConference().getTimezone()
5650 if self.getStartDate() < self.getConference().getSchedule().getStartDate() or \
5651 self.getEndDate() > self.getConference().getSchedule().getEndDate():
5652 raise MaKaCError( _("Impossible to move the session because it would be out of the conference dates"))
5653 for entry in self.getSchedule().getEntries():
5654 if isinstance(entry,LinkedTimeSchEntry) and \
5655 isinstance(entry.getOwner(), SessionSlot):
5656 e = entry.getOwner()
5657 e.move(e.getStartDate() + diff)
5658 self.getSchedule().reSchedule()
5659 self.getConference().getSchedule().reSchedule()
5660 self.notifyModification()
5662 def clone(self, deltaTime, conf, options, session_id=None):
5663 ses = Session()
5664 conf.addSession(ses, check=0, id=session_id)
5665 ses.setTitle(self.getTitle())
5666 ses.setDescription(self.getDescription())
5667 startDate = self.getStartDate() + deltaTime
5668 ses.setStartDate(startDate, check=1)
5669 ses.setDuration(dur=self.getDuration())
5671 if self.getOwnLocation() is not None:
5672 ses.addLocation(self.getOwnLocation().clone())
5673 if self.getOwnRoom() is not None:
5674 ses.setRoom(self.getOwnRoom().clone())
5675 ses.setColor(self.getColor())
5676 ses.setTextColor(self.getTextColor())
5677 ses.setTextColorToLinks(self.isTextColorToLinks())
5678 ses.setCode(self.getCode())
5679 ses.setContribDuration(dur=self.getContribDuration())
5680 ses.setScheduleType(self.getScheduleType())
5681 ses.setComments(self.getComments())
5683 # Access Control cloning
5684 if options.get("access", False) :
5685 ses.setProtection(self.getAccessController()._getAccessProtection())
5686 for mgr in self.getManagerList() :
5687 ses.grantModification(mgr)
5688 for user in self.getAllowedToAccessList() :
5689 ses.grantAccess(user)
5690 for domain in self.getDomainList():
5691 ses.requireDomain(domain)
5692 for coord in self.getCoordinatorList():
5693 ses.addCoordinator(coord)
5695 #slots in timeschedule
5696 for slot in self.getSlotList() :
5697 newslot = slot.clone(ses, options)
5698 ses.addSlot(newslot)
5699 ContextManager.setdefault("clone.unique_id_map", {})[slot.getUniqueId()] = newslot.getUniqueId()
5701 ses.notifyModification()
5703 return ses
5706 def setTitle( self, newTitle ):
5707 self.title = newTitle
5708 self.notifyModification()
5710 def getTitle( self ):
5711 return self.title
5713 def setDescription(self, newDescription ):
5714 self.description = newDescription
5715 self.notifyModification()
5717 def getDescription(self):
5718 return self.description
5720 def getCode(self):
5721 try:
5722 if self._code:
5723 pass
5724 except AttributeError:
5725 self._code=self.id
5726 return self._code
5728 def setCode(self,newCode):
5729 self._code=str(newCode).strip()
5731 def getColor(self):
5732 try:
5733 if self._color:
5734 pass
5735 except AttributeError:
5736 self._color="#e3f2d3"
5737 return self._color
5738 getBgColor=getColor
5740 def setColor(self,newColor):
5741 self._color=str(newColor).strip()
5742 self.notifyModification()
5743 setBgColor=setColor
5745 def getTextColor(self):
5746 try:
5747 if self._textColor:
5748 pass
5749 except AttributeError:
5750 self._textColor="#202020"
5751 return self._textColor
5753 def setTextColor(self,newColor):
5754 self._textColor=str(newColor).strip()
5755 self.notifyModification()
5757 def isTextColorToLinks(self):
5758 try:
5759 if self._textColorToLink:
5760 pass
5761 except AttributeError:
5762 self._textColorToLink=False
5763 return self._textColorToLink
5765 def setTextColorToLinks(self, v):
5766 self._textColorToLink=v
5767 self.notifyModification()
5769 def getStartDate(self):
5770 return self.startDate
5772 def getAdjustedStartDate(self,tz=None):
5773 if not tz:
5774 tz = self.getConference().getTimezone()
5775 if tz not in all_timezones:
5776 tz = 'UTC'
5777 return self.startDate.astimezone(timezone(tz))
5779 def verifyStartDate(self, sdate, check=2):
5780 """check parameter:
5781 0: no check at all
5782 1: check and raise error in case of problem (default)
5783 2: check and adapt the owner dates
5786 conf=self.getConference()
5788 if conf is not None and sdate < conf.getSchedule().getStartDate():
5789 if check==1:
5790 raise ParentTimingError( _("The session starting date cannot be prior to the event starting date"), _("Session"))
5791 elif check==2:
5792 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
5793 conf, sdate.astimezone(timezone(conf.getTimezone()))))
5794 conf.setStartDate(sdate,check=0,moveEntries=0)
5796 def setStartDate(self,newDate,check=2,moveEntries=0):
5798 moveEntries parameter:
5799 0: do not move inner slots
5800 1: move
5801 2: do not move but check that session is not out of the conference dates
5804 if not newDate.tzname():
5805 raise MaKaCError("date should be timezone aware")
5806 if check != 0:
5807 self.verifyStartDate(newDate,check)
5808 oldSdate = self.getStartDate()
5809 try:
5810 tz = str(self.getStartDate().tzinfo)
5811 except:
5812 tz = 'UTC'
5813 diff = newDate - oldSdate
5814 self.startDate=copy.copy(newDate)
5815 if moveEntries == 1 and diff is not None and diff != timedelta(0):
5816 # If the start date changed, we move entries inside the timetable
5817 newDateTz = newDate.astimezone(timezone(tz))
5818 if oldSdate.astimezone(timezone(tz)).date() != newDateTz.date():
5819 entries = self.getSchedule().getEntries()[:]
5820 else:
5821 entries = self.getSchedule().getEntriesOnDay(newDateTz)[:]
5822 self.getSchedule().moveEntriesBelow(diff, entries)
5824 if moveEntries != 0 and self.getConference() and \
5825 not self.getConference().getEnableSessionSlots() and \
5826 self.getSlotList() != [] and \
5827 self.getSlotList()[0].getStartDate() != newDate:
5828 self.getSlotList()[0].startDate = newDate
5830 if check == 1:
5831 self._checkInnerSchedule()
5832 self.notifyModification()
5834 def _checkInnerSchedule( self ):
5835 self.getSchedule().checkSanity()
5837 def getEndDate(self):
5838 return self.startDate+self.duration
5840 ####################################
5841 # Fermi timezone awareness #
5842 ####################################
5844 def getAdjustedEndDate(self,tz=None):
5845 return self.getAdjustedStartDate(tz) + self.duration
5847 ####################################
5848 # Fermi timezone awareness(end) #
5849 ####################################
5851 def verifyEndDate(self, edate,check=1):
5852 """check parameter:
5853 0: no check at all
5854 1: check and raise error in case of problem
5855 2: check and adapt the owner dates
5857 try:
5858 tz = timezone(self.getConference().getTimezone())
5859 except:
5860 tz = timezone('UTC')
5861 # compare end date with start date
5862 if edate<=self.getStartDate():
5863 if check == 1:
5864 raise MaKaCError( _("End date cannot be prior to the Start date"), _("Session"))
5865 if check == 2:
5866 self.setStartDate(edate)
5867 # check conference dates
5868 if (self.getConference()):
5869 conf=self.getConference()
5870 confStartDate = conf.getSchedule().getStartDate()
5871 confEndDate = conf.getSchedule().getEndDate()
5872 if conf is not None and (edate>confEndDate or edate<=confStartDate):
5873 if check==1:
5874 raise ParentTimingError( _("The end date has to be between the event dates (%s - %s)")%\
5875 (confStartDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
5876 confEndDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
5877 _("Session"))
5878 if check==2:
5879 if edate>confEndDate:
5880 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
5881 self.getConference(),
5882 edate.astimezone(tz)))
5883 self.getConference().setEndDate(edate)
5884 if edate<=confStartDate:
5885 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
5886 self.getConference(),
5887 edate.astimezone(tz)))
5888 self.getConference().setStartDate(edate)
5889 # check inner schedule
5890 if len(self.getSlotList()) != 0 and self.getSlotList()[-1].getSchedule().hasEntriesAfter(edate):
5891 raise TimingError( _("Cannot change end date: some entries in the session schedule end after the new date"), _("Session"))
5893 def setEndDate(self,newDate,check=2):
5894 if not newDate.tzname():
5895 raise MaKaCError("date should be timezone aware")
5896 if check != 0:
5897 self.verifyEndDate(newDate,check)
5898 self.duration=newDate-self.getStartDate()
5899 # A session is not always linked to a conference (for eg. at creation time)
5900 #if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSlotList()[0].getEndDate() != newDate:
5901 # self.getSlotList()[0].duration = self.duration
5902 self.notifyModification()
5904 def setDates(self, sDate, eDate, check=1, moveEntries=0):
5905 if eDate <= sDate:
5906 tz = timezone(self.getConference().getTimezone())
5907 raise FormValuesError(_("The end date ({}) cannot be prior to the start date ({})").format(
5908 eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),
5909 _("Session"))
5910 self.setStartDate(sDate, check, moveEntries)
5911 self.setEndDate(eDate, check)
5912 self._checkInnerSchedule()
5914 def getDuration(self):
5915 return self.duration
5917 def setDuration(self, hours=0, minutes=15, dur=0):
5918 if dur == 0:
5919 dur = timedelta(hours=int(hours), minutes=int(minutes))
5920 if dur.seconds <= 0:
5921 raise FormValuesError(_("The duration cannot be less than zero"), _("Session"))
5922 self.duration = dur
5923 self.verifyEndDate(self.getEndDate())
5924 self.notifyModification()
5926 def getStartOnDay(self, day, tz=None):
5927 if not tz:
5928 tz = self.getConference().getTimezone()
5929 if type(day) is datetime:
5930 day = day.astimezone(timezone(tz))
5931 if day.date() < self.getStartDate().astimezone(timezone(tz)).date() or day.date() > self.getEndDate().astimezone(timezone(tz)).date() :
5932 return None
5933 minTime = self.getEndDate()
5934 for e in self.getSchedule().getEntriesOnDay(day) :
5935 if e.getStartDate() < minTime :
5936 minTime = e.getStartDate()
5937 if minTime == self.getEndDate() :
5938 minTime = day.replace(hour=8, minute=0)#datetime.combine(day,time(hour=8, minute=0))
5939 if minTime < self.getStartDate() :
5940 return self.getStartDate()
5941 return minTime
5943 def getEndOnDay(self, day, tz=None):
5944 if not tz:
5945 tz = self.getConference().getTimezone()
5946 if type(day) is datetime:
5947 day = day.astimezone(timezone(tz))
5948 if day.date() < self.getStartDate().astimezone(timezone(tz)).date() or day.date() > self.getEndDate().astimezone(timezone(tz)).date() :
5949 return None
5950 maxTime = self.getStartDate();
5951 for e in self.getSchedule().getEntriesOnDay(day) :
5952 if e.getEndDate() > maxTime :
5953 maxTime = e.getEndDate()
5954 if maxTime == self.getStartDate() :
5955 maxTime = day.replace(hour=19, minute=0)#datetime.combine(day,time(19,0))
5956 if maxTime > self.getEndDate() :
5957 return self.getEndDate()
5958 return maxTime
5960 def getLocationParent( self ):
5962 Returns the object from which the room/location
5963 information should be inherited
5965 return self.getConference()
5967 def getLocationList(self):
5968 """Method returning a list of "location" objects which contain the
5969 information about the different places the conference is gonna
5970 happen
5972 return self.places
5974 def addLocation(self, newPlace):
5975 self.places.append( newPlace )
5976 self.notifyModification()
5978 def _resetConveners(self):
5979 try:
5980 if self._conveners:
5981 return
5982 except AttributeError:
5983 self._conveners=[]
5984 for oc in self.conveners:
5985 newConv=SessionChair()
5986 newConv.setDataFromAvatar(oc)
5987 self._addConvener(newConv)
5989 def getConvenerList(self):
5990 self._resetConveners()
5991 return self._conveners
5993 def getAllConvenerList(self):
5994 convenerList = set()
5995 for slot in self.getSlotList():
5996 for convener in slot.getConvenerList():
5997 convenerList.add(convener)
5998 return convenerList
6000 def _addConvener(self,newConv):
6001 if newConv in self._conveners:
6002 return
6003 try:
6004 if self._convenerGen:
6005 pass
6006 except AttributeError:
6007 self._convenerGen=Counter()
6008 id = newConv.getId()
6009 if id == "":
6010 id=int(self._convenerGen.newCount())
6011 newConv.includeInSession(self,id)
6012 self._conveners.append(newConv)
6013 self.notifyModification()
6015 def addConvener(self,newConv):
6016 self._resetConveners()
6017 self._addConvener(newConv)
6018 if isinstance(newConv, AvatarUserWrapper):
6019 conv.unlinkTo(self, "convener")
6021 def removeConvener(self,conv):
6022 self._resetConveners()
6023 if conv not in self._conveners:
6024 return
6025 #--Pending queue: remove pending Convener waiting to became manager if anything
6026 self.getConference().getPendingQueuesMgr().removePendingManager(conv)
6028 #--Pending queue: remove pending Convener waiting to became coordinator if anything
6029 self.getConference().getPendingQueuesMgr().removePendingCoordinator(conv)
6031 self._conveners.remove(conv)
6032 if isinstance(conv, AvatarUserWrapper):
6033 conv.linkTo(self, "convener")
6034 conv.delete()
6035 self.notifyModification()
6037 def recoverConvener(self, con):
6038 self.addConvener(con)
6039 con.recover()
6041 def getConvenerById(self,id):
6042 id=int(id)
6043 for conv in self._conveners:
6044 if conv.getId()==id:
6045 return conv
6046 return None
6048 def getConvenerText( self ):
6049 #to be removed
6050 try:
6051 if self.convenerText:
6052 pass
6053 except AttributeError, e:
6054 self.convenerText = ""
6055 return self.convenerText
6057 def setConvenerText( self, newText ):
6058 self.convenerText = newText.strip()
6060 def appendConvenerText( self, newText ):
6061 self.setConvenerText( "%s, %s"%(self.getConvenerText(), newText.strip()) )
6063 def addContribution(self, newContrib, id=None):
6064 """Registers the contribution passed as parameter within the session
6065 assigning it a unique id.
6067 if self.hasContribution(newContrib):
6068 return
6069 self.getConference().addContribution(newContrib,id)
6070 self.contributions[newContrib.getId()]=newContrib
6071 newContrib.setSession(self)
6073 self.updateNonInheritingChildren(newContrib)
6074 for child in newContrib.getAccessController().getNonInheritingChildren():
6075 self.updateNonInheritingChildren(child)
6077 self.notifyModification()
6079 def hasContribution(self,contrib):
6080 return contrib.getSession()==self and \
6081 self.contributions.has_key(contrib.getId())
6083 def removeContribution(self,contrib):
6084 """Removes the indicated contribution from the session
6086 if not self.hasContribution(contrib):
6087 return
6088 if contrib.isScheduled():
6089 # unschedule the contribution
6090 sch=contrib.getSchEntry().getSchedule()
6091 sch.removeEntry(contrib.getSchEntry())
6092 del self.contributions[contrib.getId()]
6094 self.updateNonInheritingChildren(contrib, delete=True, propagate=False)
6095 for child in contrib.getAccessController().getNonInheritingChildren():
6096 self.updateNonInheritingChildren(child, delete=True, propagate=False)
6098 contrib.setSession(None)
6100 self.notifyModification()
6102 def newContribution( self, params = None, id=None ):
6103 c = Contribution()
6104 if params:
6105 c.setValues(params)
6106 self.addContribution( c, id )
6107 return c
6109 def getContributionById(self,id):
6110 id=str(id).strip()
6111 if self.contributions.has_key( id ):
6112 return self.contributions[ id ]
6113 return None
6115 def getContributionList( self ):
6116 return self.contributions.values()
6118 def getNumberOfContributions(self, only_scheduled=False):
6119 if only_scheduled:
6120 return len(filter(lambda c: c.isScheduled(), self.contributions.itervalues()))
6121 else:
6122 return len(self.contributions)
6124 def isProtected(self):
6125 # tells if a session is protected or not
6126 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
6128 def getAccessProtectionLevel( self ):
6129 return self.__ac.getAccessProtectionLevel()
6131 def isItselfProtected( self ):
6132 return self.__ac.isItselfProtected()
6134 def hasAnyProtection( self ):
6135 """Tells whether a session has any kind of protection over it:
6136 access or domain protection.
6138 if self.__ac.isProtected():
6139 return True
6140 if self.getDomainList():
6141 return True
6142 if self.getAccessProtectionLevel() == -1:
6143 return False
6145 return self.getOwner().hasAnyProtection()
6147 def hasProtectedOwner( self ):
6148 if self.getOwner() != None:
6149 return self.getOwner().isProtected()
6150 return False
6152 def setProtection( self, private ):
6153 self.__ac.setProtection( private )
6154 self.notify_protection_to_owner(self)
6156 def grantAccess( self, prin ):
6157 self.__ac.grantAccess( prin )
6158 if isinstance(prin, AvatarUserWrapper):
6159 prin.linkTo(self, "access")
6161 def revokeAccess( self, prin ):
6162 self.__ac.revokeAccess( prin )
6163 if isinstance(prin, AvatarUserWrapper):
6164 prin.unlinkTo(self, "access")
6166 def canView( self, aw ):
6167 """tells whether the specified user has access to the current object
6168 or any of its sub-objects
6170 if self.canAccess( aw ):
6171 return True
6173 for contrib in self.getContributionList():
6174 if contrib.canView( aw ):
6175 return True
6176 return False
6178 def isAllowedToAccess( self, user ):
6179 if not user:
6180 return False
6181 if user in self.getCoordinatorList() or self.__ac.canUserAccess( user ) \
6182 or self.canUserModify( user ) or (not self.isItselfProtected() and self.getOwner().isAllowedToAccess(user)):
6183 return True
6184 return False
6186 def canAccess( self, aw ):
6187 # Allow harvesters (Invenio, offline cache) to access
6188 # protected pages
6189 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
6190 return True
6191 #####################################################
6193 # Managers have always access
6194 if self.canModify(aw):
6195 return True
6197 flag_allowed_to_access = self.isAllowedToAccess(aw.getUser())
6198 if not self.canIPAccess(request.remote_addr) and not self.canUserModify(aw.getUser()) and \
6199 not flag_allowed_to_access:
6200 return False
6201 if not self.isProtected():
6202 return True
6203 return flag_allowed_to_access or self.conference.canKeyAccess(aw)
6205 def grantModification(self, sb, sendEmail=True):
6206 if isinstance(sb, SessionChair) or isinstance(sb, SlotChair):
6207 ah = AvatarHolder()
6208 results = ah.match({"email": sb.getEmail()}, exact=1)
6209 r = None
6210 for i in results:
6211 if sb.getEmail().lower().strip() in [j.lower().strip() for j in i.getEmails()]:
6212 r = i
6213 break
6214 if r is not None and r.isActivated():
6215 self.__ac.grantModification(r)
6216 r.linkTo(self, "manager")
6217 elif sb.getEmail() != "":
6218 modificationEmailGranted = self.__ac.grantModificationEmail(sb.getEmail())
6219 if modificationEmailGranted and sendEmail:
6220 notif = pendingQueues._PendingManagerNotification( [sb] )
6221 mail.GenericMailer.sendAndLog( notif, self.getConference() )
6222 else:
6223 self.__ac.grantModification( sb )
6224 if isinstance(sb, AvatarUserWrapper):
6225 sb.linkTo(self, "manager")
6227 def revokeModification( self, prin ):
6228 self.__ac.revokeModification( prin )
6229 if isinstance(prin, AvatarUserWrapper):
6230 prin.unlinkTo(self, "manager")
6232 def canModify(self, aw_or_user):
6233 if hasattr(aw_or_user, 'getUser'):
6234 aw_or_user = aw_or_user.getUser()
6235 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
6237 def canUserModify( self, av ):
6238 """Tells whether a user is allowed to modify the current session:
6239 only if the user is granted to modify the session or the user
6240 can modify the corresponding conference.
6242 return self.getConference().canUserModify( av ) or self.__ac.canModify( av )
6244 def getManagerList( self ):
6245 return self.__ac.getModifierList()
6247 def getAllowedToAccessList( self ):
6248 return self.__ac.getAccessList()
6250 def addMaterial( self, newMat ):
6251 newMat.setId( str(self.__materialGenerator.newCount()) )
6252 newMat.setOwner( self )
6253 self.materials[ newMat.getId() ] = newMat
6254 self.notifyModification()
6256 def removeMaterial( self, mat ):
6257 if mat.getId() in self.materials.keys():
6258 mat.delete()
6259 self.materials[mat.getId()].setOwner(None)
6260 del self.materials[ mat.getId() ]
6261 self.notifyModification()
6262 return "done: %s"%mat.getId()
6263 elif mat.getId().lower() == 'minutes':
6264 self.removeMinutes()
6265 return "done: %s"%mat.getId()
6266 return "not done: %s"%mat.getId()
6268 def recoverMaterial(self, recMat):
6269 # Id must already be set in recMat.
6270 recMat.setOwner( self )
6271 self.materials[ recMat.getId() ] = recMat
6272 recMat.recover()
6273 self.notifyModification()
6275 def getMaterialRegistry(self):
6277 Return the correct material registry for this type
6279 from MaKaC.webinterface.materialFactories import SessionMFRegistry
6280 return SessionMFRegistry
6282 def getMaterialById( self, matId ):
6283 if matId.lower() == 'minutes':
6284 return self.getMinutes()
6285 elif self.materials.has_key(matId):
6286 return self.materials[ matId ]
6287 return None
6289 def getMaterialList( self ):
6290 return self.materials.values()
6292 def getAllMaterialList(self, sort=True):
6293 l = self.getMaterialList()
6294 if self.getMinutes():
6295 l.append( self.getMinutes() )
6296 if sort:
6297 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
6298 return l
6300 def _setSchedule(self):
6301 self.__schedule=SessionSchedule(self)
6302 sl=self.getSlotList()
6303 for slot in self.getSlotList():
6304 self.__schedule.addEntry(slot.getSchEntry())
6306 def getSchedule( self ):
6307 try:
6308 if self.__schedule is None or not isinstance(self.__schedule,SessionSchedule):
6309 self._setSchedule()
6310 except AttributeError, e:
6311 self._setSchedule()
6312 return self.__schedule
6314 def getMasterSchedule( self ):
6315 return self.getOwner().getSchedule()
6317 def requireDomain( self, dom ):
6318 self.__ac.requireDomain( dom )
6320 def freeDomain( self, dom ):
6321 self.__ac.freeDomain( dom )
6323 def getDomainList( self ):
6324 return self.__ac.getRequiredDomainList()
6326 def setComments(self,comm):
6327 self._comments = comm.strip()
6329 def getComments(self):
6330 try:
6331 if self._comments:
6332 pass
6333 except AttributeError,e:
6334 self._comments=""
6335 return self._comments
6337 def createMinutes( self ):
6338 if self.getMinutes() != None:
6339 raise MaKaCError( _("The minutes for this session have already been created"), _("Session"))
6340 self.minutes = Minutes()
6341 self.minutes.setOwner( self )
6342 self.notifyModification()
6343 return self.minutes
6345 def removeMinutes( self ):
6346 if self.minutes is None:
6347 return
6348 self.minutes.delete()
6349 self.minutes.setOwner( None )
6350 self.minutes = None
6351 self.notifyModification()
6353 def recoverMinutes(self, min):
6354 self.removeMinutes() # To ensure that the current minutes are put in
6355 # the trash can.
6356 self.minutes = min
6357 self.minutes.setOwner( self )
6358 min.recover()
6359 self.notifyModification()
6360 return self.minutes
6362 def getMinutes( self ):
6363 #To be removed
6364 try:
6365 if self.minutes:
6366 pass
6367 except AttributeError, e:
6368 self.minutes = None
6370 return self.minutes
6372 def _addCoordinator(self, av):
6373 if av is None or self._coordinators.has_key(av.getId()):
6374 return
6375 self._coordinators[av.getId()]=av
6376 if self.getConference() is not None:
6377 self.getConference().addSessionCoordinator(self,av)
6379 def getCoordinatorEmailList(self):
6380 try:
6381 return self._coordinatorsEmail
6382 except:
6383 self._coordinatorsEmail = []
6384 return self._coordinatorsEmail
6386 def _addCoordinatorEmail(self, email):
6387 if email not in self.getCoordinatorEmailList():
6388 self.getCoordinatorEmailList().append(email)
6390 def removeCoordinatorEmail(self, email):
6391 if email in self.getCoordinatorEmailList():
6392 self.getCoordinatorEmailList().remove(email)
6393 self._p_changed = 1
6395 def addCoordinator( self, sb, sendEmail=True ):
6396 """Grants coordination privileges to user.
6398 Arguments:
6399 sb -- It can be either:
6400 (AvatarUserWrapper) the user to which
6401 coordination privileges must be granted.
6403 (MaKaC.conference.SessionChair) a non-existing which
6404 has to become indico user before to be granted with privileges.
6406 try:
6407 if self._coordinators:
6408 pass
6409 except AttributeError, e:
6410 self._coordinators=OOBTree()
6412 if isinstance(sb, SessionChair):
6413 ah = AvatarHolder()
6414 results=ah.match({"email":sb.getEmail()}, exact=1)
6415 r=None
6417 for i in results:
6418 if sb.getEmail().lower().strip() in [j.lower().strip() for j in i.getEmails()]:
6420 break
6422 if r is not None and r.isActivated():
6424 self._addCoordinator(r)
6425 r.linkTo(self, "coordinator")
6426 else:
6427 self.getConference().getPendingQueuesMgr().addPendingCoordinator(sb)
6428 else:
6429 self._addCoordinator(sb)
6430 if isinstance(sb, AvatarUserWrapper):
6431 sb.linkTo(self, "coordinator")
6433 def removeCoordinator( self, av ):
6434 """Revokes coordination privileges to user.
6436 Arguments:
6437 av -- (AvatarUserWrapper) user for which coordination privileges
6438 must be revoked
6440 try:
6441 if self._coordinators:
6442 pass
6443 except AttributeError, e:
6444 self._coordinators=OOBTree()
6446 if av is None or not self._coordinators.has_key(av.getId()):
6447 return
6448 del self._coordinators[av.getId()]
6449 if isinstance(av, AvatarUserWrapper):
6450 av.unlinkTo(self, "coordinator")
6451 if self.getConference() is not None:
6452 self.getConference().removeSessionCoordinator(self,av)
6454 def isCoordinator( self, av ):
6455 """Tells whether the specified user is a coordinator of the session.
6457 Arguments:
6458 av -- (AvatarUserWrapper) user to be checked
6460 Return value: (boolean)
6462 try:
6463 if self._coordinators:
6464 pass
6465 except AttributeError, e:
6466 self._coordinators=OOBTree()
6467 if (av is not None) and self._coordinators.has_key(av.getId()):
6468 return True
6469 ret = False
6470 if isinstance(av, AvatarUserWrapper):
6471 for email in av.getEmails():
6472 if email in self.getCoordinatorEmailList():
6473 self.addCoordinator(av)
6474 self.removeCoordinatorEmail(email)
6475 ret = True
6476 return ret
6478 def hasConvenerByEmail(self, email):
6479 for convener in self.getConvenerList():
6480 if email == convener.getEmail():
6481 return True
6482 return False
6485 def getCoordinatorList( self ):
6486 """Return all users which have privileges to coordinate the session.
6488 Return value: (list)
6490 try:
6491 if self._coordinators:
6492 pass
6493 except AttributeError, e:
6494 self._coordinators=OOBTree()
6496 return self._coordinators.values()
6498 def canCoordinate(self,aw, right=""):
6499 """Tells if a user has coordination privileges.
6501 Only session coordinators have coordination privileges over a
6502 session.
6504 Params:
6505 aw -- (MaKaC.accessControl.AccessWrapper) User access
6506 information for which the coordination privileges must be
6507 checked.
6509 Return value: (boolean)
6511 if right != "":
6512 return self.isCoordinator(aw.getUser()) and self.getConference().hasSessionCoordinatorRight(right)
6513 return self.isCoordinator(aw.getUser())
6516 def getScheduleType(self):
6517 try:
6518 if self._ttType:
6519 pass
6520 except AttributeError:
6521 self._ttType=SlotSchTypeFactory.getDefaultId()
6522 return self._ttType
6524 def setScheduleType(self,t):
6525 try:
6526 if self._ttType:
6527 pass
6528 except AttributeError:
6529 self._ttType=SlotSchTypeFactory.getDefaultId()
6530 t=str(t).strip().lower()
6531 if t not in SlotSchTypeFactory.getIdList() or t==self._ttType:
6532 return
6533 self._ttType=t
6534 for slot in self.getSlotList():
6535 slot.setScheduleType(t)
6537 def getAccessController(self):
6538 return self.__ac
6541 def _cmpTitle( s1, s2 ):
6542 s1=s1.getTitle().lower().strip()
6543 s2=s2.getTitle().lower().strip()
6544 return cmp( s1, s2 )
6545 _cmpTitle=staticmethod(_cmpTitle)
6548 class SessionSlot(Persistent, Fossilizable, Locatable):
6550 fossilizes(ISessionSlotFossil)
6552 def __init__(self,session,**sessionSlotData):
6553 self.session = session
6554 self.id = "not assigned"
6555 self.title = ""
6556 self.startDate=None
6557 self.duration = timedelta(minutes=1)
6558 self.places = []
6559 self.rooms = []
6560 self._conveners = []
6561 self._convenerGen=Counter()
6562 self._schedule=SlotSchTypeFactory.getDefaultKlass()(self)
6563 self._sessionSchEntry=LinkedTimeSchEntry(self)
6564 self._confSchEntry=LinkedTimeSchEntry(self)
6565 self._contributionDuration = None
6567 def getTimezone( self ):
6568 return self.getConference().getTimezone()
6570 def getLogInfo(self):
6571 data = {}
6572 data["id"] = self.id
6573 data["title"] = self.title
6574 data["session"] = self.session.getTitle()
6575 data["start date"] = self.startDate
6576 data["duration"] = self.duration
6577 i = 0
6578 for p in self.places :
6579 data["place %s"%i] = p.getName()
6580 i+=1
6581 i = 0
6582 for r in self.rooms :
6583 data["room %s"%i] = r.getName()
6584 i+=1
6585 for c in self._conveners :
6586 data["convener %s"%c.getId()] = c.getFullName()
6587 return data
6589 def clone(self,session, options):
6591 slot = SessionSlot(session)
6592 slot.session = session
6593 slot.setTitle(self.getTitle())
6594 timeDifference = session.getConference().getStartDate() - self.getSession().getConference().getStartDate()
6595 slot.setStartDate(self.getStartDate() + timeDifference)
6596 slot.setDuration(dur=self.getDuration(), check=2)
6598 #places
6599 if self.getOwnLocation() is not None:
6600 slot.setLocation(self.getOwnLocation().clone())
6601 #rooms
6602 if self.getOwnRoom() is not None:
6603 slot.setRoom(self.getOwnRoom().clone())
6605 #chairs = conveners
6606 for ch in self.getOwnConvenerList() :
6607 slot.addConvener(ch.clone())
6609 #populate the timetable
6610 if options.get("contributions", False) :
6611 for entry in self.getEntries() :
6612 if isinstance(entry, BreakTimeSchEntry) :
6613 newentry = entry.clone(slot)
6614 slot.getSchedule().addEntry(newentry,0)
6615 elif isinstance(entry, ContribSchEntry) :
6616 contrib = entry.getOwner()
6617 newcontrib = contrib.clone(session, options, timeDifference)
6618 slot.getSchedule().addEntry(newcontrib.getSchEntry(),0)
6619 ContextManager.setdefault("clone.unique_id_map", {})[contrib.getUniqueId()] = newcontrib.getUniqueId()
6621 slot.setContribDuration(0, 0, self.getContribDuration())
6622 slot.notifyModification(cleanCache = False)
6624 return slot
6626 def fit( self ):
6628 sets the start date of the slot to the start date of the first son
6629 and the end date to the end date of the last son
6631 sch = self.getSchedule()
6632 entries = sch.getEntries()
6633 if len(entries) > 0:
6634 self.setStartDate(entries[0].getStartDate(),0,0)
6635 self.setEndDate(sch.calculateEndDate(), check=0)
6637 def recalculateTimes( self, type, diff ):
6639 recalculate and reschedule the contributions of the session slot with a time "diff" of separation.
6641 if type=="duration":
6642 entries = self.getSchedule().getEntries()[:]
6644 while i<len(entries):
6645 entry=entries[i]
6646 if i+1 == len(entries):
6647 dur=self.getEndDate()-entry.getStartDate()
6648 else:
6649 nextentry=entries[i+1]
6650 dur=nextentry.getStartDate()-entry.getStartDate()-diff
6651 if dur<timedelta(0):
6652 raise EntryTimingError( _("""With the time between entries you've chosen, the entry "%s" will have a duration less than zero minutes. Please, choose another time""")%entry.getTitle())
6653 entry.setDuration(dur=dur)
6654 i+=1
6655 if len(entries) != 0 and self.getEndDate() < entry.getEndDate():
6656 self.setEndDate(entry.getEndDate(),2)
6657 elif type=="startingTime":
6658 st = self.getStartDate()
6659 entries = self.getSchedule().getEntries()[:]
6660 for entry in entries:
6661 entry.setStartDate(st,0,0)
6662 # add diff to last item end date if and only if the item is
6663 # not a break
6664 #if not isinstance(entry, BreakTimeSchEntry):
6665 # st=entry.getEndDate()+diff
6666 #else:
6667 # st=entry.getEndDate()
6668 st=entry.getEndDate()+diff
6669 if len(entries) != 0 and self.getEndDate() < st:
6670 self.setEndDate(st,2)
6672 def setValues(self,data,check=2, moveEntriesBelow=0):
6673 """check parameter:
6674 0: no check at all
6675 1: check and raise error in case of problem
6676 2: check and adapt the owner dates
6679 # In order to move the entries below, it is needed to know the diff (we have to move them)
6680 # and the list of entries to move. It's is needed to take those datas in advance because they
6681 # are going to be modified before the moving.
6682 if moveEntriesBelow == 1:
6683 oldStartDate=copy.copy(self.getStartDate())
6684 oldDuration=copy.copy(self.getDuration())
6685 i=self.getConfSchEntry().getSchedule().getEntries().index(self.getConfSchEntry())+1
6686 entriesList = self.getConfSchEntry().getSchedule().getEntries()[i:]
6687 self.title=data.get("title", "NO TITLE ASSIGNED")
6688 # Do we move all entries in the slot
6689 move = int(data.get("move",0))
6691 if "locationName" in data:
6692 loc = self.getOwnLocation()
6693 if not loc:
6694 loc = CustomLocation()
6695 self.setLocation( loc )
6696 loc.setName( data["locationName"] )
6697 loc.setAddress( data.get("locationAddress", "") )
6698 else:
6699 self.setLocation( None )
6701 if "roomName" in data:
6702 room = self.getOwnRoom()
6703 if not room:
6704 room = CustomRoom()
6705 self.setRoom( room )
6706 room.setName( data["roomName"] )
6707 else:
6708 self.setRoom( None )
6709 sDate = eDate = None
6710 confTZ = self.getOwner().getConference().getTimezone()
6711 if data.get("sDate",None) is not None:
6712 sd = data.get("sDate")
6713 sDate = timezone(confTZ).localize(datetime(sd.year,sd.month,sd.day,sd.hour,sd.minute))
6714 elif data.get("sYear","")!="" and data.get("sMonth","")!="" and \
6715 data.get("sDay","")!="" and data.get("sHour","")!="" and \
6716 data.get("sMinute","")!="":
6717 sDate = timezone(confTZ).localize(datetime(int(data["sYear"]),int(data["sMonth"]),
6718 int(data["sDay"]),int(data["sHour"]),
6719 int(data["sMinute"])))
6720 if data.get("eDate",None) is not None:
6721 ed = data.get("eDate")
6722 eDate = timezone(confTZ).localize(datetime(ed.year,ed.month,ed.day,ed.hour,ed.minute))
6723 elif data.get("eYear","")!="" and data.get("eMonth","")!="" and \
6724 data.get("eDay","")!="" and data.get("eHour","")!="" and \
6725 data.get("eMinute","")!="":
6726 eDate = timezone(confTZ).localize(datetime(int(data["eYear"]),int(data["eMonth"]),
6727 int(data["eDay"]),int(data["eHour"]),
6728 int(data["eMinute"])))
6729 if sDate != None and eDate != None:
6730 sDateUTC = sDate.astimezone(timezone('UTC'))
6731 eDateUTC = eDate.astimezone(timezone('UTC'))
6732 self.setDates(sDateUTC,eDateUTC,check,moveEntries=move)
6733 elif sDate != None:
6734 sDateUTC = sDate.astimezone(timezone('UTC'))
6735 self.setStartDate(sDateUTC,check,moveEntries=move)
6736 if data.get("durHours","")!="" and data.get("durMins","")!="":
6737 self.setDuration(hours=data["durHours"],minutes=data["durMins"],check=check)
6738 if data.get("contribDurHours","")!="" and data.get("contribDurMins","")!="":
6739 self.setContribDuration(int(data["contribDurHours"]),int(data["contribDurMins"]))
6740 elif data.get("contribDuration","")!="":
6741 self.setContribDuration(dur=data.get("contribDuration"))
6742 else:
6743 self.setContribDuration(None,None)
6744 conveners = data.get("conveners",None)
6745 if conveners is not None:
6746 self.clearConvenerList()
6747 for conv in conveners:
6748 sc = SlotChair()
6749 sc.setTitle(conv.getTitle())
6750 sc.setFirstName(conv.getFirstName())
6751 sc.setFamilyName(conv.getFamilyName())
6752 sc.setAffiliation(conv.getAffiliation())
6753 sc.setEmail(conv.getEmail())
6754 self.addConvener(sc)
6755 if moveEntriesBelow == 1:
6756 diff = (self.getStartDate() - oldStartDate) + (self.getDuration() - oldDuration)
6757 self.getSchedule().moveEntriesBelow(diff, entriesList)
6758 self._checkInnerSchedule()
6759 self.notifyModification()
6761 def _checkInnerSchedule( self ):
6762 self.getSchedule().checkSanity()
6764 def setContribDuration(self, hour=0, min=0, dur=None):
6765 self._contributionDuration = None
6766 if dur is not None:
6767 self._contributionDuration=dur
6768 elif hour != None and min != None:
6769 self._contributionDuration = timedelta(hours=hour,minutes=min)
6771 def getContribDuration(self):
6773 Duration by default for contributions within the slots.
6775 try:
6776 if self._contributionDuration:
6777 pass
6778 except AttributeError, e:
6779 self._contributionDuration = None
6780 return self._contributionDuration
6782 def notifyModification( self, cleanCache = True, cleanCacheEntries = False):
6783 self.getSession().notifyModification(cleanCache = False)
6784 if cleanCache:
6785 self.cleanCache(cleanCacheEntries)
6786 self._p_changed = 1
6788 def cleanCache(self, cleanCacheEntries = False):
6789 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
6790 ScheduleToJson.cleanCache(self)
6791 ContextManager.set('clean%s'%self.getUniqueId(), True)
6792 if cleanCacheEntries:
6793 for entry in self.getSchedule().getEntries():
6794 entry.getOwner().cleanCache(cleanConference = False)
6796 def getLocator( self ):
6797 l=self.getSession().getLocator()
6798 l["slotId"]=self.getId()
6799 return l
6801 def getConference( self ):
6802 return self.getSession().getConference()
6804 def getSession(self):
6805 return self.session
6807 def getOwner(self):
6808 return self.session
6810 def getContributionList(self):
6811 return [e.getOwner() for e in ifilter(lambda e: isinstance(e, ContribSchEntry),
6812 self.getSchedule().getEntries())]
6814 def _setSchedule(self, klass):
6815 old_sch = self.getSchedule()
6816 self._schedule = klass(self)
6817 #after removing old entries, one could try to fit them into the new
6818 # schedule, but there are several things to consider which are left
6819 # for later implementation (breaks, entries not fitting in the
6820 # slots,...)
6821 while len(old_sch.getEntries()) > 0:
6822 entry = old_sch.getEntries()[0]
6823 old_sch.removeEntry(entry)
6824 self.notifyModification()
6826 def getSchedule(self):
6827 return self._schedule
6829 def getMasterSchedule( self ):
6830 return self.getOwner().getSchedule()
6832 def getConfSchEntry( self ):
6833 try:
6834 if self._confSchEntry:
6835 pass
6836 except AttributeError:
6837 self._confSchEntry=LinkedTimeSchEntry(self)
6838 return self._confSchEntry
6840 def getSessionSchEntry( self ):
6841 try:
6842 if self._sessionSchEntry:
6843 pass
6844 except AttributeError:
6845 self._sessionSchEntry=self._schEntry
6846 return self._sessionSchEntry
6848 def setId( self, newId ):
6849 self.id=str(newId)
6850 self.notifyModification()
6852 def getId( self ):
6853 return self.id
6855 def getUniqueId( self ):
6856 """Returns (string) the unique identiffier of the item.
6857 Used mainly in the web session access key table"""
6858 return "%sl%s" % (self.getSession().getUniqueId(),self.id)
6860 def setTitle( self, newTitle ):
6861 self.title=newTitle
6862 self.notifyModification()
6864 def getTitle( self ):
6865 try:
6866 if self.title:
6867 pass
6868 except AttributeError,e:
6869 self.title=""
6870 return self.title
6872 def getFullTitle( self ):
6873 return self.getSession().getTitle() + (": " + self.getTitle() if self.getTitle() else "")
6875 def getName(self):
6876 return "slot %s"%self.getId()
6878 def getDescription(self):
6879 return self.getSession().getDescription()
6881 def setDates(self, sDate, eDate, check=2, moveEntries=0):
6882 """check parameter:
6883 0: no check at all
6884 1: check and raise error in case of problem
6885 2: check and adapt the owner dates"""
6887 if sDate > eDate:
6888 raise FormValuesError(_("End date cannot be prior to Start date"), _("Slot"))
6890 self.setStartDate(sDate, check, moveEntries, checkDuration=False)
6891 self.setDuration(0, 0, 0, eDate-sDate, check)
6892 self.notifyModification()
6894 def getEntries(self):
6895 entriesList = self.getSchedule().getEntries()
6896 return entriesList
6898 def move(self, sDate):
6899 diff=sDate-self.startDate
6900 self.startDate = sDate
6901 for slotEntry in self.getSchedule().getEntries():
6902 if isinstance(slotEntry, BreakTimeSchEntry):
6903 slotEntry.startDate = slotEntry.getStartDate() + diff
6904 else:
6905 se = slotEntry.getOwner()
6906 se.startDate = se.getStartDate() + diff
6907 self.getSchedule().reSchedule()
6909 def verifyStartDate(self, sDate,check=2):
6910 """check parameter:
6911 0: no check at all
6912 1: check and raise error in case of problem
6913 2: check and adapt the owner dates"""
6915 tz = timezone(self.getConference().getTimezone())
6917 if sDate < self.getSession().getStartDate():
6918 if check == 1:
6919 raise ParentTimingError(_("The slot \"%s\" cannot start (%s) before its parent session starts (%s)")%\
6920 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
6921 self.getSession().getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
6922 _("Slot"))
6923 elif check == 2:
6924 self.getSession().setStartDate(sDate, check, 0)
6926 def setStartDate(self,sDate,check=2,moveEntries=0,checkDuration=True):
6927 """check parameter:
6928 0: no check at all
6929 1: check and raise error in case of problem
6930 2: check and adapt the owner dates"""
6931 if sDate is None:
6932 return
6933 if not sDate.tzname():
6934 raise MaKaCError("date should be timezone aware")
6935 if check != 0:
6936 #If not using .fit() at the end of this method, comment it out
6937 #if self.getSession().getStartDate() > sDate:
6938 # self.getSession().duration += self.getSession().getStartDate() - sDate
6939 self.verifyStartDate(sDate,check)
6941 # calculate the difference betwwen old and new date
6942 difference = None
6943 if self.startDate is not None:
6944 difference = sDate - self.getStartDate()
6946 self.startDate=copy.copy(sDate)
6948 if difference != None and difference != timedelta(0) and moveEntries:
6949 ContextManager.get('autoOps').append((self, "ENTRIES_MOVED",
6950 self, sDate.astimezone(timezone(self.getTimezone()))))
6951 self.getSchedule().moveEntriesBelow(difference,self.getSchedule().getEntries()[:])
6953 if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSession().getStartDate() != sDate:
6954 self.getSession().setStartDate(sDate, check, 0)
6955 if check != 0 and self.getSession() and checkDuration:
6956 self.verifyDuration(self.getDuration(), check=check)
6958 # synchronize with other timetables
6959 self.getSessionSchEntry().synchro()
6960 self.getConfSchEntry().synchro()
6961 self.getSession().fit()
6962 self.notifyModification()
6964 def setEndDate(self,eDate,check=2):
6965 if not eDate.tzname():
6966 raise MaKaCError("date should be timezone aware")
6967 if check != 0:
6968 self.verifyDuration(eDate-self.startDate, check)
6969 self.setDuration(dur=eDate-self.startDate,check=check)
6970 if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSession().getEndDate() != eDate:
6971 self.getSession().setEndDate(eDate, check)
6972 self.getSession().fit()
6973 self.notifyModification()
6975 def getStartDate( self ):
6976 return self.startDate
6978 def getAdjustedStartDate(self,tz=None):
6979 if not tz:
6980 tz = self.getConference().getTimezone()
6981 if tz not in all_timezones:
6982 tz = 'UTC'
6983 return self.startDate.astimezone(timezone(tz))
6985 def getEndDate( self ):
6986 if self.startDate is None:
6987 return None
6988 return self.startDate+self.duration
6990 def getAdjustedEndDate( self, tz=None ):
6991 if not tz:
6992 tz = self.getConference().getTimezone()
6993 if tz not in all_timezones:
6994 tz = 'UTC'
6995 if self.getEndDate():
6996 return self.getEndDate().astimezone(timezone(tz))
6997 return None
6999 def getDuration( self ):
7000 return self.duration
7002 def isMoreThanDay(self):
7003 if self.getDuration() >= timedelta(days=1):
7004 return True
7005 return False
7007 def verifyDuration(self, dur, check=1):
7008 """check parameter:
7009 0: no check at all
7010 1: check and raise error in case of problem
7011 2: check and adapt the owner dates"""
7013 tz = timezone(self.getConference().getTimezone())
7014 if dur <= timedelta(0):
7015 raise FormValuesError(_("The duration cannot be less than zero"), _("Slot"))
7016 if dur.days > 1:
7017 raise FormValuesError(_("The duration cannot be more than one day"), _("Slot"))
7018 if self.startDate is not None:
7019 sessionStartDate = self.getSession().getStartDate()
7020 sessionEndDate = self.getSession().getEndDate()
7021 # end date has to be between the session dates
7022 eDate = self.startDate + dur
7023 if eDate > sessionEndDate:
7024 if check==1:
7025 raise EntryTimingError(_("The session slot cannot end (%s) after its parent session (%s)") \
7026 % (eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
7027 sessionEndDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
7028 _("Slot"))
7029 elif check==2:
7030 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
7031 self.getSession(), eDate.astimezone(tz)))
7032 self.getSession().setEndDate(eDate,check)
7033 if eDate.astimezone(tz).date() > self.startDate.astimezone(tz).date():
7034 raise TimingError( _("The time slot must end on the same day it has started"), _("Slot"))
7035 # do not modify if slot entries will be affected
7036 sch = self.getSchedule()
7037 entries = sch.getEntries()
7038 if entries != []:
7039 if eDate < sch.calculateEndDate():
7040 raise TimingError(_("The session slot cannot end at (%s) because there is a contribution (%s) ending after that time. ")%\
7041 (eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
7042 sch.calculateEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
7043 _("Slot"))
7045 def setDuration(self, days=0,hours=0,minutes=0,dur=0,check=1):
7046 """check parameter:
7047 0: no check at all
7048 1: check and raise error in case of problem
7049 2: check and adapt the owner dates"""
7051 if dur==0:
7052 dur = timedelta(days=int(days),hours=int(hours),minutes=int(minutes))
7053 if dur==0 and check==2:
7054 ContextManager.get('autoOps').append((self, "DURATION_SET",
7055 self, 1))
7056 dur = timedelta(minutes=1)
7057 if dur > timedelta(days=1) and check==2:
7058 pass#dur = timedelta(days=1)
7059 if check != 0:
7060 self.verifyDuration(dur, check)
7061 self.duration = dur
7062 self.getSessionSchEntry().synchro()
7063 self.getConfSchEntry().synchro()
7064 self.getSession().fit()
7065 self.notifyModification()
7067 def getLocationParent( self ):
7069 Returns the object from which the room/location
7070 information should be inherited
7072 return self.session.conference
7074 def delete(self):
7075 signals.event.session_slot_deleted.send(self)
7076 self.getSchedule().clear()
7077 if self.getSession() is not None:
7078 self.getSession().removeSlot(self)
7079 self.session=None
7080 TrashCanManager().add(self)
7082 def recover(self):
7083 TrashCanManager().remove(self)
7085 def getAccessController( self ):
7086 return self.getSession().getAccessController()
7088 def canAccess(self,aw):
7089 return self.getSession().canAccess(aw)
7091 def canView(self,aw):
7092 return self.getSession().canView(aw)
7094 def isProtected(self):
7095 return self.getSession().isProtected()
7097 def getAccessKey( self ):
7098 return self.getSession().getAccessKey()
7100 def setScheduleType(self,id):
7101 id=str(id).strip().lower()
7102 currentId=SlotSchTypeFactory.getId(self.getSchedule())
7103 if id not in SlotSchTypeFactory.getIdList() or id==currentId:
7104 return
7105 self._setSchedule(SlotSchTypeFactory.getScheduleKlass(id))
7107 def getConvenerList(self):
7108 try:
7109 if self._conveners:
7110 pass
7111 except AttributeError:
7112 self._conveners = []
7113 if self._conveners == []:
7114 return self.getSession().getConvenerList()
7115 return self._conveners
7117 def addConvener(self,newConv):
7118 if newConv in self._conveners:
7119 return
7120 try:
7121 if self._convenerGen:
7122 pass
7123 except AttributeError:
7124 self._convenerGen=Counter()
7125 id = newConv.getId()
7126 if id == "":
7127 id=int(self._convenerGen.newCount())
7128 newConv.includeInSlot(self,id)
7129 self._conveners.append(newConv)
7130 self.notifyModification()
7132 def removeConvener(self,conv):
7133 if conv not in self._conveners:
7134 return
7135 self._conveners.remove(conv)
7136 conv.delete()
7137 self.notifyModification()
7139 def recoverConvener(self, con):
7140 self.addConvener(con)
7141 con.recover()
7143 def getConvenerById(self,id):
7144 id=int(id)
7145 for conv in self._conveners:
7146 if conv.getId()==id:
7147 return conv
7148 return None
7150 def getOwnConvenerList(self):
7151 try:
7152 if self._conveners:
7153 pass
7154 except AttributeError:
7155 self._conveners = []
7156 return self._conveners
7158 def clearConvenerList(self):
7159 while len(self.getOwnConvenerList()) > 0:
7160 self._conveners.pop()
7161 self.notifyModification()
7163 def getColor(self):
7164 res=""
7165 if self.getSession() is not None:
7166 res=self.getSession().getColor()
7167 return res
7169 def getTextColor(self):
7170 res=""
7171 if self.getSession() is not None:
7172 res=self.getSession().getTextColor()
7173 return res
7175 def getAllMaterialList(self, sort=True):
7176 return self.getSession().getAllMaterialList(sort=sort)
7178 def getRecursiveAllowedToAccessList(self):
7179 return self.getSession().getRecursiveAllowedToAccessList()
7181 def canModify(self, aw_or_user):
7182 return self.getSession().canModify(aw_or_user)
7185 class ContributionParticipation(Persistent, Fossilizable):
7187 fossilizes(IContributionParticipationFossil, IContributionParticipationMinimalFossil,\
7188 IContributionParticipationTTDisplayFossil,\
7189 IContributionParticipationTTMgmtFossil)
7191 def __init__( self ):
7192 self._contrib = None
7193 self._id = ""
7194 self._firstName = ""
7195 self._surName = ""
7196 self._email = ""
7197 self._affiliation = ""
7198 self._address = ""
7199 self._phone = ""
7200 self._title = ""
7201 self._fax = ""
7203 def _notifyModification( self ):
7204 if self._contrib != None:
7205 self._contrib.notifyModification()
7207 def setValues(self, data):
7208 self.setFirstName(data.get("firstName", ""))
7209 self.setFamilyName(data.get("familyName",""))
7210 self.setAffiliation(data.get("affilation",""))
7211 self.setAddress(data.get("address",""))
7212 self.setEmail(data.get("email",""))
7213 self.setFax(data.get("fax",""))
7214 self.setTitle(data.get("title",""))
7215 self.setPhone(data.get("phone",""))
7216 self._notifyModification()
7218 def getValues(self):
7219 data={}
7220 data["firstName"]=self.getFirstName()
7221 data["familyName"]=self.getFamilyName()
7222 data["affilation"]=self.getAffiliation()
7223 data["address"]=self.getAddress()
7224 data["email"]=self.getEmail()
7225 data["fax"]=self.getFax()
7226 data["title"]=self.getTitle()
7227 data["phone"]=self.getPhone()
7228 return data
7230 def clone(self):
7231 part = ContributionParticipation()
7232 part.setValues(self.getValues())
7233 return part
7235 def setDataFromAvatar(self,av):
7236 # av is an Avatar object.
7237 if av is None:
7238 return
7239 self.setFirstName(av.getName())
7240 self.setFamilyName(av.getSurName())
7241 self.setEmail(av.getEmail())
7242 self.setAffiliation(av.getOrganisation())
7243 self.setAddress(av.getAddress())
7244 self.setPhone(av.getTelephone())
7245 self.setTitle(av.getTitle())
7246 self.setFax(av.getFax())
7247 self._notifyModification()
7249 def setDataFromOtherCP(self,cp):
7250 # cp is a ContributionParticipation object.
7251 if cp is None:
7252 return
7253 self.setFirstName(cp.getFirstName())
7254 self.setFamilyName(cp.getFamilyName())
7255 self.setEmail(cp.getEmail())
7256 self.setAffiliation(cp.getAffiliation())
7257 self.setAddress(cp.getAddress())
7258 self.setPhone(cp.getPhone())
7259 self.setTitle(cp.getTitle())
7260 self.setFax(cp.getFax())
7261 self._notifyModification()
7263 def includeInContribution( self, contrib, id ):
7264 if self.getContribution() == contrib and self.getId()==id.strip():
7265 return
7266 self._contrib = contrib
7267 self._id = id
7269 def delete( self ):
7270 self._contrib = None
7271 TrashCanManager().add(self)
7273 def recover(self):
7274 TrashCanManager().remove(self)
7276 def setId(self, newId):
7277 self._id = newId
7279 def getId( self ):
7280 return self._id
7282 def getContribution( self ):
7283 return self._contrib
7285 def getConference(self):
7286 return self._contrib.getConference()
7288 def getLocator(self):
7289 if self.getContribution() is None:
7290 return None
7291 loc=self.getContribution().getLocator()
7292 loc["authorId"]=self.getId()
7293 return loc
7295 def _unindex(self):
7296 contrib=self.getContribution()
7297 if contrib is not None:
7298 conf=contrib.getConference()
7299 if conf is not None:
7300 conf.unindexAuthor(self)
7301 conf.unindexSpeaker(self)
7303 def _index(self):
7304 contrib=self.getContribution()
7305 if contrib is not None:
7306 conf=contrib.getConference()
7307 if conf is not None:
7308 conf.indexAuthor(self)
7309 conf.indexSpeaker(self)
7311 @Updates ('MaKaC.conference.ContributionParticipation', 'firstName')
7312 def setFirstName( self, newName ):
7313 tmp=newName.strip()
7314 if tmp==self._firstName:
7315 return
7316 self._unindex()
7317 self._firstName=tmp
7318 self._index()
7319 self._notifyModification()
7321 def getFirstName( self ):
7322 return self._firstName
7324 def getName( self ):
7325 return self._firstName
7328 @Updates ('MaKaC.conference.ContributionParticipation', 'familyName')
7329 def setFamilyName( self, newName ):
7330 tmp=newName.strip()
7331 if tmp==self._surName:
7332 return
7333 self._unindex()
7334 self._surName=tmp
7335 self._index()
7336 self._notifyModification()
7338 def getFamilyName( self ):
7339 return self._surName
7341 def getSurName( self ):
7342 return self._surName
7345 @Updates ('MaKaC.conference.ContributionParticipation', 'email')
7346 def setEmail( self, newMail ):
7347 tmp=newMail.strip()
7348 if tmp==self._email:
7349 return
7350 self._unindex()
7351 self._email=newMail.strip()
7352 self._index()
7353 self._notifyModification()
7355 def getEmail( self ):
7356 return self._email
7358 @Updates ('MaKaC.conference.ContributionParticipation', 'affiliation')
7359 def setAffiliation( self, newAffil ):
7360 self._affiliation = newAffil.strip()
7361 self._notifyModification()
7363 def getAffiliation( self ):
7364 if self._affiliation.lower() == "unknown":
7365 return ""
7366 return self._affiliation
7368 @Updates ('MaKaC.conference.ContributionParticipation', 'address')
7369 def setAddress( self, newAddr ):
7370 self._address = newAddr.strip()
7371 self._notifyModification()
7373 def getAddress( self ):
7374 return self._address
7376 @Updates('MaKaC.conference.ContributionParticipation', 'phone')
7377 def setPhone( self, newPhone ):
7378 self._phone = newPhone.strip()
7379 self._notifyModification()
7381 def getPhone( self ):
7382 return self._phone
7384 @Updates ('MaKaC.conference.ContributionParticipation', 'title')
7385 def setTitle( self, newTitle ):
7386 self._title = newTitle.strip()
7387 self._notifyModification()
7389 def getTitle( self ):
7390 return self._title
7392 @Updates ('MaKaC.conference.ContributionParticipation', 'fax')
7393 def setFax( self, newFax ):
7394 self._fax = newFax.strip()
7395 self._notifyModification()
7397 def getFax( self ):
7398 try:
7399 if self._fax:
7400 pass
7401 except AttributeError:
7402 self._fax=""
7403 return self._fax
7405 def getDirectFullName( self ):
7406 res = self.getDirectFullNameNoTitle()
7407 if self.getTitle() != "":
7408 res = "%s %s"%( self.getTitle(), res )
7409 return res
7411 def getDirectFullNameNoTitle(self, upper=True):
7412 familyName = safe_upper(self.getFamilyName()) if upper else self.getFamilyName()
7413 return "{0} {1}".format(self.getFirstName(), familyName).strip()
7415 def getFullName(self):
7416 res = self.getFullNameNoTitle()
7417 if self.getTitle():
7418 res = "%s %s" % (self.getTitle(), res)
7419 return res
7421 def getFullNameNoTitle(self):
7422 res = safe_upper(self.getFamilyName())
7423 if self.getFirstName():
7424 if res.strip():
7425 res = "%s, %s" % (res, self.getFirstName())
7426 else:
7427 res = self.getFirstName()
7428 return res
7430 def getAbrName(self):
7431 res = self.getFamilyName()
7432 if self.getFirstName():
7433 if res:
7434 res = "%s, " % res
7435 res = "%s%s." % (res, safe_upper(self.getFirstName()[0]))
7436 return res
7438 def isSubmitter(self):
7439 if self.getContribution() is None:
7440 return False
7441 return self.getContribution().canUserSubmit(self)
7443 def isPendingSubmitter(self):
7444 if self.getContribution() is None:
7445 return False
7446 if self.getContribution().getConference() is None:
7447 return False
7448 return self.getContribution().getConference().getPendingQueuesMgr().isPendingSubmitter(self)
7450 def isInAuthorList(self):
7451 # Sometimes authors are not in the author index for an unknown reason.
7452 # In this case we don't want to link to the author page since opening it would fail
7453 return self.getConference().getAuthorIndex().getByAuthorObj(self) is not None
7455 @staticmethod
7456 def _cmpFamilyName(cp1, cp2):
7457 o1 = "%s %s"%(cp1.getFamilyName(), cp1.getFirstName())
7458 o2 = "%s %s"%(cp2.getFamilyName(), cp2.getFirstName())
7459 o1=o1.lower().strip()
7460 o2=o2.lower().strip()
7461 return cmp( o1, o2 )
7464 class AuthorIndex(Persistent):
7466 def __init__(self):
7467 self._idx=OOBTree()
7469 def _getKey(self,author):
7470 k = "%s %s %s"%(author.getFamilyName().lower(),author.getFirstName().lower(),author.getEmail().lower())
7471 return k.strip()
7473 def index(self,author):
7474 key=self._getKey(author)
7475 if not self._idx.has_key(key):
7476 self._idx[key]=[]
7477 l = self._idx[key]
7478 l.append(author)
7479 self._idx[key] = l
7480 self.notifyModification()
7482 def unindex(self,author):
7483 key=self._getKey(author)
7484 if self._idx.has_key(key):
7485 if author in self._idx[key]:
7486 l = self._idx[key]
7487 l.remove(author)
7488 self._idx[key] = l
7489 if len(self._idx[key])<=0:
7490 del self._idx[key]
7491 self.notifyModification()
7493 def getParticipations(self):
7494 return self._idx.values()
7496 def getById(self, id):
7497 return self._idx.get(id,None)
7499 def getByAuthorObj(self, auth):
7500 return self.getById(self._getKey(auth))
7502 def getParticipationKeys(self):
7503 return self._idx.keys()
7505 def notifyModification(self):
7506 self._idx._p_changed = 1
7507 self._p_changed = 1
7509 def iteritems(self):
7510 return self._idx.iteritems()
7512 def match(self, criteria, exact=0):
7513 self._options = ['organisation', 'surName', 'name', 'email']
7514 l = []
7515 for item in self.getParticipations():
7516 if len(item)>0:
7517 ok = []
7518 for f,v in criteria.items():
7519 if f == 'organisation' and v != '':
7520 if (exact == 0 and item[0].getAffiliation().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getAffiliation().lower() != v.lower()):
7521 ok.append(False)
7522 else:
7523 ok.append(True)
7524 if f == 'surName' and v!= '':
7525 if (exact == 0 and item[0].getSurName().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getSurName().lower() != v.lower()):
7526 ok.append(False)
7527 else:
7528 ok.append(True)
7529 if f == 'name' and v!= '':
7530 if (exact == 0 and item[0].getName().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getName().lower() != v.lower()):
7531 ok.append(False)
7532 else:
7533 ok.append(True)
7534 if f == 'email' and v!= '':
7535 if (exact == 0 and item[0].getEmail().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getEmail().lower() != v.lower()):
7536 ok.append(False)
7537 else:
7538 ok.append(True)
7539 if len(ok) > 0 and not False in ok:
7540 l.append(item[0])
7541 return l
7543 class _AuthIdx(Persistent):
7545 def __init__(self,conf):
7546 self._conf=conf
7547 self._idx=OOBTree()
7549 def _getKey(self,auth):
7550 return "%s %s"%(auth.getFamilyName().lower(),auth.getFirstName().lower())
7552 def index(self,auth):
7553 if auth.getContribution() is None:
7554 raise MaKaCError( _("Cannot index an author of a contribution which has not been included in a Conference"), _("Author Index"))
7555 if auth.getContribution().getConference()!=self._conf:
7556 raise MaKaCError( _("cannot index an author of a contribution which does not belong to this Conference"), _("Author Index"))
7557 key=self._getKey(auth)
7558 contribId=str(auth.getContribution().getId())
7559 if not self._idx.has_key(key):
7560 self._idx[key]=OIBTree()
7561 if not self._idx[key].has_key(contribId):
7562 self._idx[key][contribId]=0
7563 self._idx[key][contribId]+=1
7565 def unindex(self,auth):
7566 if auth.getContribution() is None:
7567 raise MaKaCError( _("Cannot unindex an author of a contribution which is not included in a conference"), _("Author Index"))
7568 if auth.getContribution().getConference()!=self._conf:
7569 raise MaKaCError( _("Cannot unindex an author of a contribution which does not belong to this conference"), _("Author Index"))
7570 key=self._getKey(auth)
7571 if not self._idx.has_key(key):
7572 return
7573 contribId=str(auth.getContribution().getId())
7574 self._idx[key][contribId]-=1
7575 if self._idx[key][contribId]<=0:
7576 del self._idx[key][contribId]
7577 if len(self._idx[key])<=0:
7578 del self._idx[key]
7580 def match(self,query):
7581 query=query.lower().strip()
7582 res=OISet()
7583 for k in self._idx.keys():
7584 if k.find(query)!=-1:
7585 res=union(res,self._idx[k])
7586 return res
7589 class _PrimAuthIdx(_AuthIdx):
7591 def __init__(self,conf):
7592 _AuthIdx.__init__(self,conf)
7593 for contrib in self._conf.getContributionList():
7594 for auth in contrib.getPrimaryAuthorList():
7595 self.index(auth)
7598 class Contribution(CommonObjectBase, Locatable):
7599 """This class implements a conference contribution, being the concrete
7600 contributes of the conference participants. The class contains
7601 necessary attributes to store contribution basic meta data and provides
7602 the useful operations to access and manage them. A contribution can be
7603 attached either to a session or to a conference.
7606 fossilizes(IContributionFossil, IContributionWithSpeakersFossil, IContributionWithSubContribsFossil)
7608 def __init__(self, **contribData):
7609 self.parent = None
7610 self._session = None
7611 self.id = ""
7612 self.title = ""
7613 self._fields = {}
7614 self.description = ""
7615 self.startDate = None
7616 self.duration = timedelta(0)
7617 self.speakers = []
7618 self.speakerText = ""
7619 self.place = None
7620 self.room = None
7621 self._boardNumber = ""
7622 self._resetSchEntry()
7623 self.__ac = AccessController(self)
7624 self.materials = {}
7625 self.__materialGenerator = Counter()
7626 self._subConts = []
7627 self.__subContGenerator = Counter()
7628 self.paper = None
7629 self.slides = None
7630 self.video = None
7631 self.poster = None
7632 self.minutes = None
7633 self.reviewing = None
7634 self._authorGen = Counter()
7635 self._authors = OOBTree()
7636 self._primaryAuthors = []
7637 self._coAuthors = []
7638 self._speakers = []
7639 self._track = None
7640 self._type = None
7641 self._status = ContribStatusNotSch(self)
7642 #List of allowed users to submit material
7643 self._submitters = []
7644 self._submittersEmail = []
7645 self._modificationDS = nowutc()
7646 self._keywords = ""
7647 self._reviewManager = ReviewManager(self)
7649 def __cmp__(self, other):
7650 if type(self) is not type(other):
7651 # This is actually dangerous and the ZODB manual says not to do this
7652 # because it relies on memory order. However, this branch should never
7653 # be taken anyway since we do not store different types in the same set
7654 # or use them as keys.
7655 return cmp(hash(self), hash(other))
7656 if self.getConference() == other.getConference():
7657 return cmp(self.getId(), other.getId())
7658 return cmp(self.getConference(), other.getConference())
7660 @return_ascii
7661 def __repr__(self):
7662 parent_id = self.parent.getId() if self.parent else None
7663 return '<Contribution({}, {}, {})>'.format(self.getId(), self.getTitle(), parent_id)
7665 def getVerboseType(self):
7666 return 'Contribution'
7668 def getTimezone(self):
7669 return self.getConference().getTimezone()
7671 def getReviewManager(self):
7672 if not hasattr(self, "_reviewManager"):
7673 self._reviewManager = ReviewManager(self)
7674 return self._reviewManager
7676 def updateNonInheritingChildren(self, elem, delete=False):
7677 self.getAccessController().updateNonInheritingChildren(elem, delete)
7678 self.notify_protection_to_owner(elem, delete)
7680 def notify_protection_to_owner(self, elem, delete=False):
7681 self.getOwner().updateNonInheritingChildren(elem, delete)
7683 def getKeywords(self):
7684 try:
7685 return self._keywords
7686 except:
7687 self._keywords = ""
7688 return ""
7690 def setKeywords(self, keywords):
7691 if type(keywords) is list:
7692 self._keywords = keywords[0]
7693 else:
7694 self._keywords = keywords
7695 self.notifyModification(cleanCache=False)
7697 def getFields(self, valueonly=False):
7698 try:
7699 if self._fields:
7700 pass
7701 except AttributeError:
7702 self._fields = {}
7703 if not valueonly:
7704 return self._fields
7705 else:
7706 return dict((k, v.value if isinstance(v, AbstractFieldContent) else v) for k, v in self._fields.iteritems())
7708 def removeField(self, field):
7709 if field in self.getFields():
7710 del self.getFields()[field]
7711 self.notifyModification()
7713 def setField(self, fid, v):
7714 if isinstance(v, AbstractFieldContent):
7715 v = v.value
7716 try:
7717 self.getFields()[fid].value = v
7719 # `AttritbuteError` may happen if the field is not yet an AbstractFieldContent
7720 # (lazy migration)
7721 # `KeyError` means that the attribute doesn't exist in the contrib, in which
7722 # case it should be created anyway
7723 except (AttributeError, KeyError):
7724 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7725 for f in afm.getFields():
7726 if f.getId() == fid:
7727 self.getFields()[fid] = AbstractFieldContent(f, v)
7728 break
7729 self.notifyModification()
7731 def getField(self, field):
7732 if field in self.getFields():
7733 value = self.getFields()[field]
7734 if type(value) is list:
7735 return "".join(value)
7736 elif value is None:
7737 return ""
7738 else:
7739 return value
7740 else:
7741 return ""
7743 def getLogInfo(self):
7744 data = {}
7745 data["subject"] = self.getTitle()
7746 data["id"] = self.id
7747 data["title"] = self.title
7748 data["parent title"] = self.parent.getTitle()
7749 if self._session is not None:
7750 data["session title"] = self._session.getTitle()
7751 data["description"] = self.description
7752 if self.getConference():
7753 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7754 for f in afm.getFields():
7755 id = f.getId()
7756 data[id] = self.getField(id)
7757 data["start date"] = "%s" % self.startDate
7758 data["duration"] = "%s" % self.duration
7759 if self._track is not None:
7760 data["track"] = self._track.getTitle()
7761 if self._type is not None:
7762 data["type"] = self._type
7763 data["speaker text"] = self.speakerText
7764 if self.place is not None:
7765 data["place"] = self.place.getName()
7766 if self.room is not None:
7767 data["room"] = self.room.getName()
7768 data["board number"] = self._boardNumber
7769 for sc in self.getSubContributionList():
7770 data["subcontribution %s" % sc.getId()] = sc.getTitle()
7771 for pa in self._primaryAuthors:
7772 data["primary author %s" % pa.getId()] = pa.getFullName()
7773 for ca in self._coAuthors:
7774 data["co-author %s" % ca.getId()] = ca.getFullName()
7775 for sp in self._speakers:
7776 data["speaker %s" % sp.getId()] = sp.getFullName()
7777 for s in self.getSubmitterList():
7778 if isinstance(s, AvatarUserWrapper):
7779 data["submitter"] = s.getFullName()
7780 else:
7781 data["submitter"] = s.getName()
7782 return data
7784 def setValues(self, data, check=2, moveEntriesBelow=0):
7785 """Sets all the values of the current contribution object from a
7786 dictionary containing the following key-value pairs:
7787 title-(str)
7788 description-(str)
7789 locationName-(str) => name of the location, if not specified
7790 it will be set to the parent location name.
7791 locationAddress-(str)
7792 roomName-(str) => name of the room, if not specified it will
7793 be set to the parent room name.
7794 year, month, day, sHour, sMinute - (str) => components of the
7795 starting date of the session, if not specified it will
7796 be set to now.
7797 durationHours, durationMinutes - (str)
7798 speakers - (str)
7799 check parameter:
7800 0: no check at all
7801 1: check and raise error in case of problem
7802 2: check and adapt the owner dates
7803 moveEntries:
7804 0: no move
7805 1: moveEntries below the contribution
7806 Please, note that this method sets ALL values which means that if
7807 the given dictionary doesn't contain any of the keys the value
7808 will set to a default value.
7811 # In order to move the entries below, it is needed to know the diff (we have to move them)
7812 # and the list of entries to move. It's is needed to take those datas in advance because they
7813 # are going to be modified before the moving.
7814 if moveEntriesBelow == 1:
7815 oldStartDate = copy.copy(self.getStartDate())
7816 oldDuration = copy.copy(self.getDuration())
7817 i = self.getSchEntry().getSchedule().getEntries().index(self.getSchEntry())+1
7818 entriesList = self.getSchEntry().getSchedule().getEntries()[i:]
7819 if data.has_key("title"):
7820 self.setTitle(data["title"])
7821 if data.has_key("keywords"):
7822 self.setKeywords(data["keywords"])
7823 if data.has_key("description"):
7824 self.setDescription(data["description"])
7825 if data.has_key("type") and self.getConference():
7826 self.setType(self.getConference().getContribTypeById(data["type"]))
7827 if self.getConference():
7828 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7829 for f in afm.getFields():
7830 id = f.getId()
7831 if data.has_key("f_%s" % id):
7832 self.setField(id, data["f_%s" % id])
7834 if "locationName" in data:
7835 loc = self.getOwnLocation()
7836 if not loc:
7837 loc = CustomLocation()
7838 self.setLocation(loc)
7839 loc.setName(data["locationName"])
7840 loc.setAddress(data.get("locationAddress", ""))
7841 else:
7842 self.setLocation(None)
7844 #same as for the location
7845 if "roomName" in data:
7846 room = self.getOwnRoom()
7847 if not room:
7848 room = CustomRoom()
7849 self.setRoom(room)
7850 room.setName(data["roomName"])
7851 room.retrieveFullName(data.get("locationName", ""))
7852 else:
7853 self.setRoom(None)
7855 tz = 'UTC'
7856 if self.getConference():
7857 tz = self.getConference().getTimezone()
7858 if data.get("targetDay", "") != "" and data.get("sHour", "") != "" and data.get("sMinute", "") != "" and check == 2:
7859 ############################################
7860 # Fermi timezone awareness #
7861 ############################################
7862 me = timezone(tz).localize(datetime(int(data["targetDay"][0:4]),
7863 int(data["targetDay"][5:7]), int(data["targetDay"][8:])))
7864 sdate = timezone(tz).localize(datetime(me.year, me.month,
7865 me.day, int(data["sHour"]), int(data["sMinute"])))
7866 self.setStartDate(sdate.astimezone(timezone('UTC')), check=2)
7867 if data.get("sYear", "") != "" and data.get("sMonth", "") != "" and \
7868 data.get("sDay", "") != "" and data.get("sHour", "") != "" and \
7869 data.get("sMinute", "") != "":
7870 self.setStartDate(timezone(tz).localize(datetime(int(data["sYear"]),
7871 int(data["sMonth"]), int(data["sDay"]),
7872 int(data["sHour"]), int(data["sMinute"]))).astimezone(timezone('UTC')),
7873 check=2)
7874 ############################################
7875 # Fermi timezone awareness(end) #
7876 ############################################
7877 if data.get("durTimedelta", "") != "":
7878 self.setDuration(check=check, dur=data["durTimedelta"])
7879 elif data.get("durHours", "") != "" and data.get("durMins", "") != "":
7880 self.setDuration(data["durHours"], data["durMins"], check)
7881 else:
7882 h = data.get("durHours", "").strip()
7883 m = data.get("durMins", "").strip()
7884 if h != "" or m != "":
7885 h = h or "0"
7886 m = m or "0"
7887 if h != "0" or m != "0":
7888 self.setDuration(int(h), int(m), check)
7889 if data.has_key("boardNumber"):
7890 self.setBoardNumber(data.get("boardNumber", ""))
7891 if moveEntriesBelow == 1:
7892 diff = (self.getStartDate() - oldStartDate) + (self.getDuration() - oldDuration)
7893 self.getConference().getSchedule().moveEntriesBelow(diff, entriesList)
7894 self.notifyModification()
7896 def clone(self, parent, options, deltaTime = 0):
7897 cont = Contribution()
7898 parent.addContribution(cont)
7899 cont.setTitle( self.getTitle() )
7900 cont.setDescription( self.getDescription() )
7901 for k, v in self.getFields().items():
7902 cont.setField(k, v)
7903 cont.setKeywords( self.getKeywords() )
7904 if deltaTime == 0 :
7905 deltaTime = parent.getStartDate() - self.getOwner().getStartDate()
7907 startDate = None
7908 if self.startDate is not None :
7909 startDate = self.getStartDate() + deltaTime
7910 cont.setStartDate( startDate )
7912 cont.setDuration( dur=self.getDuration() )
7914 if self.getOwnLocation() is not None:
7915 cont.setLocation(self.getOwnLocation().clone())
7916 if self.getOwnRoom() is not None:
7917 cont.setRoom(self.getOwnRoom().clone())
7918 cont.setBoardNumber(self.getBoardNumber())
7919 cont.setReportNumberHolder(self.getReportNumberHolder().clone(self))
7921 cont.setStatus(self.getCurrentStatus())
7923 if self.getType() is not None :
7924 for ct in cont.getConference().getContribTypeList() :
7925 if ct.getName() == self.getType().getName() :
7926 cont.setType(ct)
7927 break
7929 if options.get("tracks", False) :
7930 if self.getTrack() is not None :
7931 for tr in cont.getConference().getTrackList() :
7932 if tr.getTitle() == self.getTrack().getTitle() :
7933 cont.setTrack(tr)
7934 break
7935 else :
7936 cont.setTrack(None)
7938 if options.get("access", False) :
7939 cont.setProtection(self.getAccessController()._getAccessProtection())
7940 for u in self.getAllowedToAccessList() :
7941 cont.grantAccess(u)
7942 for mgr in self.getManagerList() :
7943 cont.grantModification(mgr)
7944 for sub in self.getSubmitterList() :
7945 cont.grantSubmission(sub)
7946 for domain in self.getDomainList():
7947 cont.requireDomain(domain)
7949 if options.get("authors", False) :
7950 for a in self.getPrimaryAuthorList() :
7951 cont.addPrimaryAuthor(a.clone())
7952 for ca in self.getCoAuthorList() :
7953 cont.addCoAuthor(ca.clone())
7954 for sp in self.getSpeakerList():
7955 cont.newSpeaker(sp.clone())
7956 cont.setSpeakerText(self.getSpeakerText())
7958 if options.get("materials", False) :
7959 for m in self.getMaterialList() :
7960 cont.addMaterial(m.clone(cont))
7961 if self.getPaper() is not None:
7962 cont.setPaper(self.getPaper().clone(cont))
7963 if self.getSlides() is not None:
7964 cont.setSlides(self.getSlides().clone(cont))
7965 if self.getVideo() is not None:
7966 cont.setVideo(self.getVideo().clone(cont))
7967 if self.getPoster() is not None:
7968 cont.setPoster(self.getPoster().clone(cont))
7969 if self.getMinutes() is not None:
7970 cont.setMinutes(self.getMinutes().clone(cont))
7971 if self.getReviewing() is not None:
7972 cont.setReviewing(self.getReviewing().clone(cont))
7974 if options.get("subcontribs", False) :
7975 for sc in self.getSubContributionList() :
7976 cont.addSubContribution(sc.clone(cont, self, options))
7977 return cont
7979 def notifyModification( self, date = None, raiseEvent = True, cleanCache = True):
7980 self.setModificationDate(date)
7982 if raiseEvent:
7983 signals.event.contribution_data_changed.send(self)
7985 if cleanCache:
7986 self.cleanCache()
7988 parent = self.getParent()
7989 if parent:
7990 parent.setModificationDate()
7991 self._p_changed = 1
7993 def cleanCache(self, cleanConference = True):
7994 # Do not clean cache if already cleaned
7995 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
7996 ScheduleToJson.cleanCache(self)
7997 ContextManager.set('clean%s'%self.getUniqueId(), cleanConference)
7999 def getCategoriesPath(self):
8000 return self.getConference().getCategoriesPath()
8002 def getModifKey( self ):
8003 return self.getConference().getModifKey()
8005 def getAccessKey( self ):
8006 return self.getConference().getAccessKey()
8008 def getLocator( self ):
8009 """Gives back a globaly unique identification encapsulated in a Locator
8010 object for the contribution instance
8012 if self.getConference() == None:
8013 return Locator()
8014 lconf = self.getConference().getLocator()
8015 if self.getSession() is not None:
8016 lconf["sessionId"] = self.getSession().getId()
8017 lconf["contribId"] = self.getId()
8018 return lconf
8020 def _setConference( self, conf ):
8021 self.parent = conf
8023 def _setId( self, id ):
8024 self.id = id
8026 def includeInConference( self, conf, id ):
8027 """sets the conference of a contribution
8029 if self.getConference() is not None:
8030 #raise MaKaCError("the contribution is already included in a conference")
8031 pass
8032 else:
8033 self._setConference( conf )
8034 self._setId( id )
8036 def delete( self ):
8037 """deletes a contribution and all of its subitems
8040 oldParent = self.getConference()
8042 if oldParent != None:
8043 signals.event.contribution_deleted.send(self, parent=oldParent)
8045 self.setTrack(None)
8046 for mat in self.getMaterialList():
8047 self.removeMaterial(mat)
8048 self.removePaper()
8049 self.removeSlides()
8050 self.removeVideo()
8051 self.removePoster()
8052 self.removeMinutes()
8053 self.removeReviewing()
8055 self.notify_protection_to_owner(self, delete=True)
8057 self.setSession(None)
8059 while len(self.getSubContributionList()) > 0:
8061 sc = self.getSubContributionList()[0]
8063 self.removeSubContribution(sc)
8066 # delete it from parent session (if it exists)
8067 if self.getOwner() != self.getConference():
8069 self.getOwner().removeContribution( self )
8071 # (always) delete it from the parent conference
8072 self.getConference().removeContribution( self, callDelete=False )
8074 self._setConference( None )
8076 self.setStatus(ContribStatusNone(self))
8078 TrashCanManager().add(self)
8080 def recover(self):
8081 TrashCanManager().remove(self)
8083 def setId( self, newId ):
8084 self._setId(newId)
8086 def getId( self ):
8087 return self.id
8089 def getUniqueId( self ):
8090 """returns (string) the unique identifier of the item"""
8091 """used mainly in the web session access key table"""
8092 return "%st%s" % (self.getConference().getUniqueId(),self.id)
8094 def setTitle( self, newTitle, notify = True ):
8095 oldTitle = self.title
8096 self.title = newTitle.strip()
8098 if notify:
8099 if oldTitle != newTitle:
8100 signals.event.contribution_title_changed.send(self, old=oldTitle, new=newTitle)
8101 self.notifyModification()
8103 def getTitle( self ):
8104 if self.title.strip() == "":
8105 return "(no title)"
8106 return self.title
8108 def getDescription(self):
8109 return str(self.getField("content"))
8111 def setDescription(self, desc):
8112 self.setField("content", desc)
8114 def setParent(self,parent):
8115 self.parent=parent
8116 self.notifyModification(cleanCache = False)
8117 if self.parent==None:
8118 return
8120 def getParent( self ):
8121 if self.getSession() is not None:
8122 return self.getSession()
8123 return self.getConference()
8125 def getOwner( self ):
8126 return self.getParent()
8128 def setOwner(self, owner):
8129 self.setParent(owner)
8131 def getConference( self ):
8132 return self.parent
8134 def getSession( self ):
8135 try:
8136 if self._session:
8137 pass
8138 except AttributeError:
8139 self._session=None
8140 return self._session
8142 def setSession(self,session):
8143 if self.getSession()==session:
8144 return
8145 if self.isScheduled():
8146 schEntry=self.getSchEntry()
8147 schEntry.getSchedule().removeEntry(schEntry)
8148 oldSession=self.getSession()
8149 if oldSession is not None:
8150 oldSession.removeContribution(self)
8151 self._session=session
8152 if session is not None:
8153 session.addContribution(self)
8155 def getContribution(self):
8156 return self
8158 def _resetSchEntry(self):
8159 self.__schEntry=ContribSchEntry(self)
8161 def getSchEntry(self):
8162 if self.__schEntry is None or \
8163 not isinstance(self.__schEntry,ContribSchEntry):
8164 self._resetSchEntry()
8165 return self.__schEntry
8167 def isScheduled(self):
8168 #For the moment we do it like this
8169 return self.getSchEntry().getSchedule() is not None
8171 def isWithdrawn(self):
8172 return isinstance(self.getCurrentStatus(), ContribStatusWithdrawn)
8174 def getLocationParent(self):
8176 Returns the object from which the room/location
8177 information should be inherited
8179 if not self.getConference().getEnableSessionSlots() and self.getSession():
8180 return self.getSession()
8181 if self.isScheduled():
8182 return self.getSchEntry().getSchedule().getOwner()
8183 return self.getOwner()
8185 def getOwnLocation(self):
8186 return self.place
8188 def setLocation(self, newLocation):
8189 oldLocation = self.place
8190 self.place = newLocation
8191 self.notifyModification()
8193 def getOwnRoom(self):
8194 return self.room
8196 def setRoom(self, newRoom):
8197 oldRoom = self.room
8198 self.room = newRoom
8199 self.notifyModification()
8201 def setBoardNumber(self, newBoardNum):
8202 self._boardNumber=str(newBoardNum).strip()
8204 def getBoardNumber(self):
8205 try:
8206 if self._boardNumber:
8207 pass
8208 except AttributeError:
8209 self._boardNumber=""
8210 return self._boardNumber
8212 def verifyStartDate(self, sDate, check=2):
8213 """check parameter:
8214 0: no check at all
8215 1: check and raise error in case of problem
8216 2: check and adapt the owner dates"""
8218 tz = timezone(self.getConference().getTimezone())
8219 if self.getSchEntry().getSchedule():
8220 owner = self.getSchEntry().getSchedule().getOwner()
8221 else:
8222 owner = self.getOwner()
8223 if sDate < owner.getStartDate():
8224 if check == 1:
8225 raise ParentTimingError(_("The contribution <i>\"%s\"</i> cannot start before (%s) its parent (%s)") %\
8226 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8227 owner.getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
8228 _("Contribution"))
8229 if check == 2:
8230 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
8231 owner, sDate.astimezone(tz)))
8232 owner.setDates(sDate,owner.getEndDate(), check)
8233 if sDate > owner.getEndDate():
8234 if check == 1:
8235 raise ParentTimingError(_("The contribution <i>\"%s\"</i> cannot start after (%s) its parent end date(%s)") %\
8236 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8237 owner.getEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
8238 _("Contribution"))
8239 if check == 2:
8240 owner.setEndDate(sDate+self.getDuration(),check)
8241 # Check that after modifying the start date, the end date is still within the limits of the slot
8242 if self.getDuration() and sDate + self.getDuration() > owner.getEndDate():
8243 if check==1:
8244 raise ParentTimingError("The contribution cannot end after (%s) its parent ends (%s)"%\
8245 ((sDate + self.getDuration()).astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8246 owner.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
8247 _("Contribution"))
8248 elif check==2:
8249 # update the schedule
8250 owner.setEndDate(sDate + self.getDuration(),check)
8251 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
8252 owner, owner.getAdjustedEndDate()))
8254 def setStartDate(self, newDate, check=2, moveEntries=0):
8255 """check parameter:
8256 0: no check at all
8257 1: check and raise error in case of problem
8258 2: check and adapt the owner dates"""
8259 if newDate == None:
8260 self.startDate=None
8261 return
8262 if not newDate.tzname():
8263 raise MaKaCError("date should be timezone aware")
8265 if newDate != None and check != 0:
8266 self.verifyStartDate(newDate, check)
8267 self.startDate=copy.copy(newDate)
8268 self.getSchEntry().synchro()
8269 self.notifyModification()
8271 def getStartDate(self):
8272 return self.startDate
8274 def getAdjustedStartDate(self, tz=None):
8275 if self.getStartDate() is None:
8276 return None
8277 if not tz:
8278 tz = self.getConference().getTimezone()
8279 if tz not in all_timezones:
8280 tz = 'UTC'
8281 return self.getStartDate().astimezone(timezone(tz))
8283 def getEndDate(self):
8284 if self.getStartDate() is None:
8285 return None
8286 return self.getStartDate()+self.getDuration()
8288 def getAdjustedEndDate(self, tz=None):
8289 if not tz:
8290 tz = self.getConference().getTimezone()
8291 if tz not in all_timezones:
8292 tz = 'UTC'
8293 if self.getEndDate():
8294 return self.getEndDate().astimezone(timezone(tz))
8295 return None
8297 def getDuration(self):
8298 return self.duration
8300 def verifyDuration(self, check=2):
8301 """check parameter:
8302 0: no check at all
8303 1: check and raise error in case of problem
8304 2: check and adapt the owner dates"""
8306 tz = timezone(self.getConference().getTimezone())
8308 endDate = self.getEndDate()
8310 if self.getSchEntry().getSchedule() is not None:
8311 owner = self.getSchEntry().getSchedule().getOwner()
8312 if endDate > owner.getEndDate():
8313 if check==1:
8314 raise ParentTimingError(_("The contribution \"%s\" ending date (%s) has to fit between its parent's dates (%s - %s)") %\
8315 (self.getTitle(), endDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8316 owner.getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8317 owner.getEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
8318 _("Contribution"))
8319 elif check==2:
8320 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
8321 owner, self.getAdjustedEndDate()))
8322 owner.setEndDate(endDate, check)
8324 def setDuration(self, hours=0, minutes=15, check=2, dur=0):
8325 """check parameter:
8326 0: no check at all
8327 1: check and raise error in case of problem
8328 2: check and adapt the owner dates"""
8330 if dur!=0:
8331 self.duration=dur
8332 else:
8333 self.duration=timedelta(hours=int(hours),minutes=int(minutes))
8334 if check != 0:
8335 self.verifyDuration(check)
8336 self.getSchEntry().synchro()
8337 self.notifyModification()
8339 def _addAuthor(self, part):
8342 try:
8343 if self._authors:
8344 pass
8345 except AttributeError:
8346 self._authors = OOBTree()
8347 try:
8348 if self._authorGen:
8349 pass
8350 except AttributeError:
8351 self._authorGen=Counter()
8352 newId = part.getId()
8353 if newId == "":
8354 newId = str( self._authorGen.newCount() )
8355 self._authors[newId] = part
8356 part.includeInContribution( self, newId )
8358 def _removeAuthor(self, part):
8361 try:
8362 if self._authors:
8363 pass
8364 except AttributeError:
8365 self._authors = OOBTree()
8366 if not self._authors.has_key( part.getId() ):
8367 return
8368 del self._authors[ part.getId() ]
8369 if not self.isSpeaker(part):
8370 part.delete()
8372 def addPrimaryAuthor(self, part, index=None):
8375 try:
8376 if self._primaryAuthors:
8377 pass
8378 except AttributeError:
8379 self._primaryAuthors = []
8380 self._addAuthor( part )
8381 if index is not None:
8382 self._primaryAuthors.insert(index, part)
8383 else:
8384 self._primaryAuthors.append( part )
8385 if self.getConference() is not None:
8386 self.getConference().indexAuthor(part)
8387 self.notifyModification(cleanCache = False)
8389 def removePrimaryAuthor(self, part, removeSpeaker=1, removePendingSubm=True):
8392 try:
8393 if self._primaryAuthors:
8394 pass
8395 except AttributeError:
8396 self._primaryAuthors = []
8397 if part not in self._primaryAuthors:
8398 return
8399 if self.getConference() is not None:
8400 self.getConference().unindexAuthor(part)
8401 self._primaryAuthors.remove( part )
8402 if removeSpeaker:
8403 self.removeSpeaker( part )
8404 self._removeAuthor( part )
8405 if removePendingSubm:
8406 #--Pending queue: remove pending participant waiting to became submitter if anything
8407 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8409 self.notifyModification(cleanCache = False)
8411 def recoverPrimaryAuthor(self, pa, isPendingSubmitter):
8412 self.addPrimaryAuthor(pa)
8413 pa.recover()
8414 if isPendingSubmitter:
8415 self.getConference().getPendingQueuesMgr().addPendingSubmitter(pa, False)
8417 def isPrimaryAuthor(self, part):
8420 try:
8421 if self._primaryAuthors:
8422 pass
8423 except AttributeError:
8424 self._primaryAuthors = []
8425 return part in self._primaryAuthors
8427 def isCoAuthor(self, part):
8428 try:
8429 if self._coAuthors:
8430 pass
8431 except AttributeError:
8432 self._coAuthors = []
8433 return part in self._coAuthors
8435 def isPrimaryAuthorByEmail(self, email):
8436 for prAuthor in self.getPrimaryAuthorList():
8437 if prAuthor.getEmail() == email:
8438 return True
8439 return False
8441 def isCoAuthorByEmail(self, email):
8442 for coAuthor in self.getCoAuthorList():
8443 if coAuthor.getEmail() == email:
8444 return True
8445 return False
8447 def isSpeakerByEmail(self, email):
8448 for speaker in self.getSpeakerList():
8449 if speaker.getEmail() == email:
8450 return True
8451 return False
8453 def changePosPrimaryAuthor(self, part, index):
8456 try:
8457 if self._primaryAuthors:
8458 pass
8459 except AttributeError:
8460 self._primaryAuthors=[]
8461 if not part in self._primaryAuthors:
8462 return
8463 self._primaryAuthors.remove(part)
8464 self._primaryAuthors.insert(index,part)
8465 self.notifyModification(cleanCache = False)
8467 def upPrimaryAuthor(self, part):
8470 try:
8471 if self._primaryAuthors:
8472 pass
8473 except AttributeError:
8474 self._primaryAuthors=[]
8475 try:
8476 idx=self._primaryAuthors.index(part)
8477 except ValueError:
8478 return
8479 if idx==0:
8480 return
8481 self._primaryAuthors.remove(part)
8482 self._primaryAuthors.insert(idx-1,part)
8483 self.notifyModification(cleanCache=False)
8485 def downPrimaryAuthor(self, part):
8488 try:
8489 if self._primaryAuthors:
8490 pass
8491 except AttributeError:
8492 self._primaryAuthors=[]
8493 try:
8494 idx=self._primaryAuthors.index(part)
8495 except ValueError:
8496 return
8497 if idx>len(self._primaryAuthors):
8498 return
8499 self._primaryAuthors.remove(part)
8500 self._primaryAuthors.insert(idx+1,part)
8501 self.notifyModification(cleanCache = False)
8503 def newAuthorsList(self, prAuthors, coAuthors):
8504 ''' calculate new lists of both kind of authors, because something has
8505 been changed the position by drag and drop '''
8506 newPrList = self.calculateNewAuthorList(prAuthors, "prAuthor")
8507 newCoList = self.calculateNewAuthorList(coAuthors, "coAuthor")
8508 self.setPrimaryAuthorList(newPrList)
8509 self.setCoAuthorList(newCoList)
8511 def calculateNewAuthorList(self, list, kind):
8512 result = []
8513 if kind == "prAuthor":
8514 for auth in list:
8515 author = self.getPrimaryAuthorById(auth['id'])
8516 if author:
8517 result.append(author)
8518 else:
8519 author = self.getCoAuthorById(auth['id'])
8520 if author:
8521 result.append(author)
8523 elif kind == "coAuthor":
8524 for auth in list:
8525 author = self.getCoAuthorById(auth['id'])
8526 if author:
8527 result.append(author)
8528 else:
8529 author = self.getPrimaryAuthorById(auth['id'])
8530 if author:
8531 result.append(author)
8532 return result
8534 def getPrimaryAuthorById(self, authorId):
8535 for author in self.getPrimaryAuthorList():
8536 if authorId == author.getId():
8537 return author
8538 return None
8540 def getCoAuthorById(self, authorId):
8541 for author in self.getCoAuthorList():
8542 if authorId == author.getId():
8543 return author
8544 return None
8546 def setPrimaryAuthorList(self, l):
8547 self._primaryAuthors = l
8548 self.notifyModification(cleanCache = False)
8550 def setCoAuthorList(self, l):
8551 self._coAuthors = l
8552 self.notifyModification(cleanCache = False)
8554 def changePosCoAuthor(self, part, index):
8557 try:
8558 if self._coAuthors:
8559 pass
8560 except AttributeError:
8561 self._coAuthors=[]
8562 if not part in self._coAuthors:
8563 return
8564 self._coAuthors.remove(part)
8565 self._coAuthors.insert(index,part)
8566 self.notifyModification(cleanCache = False)
8568 def upCoAuthor(self, part):
8571 try:
8572 if self._coAuthors:
8573 pass
8574 except AttributeError:
8575 self._coAuthors=[]
8576 try:
8577 idx=self._coAuthors.index(part)
8578 except ValueError:
8579 return
8580 if idx==0:
8581 return
8582 self._coAuthors.remove(part)
8583 self._coAuthors.insert(idx-1,part)
8584 self.notifyModification(cleanCache = False)
8586 def downCoAuthor(self, part):
8589 try:
8590 if self._coAuthors:
8591 pass
8592 except AttributeError:
8593 self._coAuthors=[]
8594 try:
8595 idx=self._coAuthors.index(part)
8596 except ValueError:
8597 return
8598 if idx>len(self._coAuthors):
8599 return
8600 self._coAuthors.remove(part)
8601 self._coAuthors.insert(idx+1,part)
8602 self.notifyModification(cleanCache = False)
8604 def getPrimaryAuthorList(self):
8607 try:
8608 if self._primaryAuthors:
8609 pass
8610 except AttributeError:
8611 self._primaryAuthors = []
8612 return self._primaryAuthors
8614 getPrimaryAuthorsList = getPrimaryAuthorList
8616 def getAuthorList(self):
8619 try:
8620 if self._authors:
8621 pass
8622 except AttributeError:
8623 self._authors = OOBTree()
8624 return self._authors.values()
8626 def getAllAuthors(self):
8627 """ This method returns a list composed by the primary authors
8628 and co-authors. The different with getAuthorList() is the type
8629 of the output.
8631 return self.getPrimaryAuthorList() + self.getCoAuthorList()
8633 def addCoAuthor(self, part, index=None):
8636 try:
8637 if self._coAuthors:
8638 pass
8639 except AttributeError:
8640 self._coAuthors = []
8641 self._addAuthor( part )
8642 if index is not None:
8643 self._coAuthors.insert(index, part)
8644 else:
8645 self._coAuthors.append( part )
8646 if self.getConference() is not None:
8647 self.getConference().indexAuthor(part)
8648 self.notifyModification(cleanCache = False)
8650 def removeCoAuthor(self, part, removeSpeaker=1, removePendingSubm=True):
8653 try:
8654 if self._coAuthors:
8655 pass
8656 except AttributeError:
8657 self._coAuthors = []
8658 if part not in self._coAuthors:
8659 return
8660 if self.getConference() is not None:
8661 self.getConference().unindexAuthor(part)
8662 self._coAuthors.remove( part )
8663 if removeSpeaker:
8664 self.removeSpeaker( part )
8665 self._removeAuthor( part )
8666 if removePendingSubm:
8667 #--Pending queue: remove pending participant waiting to became submitter if anything
8668 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8670 self.notifyModification(cleanCache = False)
8672 def recoverCoAuthor(self, ca, isPendingSubmitter):
8673 self.addCoAuthor(ca)
8674 ca.recover()
8675 if isPendingSubmitter:
8676 self.getConference().getPendingQueuesMgr().addPendingSubmitter(ca, False)
8678 def getCoAuthorList(self):
8681 try:
8682 if self._coAuthors:
8683 pass
8684 except AttributeError:
8685 self._coAuthors = []
8686 return self._coAuthors
8688 def getAuthorById(self, authorId):
8691 try:
8692 if self._authors:
8693 pass
8694 except AttributeError:
8695 self._authors = OOBTree()
8696 return self._authors.get( authorId.strip(), None )
8698 def isAuthor(self, part):
8701 try:
8702 if self._authors:
8703 pass
8704 except AttributeError:
8705 self._authors = OOBTree()
8706 return self._authors.has_key( part.getId() )
8708 def getSpeakerById(self, authorId):
8711 try:
8712 if self._speakers:
8713 pass
8714 except AttributeError:
8715 self._speakers = []
8716 for spk in self._speakers:
8717 if spk.getId() == authorId:
8718 return spk
8719 return None
8721 def changePosSpeaker(self, part, index):
8724 try:
8725 if self._speakers:
8726 pass
8727 except AttributeError:
8728 self._speakers = []
8729 if not part in self._speakers:
8730 return
8731 self._speakers.remove(part)
8732 self._speakers.insert(index,part)
8733 self.notifyModification()
8735 def addSpeaker(self, part, index=None):
8737 Adds a speaker (ContributionParticipation object) to the contribution
8738 forcing it to be one of the authors of the contribution
8740 try:
8741 if self._speakers:
8742 pass
8743 except AttributeError:
8744 self._speakers = []
8745 if not self.isAuthor( part ):
8746 raise MaKaCError( _("The Specified speaker is not the Author"), _("Contribution"))
8747 if index is not None:
8748 self._speakers.insert(index, part)
8749 else:
8750 self._speakers.append( part )
8751 if self.getConference() is not None:
8752 self.getConference().indexSpeaker(part)
8753 self.notifyModification()
8755 def newSpeaker(self, part):
8757 Adds a new speaker (ContributionParticipation object) to the contribution
8758 setting the speakers ID and the fact it belongs to that contribution
8760 try:
8761 if self._speakers:
8762 pass
8763 except AttributeError:
8764 self._speakers = []
8765 try:
8766 if self._authorGen:
8767 pass
8768 except AttributeError:
8769 self._authorGen=Counter()
8770 self._speakers.append( part )
8771 newId = part.getId()
8772 if newId == "":
8773 newId = str( self._authorGen.newCount() )
8774 part.includeInContribution(self, newId)
8775 if self.getConference() is not None:
8776 self.getConference().indexSpeaker(part)
8777 self.notifyModification()
8779 def removeSpeaker(self, part):
8782 try:
8783 if self._speakers:
8784 pass
8785 except AttributeError:
8786 self._speakers = []
8787 if part not in self._speakers:
8788 return
8789 self._speakers.remove( part )
8790 if self.getConference() is not None:
8791 self.getConference().unindexSpeaker(part)
8792 if part not in self.getAuthorList():
8793 part.delete()
8794 #--Pending queue: remove pending participant waiting to became submitter if anything
8795 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8797 self.notifyModification()
8799 def recoverSpeaker(self, spk, isPendingSubmitter):
8800 self.newSpeaker(spk)
8801 spk.recover()
8802 if isPendingSubmitter:
8803 self.getConference().getPendingQueuesMgr().addPendingSubmitter(spk, False)
8805 def isSpeaker(self, part):
8808 try:
8809 if self._speakers:
8810 pass
8811 except AttributeError:
8812 self._speakers = []
8813 return part in self._speakers
8815 def getSpeakerList(self):
8818 try:
8819 if self._speakers:
8820 pass
8821 except AttributeError:
8822 self._speakers = []
8823 return self._speakers
8825 def getSpeakerText(self):
8826 #to be removed
8827 try:
8828 if self.speakerText:
8829 pass
8830 except AttributeError, e:
8831 self.speakerText = ""
8832 return self.speakerText
8834 def setSpeakerText(self, newText):
8835 self.speakerText = newText.strip()
8837 def appendSpeakerText(self, newText):
8838 self.setSpeakerText("%s, %s" % (self.getSpeakerText(), newText.strip()))
8840 def isProtected(self):
8841 # tells if a contribution is protected or not
8842 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
8844 def getAccessProtectionLevel(self):
8845 return self.__ac.getAccessProtectionLevel()
8847 def isItselfProtected(self):
8848 return self.__ac.isItselfProtected()
8850 def hasAnyProtection(self):
8851 """Tells whether a contribution has any kind of protection over it:
8852 access or domain protection.
8854 if self.__ac.isProtected():
8855 return True
8856 if self.getDomainList():
8857 return True
8858 if self.getAccessProtectionLevel() == -1:
8859 return False
8860 if self.getOwner():
8861 return self.getOwner().hasAnyProtection()
8862 else:
8863 return False
8865 def hasProtectedOwner(self):
8866 if self.getOwner() != None:
8867 return self.getOwner().isProtected()
8868 return False
8870 def setProtection(self, private):
8872 oldValue = 1 if self.isProtected() else -1
8874 self.__ac.setProtection( private )
8875 self.notify_protection_to_owner(self)
8877 if oldValue != private:
8878 # notify listeners
8879 signals.event.contribution_protection_changed.send(self, old=oldValue, new=private)
8881 def grantAccess(self, prin):
8882 self.__ac.grantAccess( prin )
8883 if isinstance(prin, AvatarUserWrapper):
8884 prin.linkTo(self, "access")
8885 self.notifyModification(raiseEvent = False)
8887 def revokeAccess( self, prin ):
8888 self.__ac.revokeAccess( prin )
8889 if isinstance(prin, AvatarUserWrapper):
8890 prin.unlinkTo(self, "access")
8891 self.notifyModification(raiseEvent = False)
8893 def canView( self, aw ):
8894 """tells whether the specified user has access to the current object
8895 or any of its sub-objects
8897 if self.canAccess( aw ):
8898 return True
8899 ################################################################################################
8900 for sc in self.getSubContributionList():
8901 if sc.canView( aw ):
8902 return True
8903 return False
8905 def isAllowedToAccess( self, user ):
8906 if not user:
8907 return False
8908 return (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user )) or\
8909 self.__ac.canUserAccess( user ) or\
8910 self.canUserModify( user ) or \
8911 self.canUserSubmit(user)
8913 def canAccess( self, aw ):
8914 # Allow harvesters (Invenio, offline cache) to access
8915 # protected pages
8916 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
8917 return True
8918 #####################################################
8920 if self.canModify(aw):
8921 return True
8923 if not self.canIPAccess(request.remote_addr) and not self.isAllowedToAccess(aw.getUser()):
8924 return False
8925 if not self.isProtected():
8926 return True
8927 flag = self.isAllowedToAccess( aw.getUser() )
8928 return flag or self.getConference().canKeyAccess(aw)
8930 def grantModification( self, prin ):
8931 self.__ac.grantModification( prin )
8932 if isinstance(prin, AvatarUserWrapper):
8933 prin.linkTo(self, "manager")
8934 self.notifyModification(raiseEvent = False)
8936 def revokeModification( self, prin ):
8937 self.__ac.revokeModification( prin )
8938 if isinstance(prin, AvatarUserWrapper):
8939 prin.unlinkTo(self, "manager")
8940 self.notifyModification(raiseEvent = False)
8942 def canModify(self, aw_or_user):
8943 if hasattr(aw_or_user, 'getUser'):
8944 aw_or_user = aw_or_user.getUser()
8945 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
8947 def canUserModify( self, av ):
8948 """Tells whether a user is allowed to modify the current contribution:
8949 only if the user is granted to modify the contribution or the user
8950 can modify any of its upper objects (i.e. conference or session).
8952 return self.getParent().canUserModify( av ) or self.__ac.canModify( av )
8954 def getManagerList( self ):
8955 return self.__ac.getModifierList()
8957 def getAllowedToAccessList( self ):
8958 return self.__ac.getAccessList()
8960 def addMaterial( self, newMat ):
8961 newMat.setId( str(self.__materialGenerator.newCount()) )
8962 newMat.setOwner( self )
8963 self.materials[ newMat.getId() ] = newMat
8964 self.notifyModification()
8966 def removeMaterial(self, mat):
8967 if mat.getId() in self.materials.keys():
8968 mat.delete()
8969 self.materials[mat.getId()].setOwner(None)
8970 del self.materials[ mat.getId() ]
8971 self.notifyModification()
8972 elif mat.getId().lower() == 'paper':
8973 self.removePaper()
8974 elif mat.getId().lower() == 'slides':
8975 self.removeSlides()
8976 elif mat.getId().lower() == 'minutes':
8977 self.removeMinutes()
8978 elif mat.getId().lower() == 'video':
8979 self.removeVideo()
8980 elif mat.getId().lower() == 'poster':
8981 self.removePoster()
8982 elif mat.getId().lower() == 'reviewing':
8983 self.removeReviewing()
8985 def recoverMaterial(self, recMat):
8986 # Id must already be set in recMat.
8987 recMat.setOwner( self )
8988 self.materials[ recMat.getId() ] = recMat
8989 recMat.recover()
8990 self.notifyModification()
8992 def getMaterialRegistry(self):
8994 Return the correct material registry for this type
8996 from MaKaC.webinterface.materialFactories import ContribMFRegistry
8997 return ContribMFRegistry
8999 def getMaterialById( self, matId ):
9000 if matId.lower() == 'paper':
9001 return self.getPaper()
9002 elif matId.lower() == 'slides':
9003 return self.getSlides()
9004 elif matId.lower() == 'video':
9005 return self.getVideo()
9006 elif matId.lower() == 'poster':
9007 return self.getPoster()
9008 elif matId.lower() == 'minutes':
9009 return self.getMinutes()
9010 elif self.materials.has_key(matId):
9011 return self.materials[ matId ]
9012 return None
9014 def getMaterialList( self ):
9015 return self.materials.values()
9017 def getAllMaterialList(self, sort=True):
9018 l = self.getMaterialList()
9019 if self.getPaper():
9020 l.append( self.getPaper() )
9021 if self.getSlides():
9022 l.append( self.getSlides() )
9023 if self.getVideo():
9024 l.append( self.getVideo() )
9025 if self.getPoster():
9026 l.append( self.getPoster() )
9027 if self.getMinutes():
9028 l.append( self.getMinutes() )
9029 if sort:
9030 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
9031 return l
9033 def getAllViewableMaterialList( self, aw=None ):
9034 if not aw:
9035 aw = ContextManager.get("currentAW", ContextManager.get("currentRH").getAW())
9036 return [mat for mat in self.getAllMaterialList() if mat.canView(aw)]
9039 def newSubContribution(self):
9040 newSub = SubContribution()
9041 self.addSubContribution(newSub)
9042 signals.event.subcontribution_created.send(newSub, parent=self)
9043 return newSub
9045 def addSubContribution( self, newSubCont ):
9046 newSubCont.setId(str( self.__subContGenerator.newCount()) )
9047 newSubCont.setOwner( self )
9048 self._subConts.append( newSubCont )
9049 self.notifyModification(cleanCache = False)
9051 def removeSubContribution( self, subCont ):
9052 if subCont in self._subConts:
9053 subCont.delete()
9054 subCont.setOwner(None)
9055 self._subConts.remove(subCont)
9056 self.notifyModification(cleanCache = False)
9058 def recoverSubContribution( self, recSubCont ):
9059 # Id must already be set in recSubCont.
9060 recSubCont.setOwner( self )
9061 self._subConts.append( recSubCont )
9062 recSubCont.recover()
9063 self.notifyModification(cleanCache = False)
9065 def getSubContributionById(self, SCId):
9066 for sb in self._subConts:
9067 if sb.getId() == SCId:
9068 return sb
9070 def getSubContributionList(self):
9071 return self._subConts
9073 def iterSubContributions(self):
9074 return iter(self._subConts)
9076 def getNumberOfSubcontributions(self):
9077 return len(self._subConts)
9079 def upSubContribution(self, subcont):
9080 if subcont in self._subConts:
9081 if self._subConts.index(subcont) != 0:
9082 index = self._subConts.index(subcont)
9083 sb = self._subConts.pop(index)
9084 self._subConts.insert(index-1, sb)
9085 self.notifyModification(cleanCache = False)
9087 def downSubContribution(self, subCont):
9088 if subCont in self._subConts:
9089 if self._subConts.index(subCont) < len(self._subConts)-1:
9090 index = self._subConts.index(subCont)
9091 sb = self._subConts.pop(index)
9092 self._subConts.insert(index+1, sb)
9093 self.notifyModification(cleanCache = False)
9095 def setPaper( self, newPaper ):
9096 if self.getPaper() != None:
9097 raise MaKaCError( _("The paper for this contribution has already been set"), _("Contribution"))
9098 self.paper=newPaper
9099 self.paper.setOwner( self )
9100 self.notifyModification()
9102 def removePaper( self ):
9103 if self.paper is None:
9104 return
9105 self.paper.delete()
9106 self.paper.setOwner(None)
9107 self.paper = None
9108 self.notifyModification()
9110 def recoverPaper(self, p):
9111 self.setPaper(p)
9112 p.recover()
9114 def getPaper( self ):
9115 return self.paper
9117 def setSlides( self, newSlides ):
9118 if self.getSlides() != None:
9119 raise MaKaCError( _("The slides for this contribution have already been set"), _("contribution"))
9120 self.slides=newSlides
9121 self.slides.setOwner( self )
9122 self.notifyModification()
9124 def removeSlides( self ):
9125 if self.slides is None:
9126 return
9127 self.slides.delete()
9128 self.slides.setOwner( None )
9129 self.slides= None
9130 self.notifyModification()
9132 def recoverSlides(self, s):
9133 self.setSlides(s)
9134 s.recover()
9136 def getSlides( self ):
9137 return self.slides
9139 def setVideo( self, newVideo ):
9140 if self.getVideo() != None:
9141 raise MaKaCError( _("the video for this contribution has already been set"))
9142 self.video=newVideo
9143 self.video.setOwner( self )
9144 self.notifyModification()
9146 def removeVideo( self ):
9147 if self.getVideo() is None:
9148 return
9149 self.video.delete()
9150 self.video.setOwner(None)
9151 self.video = None
9152 self.notifyModification()
9154 def recoverVideo(self, v):
9155 self.setVideo(v)
9156 v.recover()
9158 def getVideo( self ):
9159 try:
9160 if self.video:
9161 pass
9162 except AttributeError:
9163 self.video = None
9164 return self.video
9166 def setPoster( self, newPoster ):
9167 if self.getPoster() != None:
9168 raise MaKaCError( _("the poster for this contribution has already been set"))
9169 self.poster=newPoster
9170 self.poster.setOwner( self )
9171 self.notifyModification()
9173 def removePoster( self ):
9174 if self.getPoster() is None:
9175 return
9176 self.poster.delete()
9177 self.poster.setOwner(None)
9178 self.poster = None
9179 self.notifyModification()
9181 def recoverPoster(self, p):
9182 self.setPoster(p)
9183 p.recover()
9185 def getPoster( self ):
9186 try:
9187 if self.poster:
9188 pass
9189 except AttributeError:
9190 self.poster = None
9191 return self.poster
9193 def setMinutes( self, newMinutes ):
9194 if self.getMinutes() != None:
9195 raise MaKaCError( _("the Minutes for this contribution has already been set"))
9196 self.minutes=newMinutes
9197 self.minutes.setOwner( self )
9198 self.notifyModification()
9200 def createMinutes( self ):
9201 if self.getMinutes() != None:
9202 raise MaKaCError( _("The minutes for this contribution have already been created"), _("Contribution"))
9203 self.minutes = Minutes()
9204 self.minutes.setOwner( self )
9205 self.notifyModification()
9206 return self.minutes
9208 def removeMinutes( self ):
9209 if self.getMinutes() is None:
9210 return
9211 self.minutes.delete()
9212 self.minutes.setOwner( None )
9213 self.minutes = None
9214 self.notifyModification()
9216 def recoverMinutes(self, min):
9217 self.removeMinutes() # To ensure that the current minutes are put in
9218 # the trash can.
9219 self.minutes = min
9220 self.minutes.setOwner( self )
9221 min.recover()
9222 self.notifyModification()
9223 return self.minutes
9225 def getMinutes( self ):
9226 #To be removed
9227 try:
9228 if self.minutes:
9229 pass
9230 except AttributeError, e:
9231 self.minutes = None
9232 return self.minutes
9234 def setReviewing( self, newReviewing ):
9235 if self.getReviewing() != None:
9236 raise MaKaCError( _("The reviewing maretial for this contribution has already been set"), _("Contribution"))
9237 self.reviewing=newReviewing
9238 self.reviewing.setOwner( self )
9239 self.notifyModification()
9241 def removeReviewing( self ):
9242 if self.getReviewing() is None:
9243 return
9244 self.reviewing.delete()
9245 self.reviewing.setOwner(None)
9246 self.reviewing = None
9247 self.notifyModification()
9249 def recoverReviewing(self, p):
9250 self.setReviewing(p)
9251 p.recover()
9253 def getReviewing( self ):
9254 try:
9255 if self.reviewing:
9256 pass
9257 except AttributeError, e:
9258 self.reviewing = None
9259 return self.reviewing
9261 def getMasterSchedule( self ):
9262 return self.getOwner().getSchedule()
9264 def requireDomain( self, dom ):
9265 self.__ac.requireDomain( dom )
9267 def freeDomain( self, dom ):
9268 self.__ac.freeDomain( dom )
9270 def getDomainList( self ):
9271 return self.__ac.getRequiredDomainList()
9273 def getTrack( self ):
9274 try:
9275 if self._track:
9276 pass
9277 except AttributeError:
9278 self._track = None
9279 return self._track
9281 def setTrack( self, newTrack ):
9282 currentTrack = self.getTrack()
9283 if newTrack == currentTrack:
9284 return
9285 if currentTrack:
9286 currentTrack.removeContribution( self )
9287 self._track = newTrack
9288 if self._track:
9289 self._track.addContribution( self )
9291 def removeTrack(self, track):
9292 if track == self._track:
9293 self._track = None
9295 def setType( self, newType ):
9296 self._type = newType
9298 def getType( self ):
9299 try:
9300 if self._type:
9301 pass
9302 except AttributeError:
9303 self._type = None
9304 return self._type
9306 def getModificationDate( self ):
9307 """Returns the date in which the contribution was last modified"""
9308 try:
9309 return self._modificationDS
9310 except:
9311 if self.getConference():
9312 self._modificationDS = self.getConference().getModificationDate()
9313 else:
9314 self._modificationDS = nowutc()
9315 return self._modificationDS
9317 def getCurrentStatus(self):
9318 try:
9319 if self._status:
9320 pass
9321 except AttributeError:
9322 self._status=ContribStatusNotSch(self)
9323 return self._status
9324 getStatus = getCurrentStatus
9326 def setStatus(self,newStatus):
9329 self._status=newStatus
9331 def withdraw(self,resp,comment):
9332 """ Remove or put a contribution in a conference
9335 if self.isWithdrawn():
9336 #put back the authors in the author index
9337 for auth in self.getAuthorList():
9338 self.getConference().getAuthorIndex().index(auth)
9339 for spk in self.getSpeakerList():
9340 self.getConference().getSpeakerIndex().index(spk)
9341 #change the status of the contribution
9342 self._status=ContribStatusNotSch(self)
9344 else:
9345 #remove the authors from the author index
9346 if self.getConference() is not None:
9347 for auth in self.getAuthorList():
9348 self.getConference().getAuthorIndex().unindex(auth)
9349 for spk in self.getSpeakerList():
9350 self.getConference().unindexSpeaker(spk)
9351 #remove the contribution from any schedule it is included
9352 if self.isScheduled():
9353 self.getSchEntry().getSchedule().removeEntry(self.getSchEntry())
9354 self.getCurrentStatus().withdraw(resp,comment)
9357 def getSubmitterList(self, no_groups=False):
9358 try:
9359 if self._submitters:
9360 pass
9361 except AttributeError:
9362 self._submitters=[] #create the attribute
9363 self.notifyModification(raiseEvent = False)
9364 if no_groups:
9365 return [s for s in self._submitters if not isinstance(s, GroupWrapper)]
9366 else:
9367 return self._submitters
9369 def _grantSubmission(self,av):
9370 if av not in self.getSubmitterList():
9371 self.getSubmitterList().append(av)
9372 if self.getConference() is not None:
9373 self.getConference().addContribSubmitter(self,av)
9374 if isinstance(av, AvatarUserWrapper):
9375 av.linkTo(self, "submission")
9376 self.notifyModification(raiseEvent = False)
9378 def _grantSubmissionEmail(self, email):
9379 """Returns True if submission email was granted. False if email was already in the list.
9381 if not email.lower() in map(lambda x: x.lower(), self.getSubmitterEmailList()):
9382 self.getSubmitterEmailList().append(email.lower().strip())
9383 return True
9384 return False
9386 def revokeSubmissionEmail(self, email):
9387 if email in self.getSubmitterEmailList():
9388 self.getSubmitterEmailList().remove(email)
9389 self._p_changed=1
9391 def grantSubmission(self, sb, sendEmail=True):
9392 """Grants a user with submission privileges for the contribution
9393 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9395 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9396 ah = AvatarHolder()
9397 results=ah.match({"email":sb.getEmail()}, exact=1, searchInAuthenticators=False)
9398 if not results:
9399 results=ah.match({"email":sb.getEmail()}, exact=1)
9400 r=None
9401 for i in results:
9402 if i.hasEmail(sb.getEmail()):
9404 break
9405 if r and r.isActivated():
9406 self._grantSubmission(r)
9407 elif sb.getEmail():
9408 self.getConference().getPendingQueuesMgr().addPendingSubmitter(sb, False)
9409 submissionEmailGranted = self._grantSubmissionEmail(sb.getEmail())
9410 if submissionEmailGranted and sendEmail:
9411 notif = pendingQueues._PendingSubmitterNotification( [sb] )
9412 mail.GenericMailer.sendAndLog( notif, self.getConference() )
9413 if self.getConference():
9414 self.getConference().addContribSubmitter(self,sb)
9415 else:
9416 self._grantSubmission(sb)
9418 def _revokeSubmission(self, av):
9419 if av in self.getSubmitterList():
9420 self.getSubmitterList().remove(av)
9421 if self.getConference():
9422 self.getConference().removeContribSubmitter(self, av)
9423 if isinstance(av, AvatarUserWrapper):
9424 av.unlinkTo(self, "submission")
9425 self.notifyModification(raiseEvent = False)
9427 def revokeSubmission(self, sb):
9428 """Removes submission privileges for the specified user
9429 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9431 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9432 ah = AvatarHolder()
9433 results = ah.match({"email": sb.getEmail()}, exact=1, searchInAuthenticators=False)
9434 r = None
9435 for i in results:
9436 if i.hasEmail(sb.getEmail()):
9438 break
9439 if r:
9440 self._revokeSubmission(r)
9441 else:
9442 self.revokeSubmissionEmail(sb.getEmail())
9443 else:
9444 self._revokeSubmission(sb)
9446 def revokeAllSubmitters(self):
9447 self._submitters = []
9448 self.notifyModification(raiseEvent = False)
9450 def getSubmitterEmailList(self):
9451 try:
9452 return self._submittersEmail
9453 except:
9454 self._submittersEmail = []
9455 return self._submittersEmail
9457 def canUserSubmit(self, sb):
9458 """Tells whether a user can submit material for the current contribution
9459 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9461 if sb is None:
9462 return False
9464 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9465 sbEmail = sb.getEmail()
9467 # Normally, we shouldn't get here unless we're adding someone as a Speaker or similar.
9468 # `no_groups` is used so that we do not consider group membership, as to not confuse the
9469 # user (since there will be speakers with "implicit" privileges) and avoid that hasEmail breaks
9470 return any(submitter.hasEmail(sbEmail) for submitter in self.getSubmitterList(no_groups=True)) or \
9471 any(submitterEmail == sbEmail for submitterEmail in self.getSubmitterEmailList())
9473 for principal in self.getSubmitterList():
9474 if principal != None and principal.containsUser(sb):
9475 return True
9477 return False
9479 def getAccessController(self):
9480 return self.__ac
9482 def getReportNumberHolder(self):
9483 try:
9484 if self._reportNumberHolder:
9485 pass
9486 except AttributeError, e:
9487 self._reportNumberHolder=ReportNumberHolder(self)
9488 return self._reportNumberHolder
9490 def setReportNumberHolder(self, rnh):
9491 self._reportNumberHolder=rnh
9493 @classmethod
9494 def contributionStartDateForSort(cls, contribution):
9495 """ Function that can be used as "key" argument to sort a list of contributions by start date
9496 The contributions with no start date will be at the end with this sort
9498 if contribution.getStartDate():
9499 return contribution.getStartDate()
9500 else:
9501 return maxDatetime()
9503 def getColor(self):
9504 res=""
9505 if self.getSession() is not None:
9506 res=self.getSession().getColor()
9507 return res
9509 def getTextColor(self):
9510 res=""
9511 if self.getSession() is not None:
9512 res=self.getSession().getTextColor()
9513 return res
9516 class AcceptedContribution(Contribution):
9517 """This class represents a contribution which has been created from an
9518 abstract
9521 def __init__(self, abstract):
9522 Contribution.__init__(self)
9523 abstract.getConference().addContribution(self, abstract.getId())
9524 self._abstract = abstract
9525 self.setTitle(abstract.getTitle())
9526 self._setFieldsFromAbstract()
9527 if isinstance(abstract.getCurrentStatus(), review.AbstractStatusAccepted):
9528 self.setTrack(abstract.getCurrentStatus().getTrack())
9529 self.setType(abstract.getCurrentStatus().getType())
9530 for auth in abstract.getAuthorList():
9531 c_auth = ContributionParticipation()
9532 self._setAuthorValuesFromAbstract(c_auth, auth)
9533 if abstract.isPrimaryAuthor(auth):
9534 self.addPrimaryAuthor(c_auth)
9535 else:
9536 self.addCoAuthor(c_auth)
9537 if abstract.isSpeaker(auth):
9538 self.addSpeaker(c_auth)
9539 self._grantSubmission(self.getAbstract().getSubmitter().getUser())
9541 def _setAuthorValuesFromAbstract(self, cAuth, aAuth):
9542 cAuth.setTitle(aAuth.getTitle())
9543 cAuth.setFirstName(aAuth.getFirstName())
9544 cAuth.setFamilyName(aAuth.getSurName())
9545 cAuth.setEmail(aAuth.getEmail())
9546 cAuth.setAffiliation(aAuth.getAffiliation())
9547 cAuth.setAddress(aAuth.getAddress())
9548 cAuth.setPhone(aAuth.getTelephone())
9550 def _setFieldsFromAbstract(self):
9551 for k, v in self._abstract.getFields().iteritems():
9552 self.setField(k, v)
9554 def getAbstract(self):
9555 return self._abstract
9557 def setAbstract(self, abs):
9558 self._abstract = abs
9560 def getSubmitterList(self, no_groups=False):
9561 try:
9562 if self._submitters:
9563 pass
9564 except AttributeError:
9565 self._submitters = [] # create the attribute
9566 self._grantSubmission(self.getAbstract().getSubmitter().getUser())
9567 if no_groups:
9568 return [s for s in self._submitters if not isinstance(s, GroupWrapper)]
9569 else:
9570 return self._submitters
9572 def delete(self):
9573 """deletes a contribution and all of their subitems
9575 abs = self.getAbstract()
9576 if abs:
9577 cs = abs.getCurrentStatus()
9578 if isinstance(cs, review.AbstractStatusAccepted):
9579 if cs.getTrack() is not None:
9580 abs.addTrack(cs.getTrack())
9581 abs.setCurrentStatus(review.AbstractStatusSubmitted(abs))
9582 abs._setContribution(None)
9583 self.setAbstract(None)
9584 Contribution.delete(self)
9587 class ContribStatus(Persistent):
9591 def __init__(self,contribution,responsible):
9592 self._setContrib(contribution)
9593 self._setResponsible(responsible)
9594 self._setDate()
9596 def clone(self, contribution, responsible):
9597 cs = ContribStatus(contribution, responsible)
9598 cs.setDate(self.getDate())
9599 return cs
9601 def _setContrib(self,newContrib):
9602 self._contrib=newContrib
9604 def getContrib(self):
9605 return self._contrib
9607 def _setResponsible(self,newResp):
9608 self._responsible=newResp
9610 def getResponsible(self):
9611 return self._responsible
9613 def _setDate(self):
9614 self._date=nowutc()
9616 def setDate(self, date):
9617 self._date = date
9619 def getDate(self):
9620 return self._date
9622 def withdraw(self,resp,comments=""):
9623 self._contrib.setStatus(ContribStatusWithdrawn(self.getContrib(),resp,comments))
9625 class ContribStatusNotSch(ContribStatus):
9628 def __init__(self,contrib):
9629 ContribStatus.__init__(self,contrib,None)
9631 def clone(self, contribution):
9632 csns = ContribStatusNotSch(contribution)
9633 csns.setDate(self.getDate())
9634 return csns
9636 ContribStatusSubmitted=ContribStatusNotSch
9638 class ContribStatusSch(ContribStatus):
9641 def __init__(self,contrib):
9642 ContribStatus.__init__(self,contrib,None)
9644 def clone(self, contribution):
9645 css = ContribStatusSch(contribution)
9646 css.setDate(self.getDate())
9647 return css
9649 class ContribStatusWithdrawn(ContribStatus):
9652 def __init__(self,contrib,resp,comments):
9653 ContribStatus.__init__(self,contrib,resp)
9654 self._setComment(comments)
9656 def clone(self, contribution):
9657 csw = ContribStatusWithdrawn(contribution)
9658 csw.setDate(self.getDate())
9659 csw.setComment(self.getComment())
9660 return csw
9662 def _setComment(self,text):
9663 self._comment=text.strip()
9665 def getComment(self):
9666 return self._comment
9668 class ContribStatusNone(ContribStatus):
9669 # This is a special status we assign to contributions that are put in the trash can.
9671 def __init__(self,contrib):
9672 ContribStatus.__init__(self,contrib,None)
9674 def clone(self, contribution):
9675 csn = ContribStatusNone(contribution)
9676 csn.setDate(self.getDate())
9677 return csn
9679 class SubContribParticipation(Persistent, Fossilizable):
9681 fossilizes(ISubContribParticipationFossil, ISubContribParticipationFullFossil)
9683 def __init__( self ):
9684 self._subContrib = None
9685 self._id = ""
9686 self._firstName = ""
9687 self._surName = ""
9688 self._email = ""
9689 self._affiliation = ""
9690 self._address = ""
9691 self._phone = ""
9692 self._title = ""
9693 self._fax = ""
9695 def getConference(self):
9696 if self._subContrib is not None:
9697 return self._subContrib.getConference()
9698 return None
9700 def _notifyModification( self ):
9701 if self._subContrib != None:
9702 self._subContrib.notifyModification()
9704 def setValues(self, data):
9705 self.setFirstName(data.get("firstName", ""))
9706 self.setFamilyName(data.get("familyName",""))
9707 self.setAffiliation(data.get("affilation",""))
9708 self.setAddress(data.get("address",""))
9709 self.setEmail(data.get("email",""))
9710 self.setFax(data.get("fax",""))
9711 self.setTitle(data.get("title",""))
9712 self.setPhone(data.get("phone",""))
9713 self._notifyModification()
9715 def getValues(self):
9716 data={}
9717 data["firstName"]=self.getFirstName()
9718 data["familyName"]=self.getFamilyName()
9719 data["affilation"]=self.getAffiliation()
9720 data["address"]=self.getAddress()
9721 data["email"]=self.getEmail()
9722 data["fax"]=self.getFax()
9723 data["title"]=self.getTitle()
9724 data["phone"]=self.getPhone()
9725 return data
9727 def clone(self):
9728 part = SubContribParticipation()
9729 part.setValues(self.getValues())
9730 return part
9732 def setDataFromAvatar(self,av):
9733 # av is an Avatar object.
9734 if av is None:
9735 return
9736 self.setFirstName(av.getName())
9737 self.setFamilyName(av.getSurName())
9738 self.setEmail(av.getEmail())
9739 self.setAffiliation(av.getOrganisation())
9740 self.setAddress(av.getAddress())
9741 self.setPhone(av.getTelephone())
9742 self.setTitle(av.getTitle())
9743 self.setFax(av.getFax())
9744 self._notifyModification()
9746 def setDataFromAuthor(self,au):
9747 # au is a ContributionParticipation object.
9748 if au is None:
9749 return
9750 self.setFirstName(au.getFirstName())
9751 self.setFamilyName(au.getFamilyName())
9752 self.setEmail(au.getEmail())
9753 self.setAffiliation(au.getAffiliation())
9754 self.setAddress(au.getAddress())
9755 self.setPhone(au.getPhone())
9756 self.setTitle(au.getTitle())
9757 self.setFax(au.getFax())
9758 self._notifyModification()
9760 def setDataFromSpeaker(self,spk):
9761 # spk is a SubContribParticipation object.
9762 if spk is None:
9763 return
9764 self.setFirstName(spk.getFirstName())
9765 self.setFamilyName(spk.getFamilyName())
9766 self.setEmail(spk.getEmail())
9767 self.setAffiliation(spk.getAffiliation())
9768 self.setAddress(spk.getAddress())
9769 self.setPhone(spk.getPhone())
9770 self.setTitle(spk.getTitle())
9771 self.setFax(spk.getFax())
9772 self._notifyModification()
9774 def includeInSubContrib( self, subcontrib, id ):
9775 if self.getSubContrib() == subcontrib and self.getId()==id.strip():
9776 return
9777 self._subContrib = subcontrib
9778 self._id = id
9780 def delete( self ):
9781 self._subContrib = None
9782 TrashCanManager().add(self)
9784 def recover(self):
9785 TrashCanManager().remove(self)
9787 @Updates ('MaKaC.conference.SubContribParticipation', 'id')
9788 def setId(self, newId):
9789 self._id = newId
9791 def getId( self ):
9792 return self._id
9794 def getSubContrib( self ):
9795 return self._subContrib
9797 def getContribution( self ):
9798 if self._subContrib is not None:
9799 return self._subContrib.getContribution()
9800 return None
9802 def _unindex(self):
9803 contrib=self.getContribution()
9804 if contrib is not None:
9805 conf=contrib.getConference()
9806 if conf is not None:
9807 conf.unindexAuthor(self)
9808 conf.unindexSpeaker(self)
9810 def _index(self):
9811 contrib=self.getContribution()
9812 if contrib is not None:
9813 conf=contrib.getConference()
9814 if conf is not None:
9815 conf.indexAuthor(self)
9816 conf.indexSpeaker(self)
9818 @Updates ('MaKaC.conference.SubContribParticipation', 'firstName')
9819 def setFirstName( self, newName ):
9820 tmp=newName.strip()
9821 if tmp==self._firstName:
9822 return
9823 self._unindex()
9824 self._firstName=tmp
9825 self._index()
9826 self._notifyModification()
9828 def getFirstName( self ):
9829 return self._firstName
9831 def getName( self ):
9832 return self._firstName
9834 @Updates ('MaKaC.conference.SubContribParticipation', 'familyName')
9835 def setFamilyName( self, newName ):
9836 tmp=newName.strip()
9837 if tmp==self._surName:
9838 return
9839 self._unindex()
9840 self._surName=tmp
9841 self._index()
9842 self._notifyModification()
9844 def getFamilyName( self ):
9845 return self._surName
9847 def getSurName( self ):
9848 return self._surName
9850 @Updates ('MaKaC.conference.SubContribParticipation', 'email')
9851 def setEmail( self, newMail ):
9852 tmp=newMail.strip()
9853 if tmp==self._email:
9854 return
9855 self._unindex()
9856 self._email=newMail.strip()
9857 self._index()
9858 self._notifyModification()
9860 def getEmail( self ):
9861 return self._email
9863 @Updates ('MaKaC.conference.SubContribParticipation', 'affiliation')
9864 def setAffiliation( self, newAffil ):
9865 self._affiliation = newAffil.strip()
9866 self._notifyModification()
9868 def getAffiliation( self ):
9869 return self._affiliation
9871 @Updates ('MaKaC.conference.SubContribParticipation', 'address')
9872 def setAddress( self, newAddr ):
9873 self._address = newAddr.strip()
9874 self._notifyModification()
9876 def getAddress( self ):
9877 return self._address
9879 @Updates ('MaKaC.conference.SubContribParticipation', 'phone')
9880 def setPhone( self, newPhone ):
9881 self._phone = newPhone.strip()
9882 self._notifyModification()
9884 def getPhone( self ):
9885 return self._phone
9887 @Updates ('MaKaC.conference.SubContribParticipation', 'title')
9888 def setTitle( self, newTitle ):
9889 self._title = newTitle.strip()
9890 self._notifyModification()
9892 def getTitle( self ):
9893 return self._title
9895 def setFax( self, newFax ):
9896 self._fax = newFax.strip()
9897 self._notifyModification()
9899 def getFax( self ):
9900 try:
9901 if self._fax:
9902 pass
9903 except AttributeError:
9904 self._fax=""
9905 return self._fax
9907 def getFullName( self ):
9908 res = self.getFullNameNoTitle()
9909 if self.getTitle() != "":
9910 res = "%s %s"%( self.getTitle(), res )
9911 return res
9913 def getFullNameNoTitle(self):
9914 res = safe_upper(self.getFamilyName())
9915 if self.getFirstName():
9916 if res.strip():
9917 res = "%s, %s" % (res, self.getFirstName())
9918 else:
9919 res = self.getFirstName()
9920 return res
9922 def getAbrName(self):
9923 res = self.getFamilyName()
9924 if self.getFirstName():
9925 if res:
9926 res = "%s, " % res
9927 res = "%s%s." % (res, safe_upper(self.getFirstName()[0]))
9928 return res
9930 def getDirectFullName(self):
9931 res = self.getDirectFullNameNoTitle()
9932 if self.getTitle():
9933 res = "%s %s" % (self.getTitle(), res)
9934 return res
9936 def getDirectFullNameNoTitle(self, upper=True):
9937 surName = safe_upper(self.getFamilyName()) if upper else self.getFamilyName()
9938 return "{0} {1}".format(self.getFirstName(), surName).strip()
9941 class SubContribution(CommonObjectBase, Locatable):
9945 fossilizes(ISubContributionFossil, ISubContributionWithSpeakersFossil)
9947 def __init__( self, **subContData ):
9948 self.parent = None
9949 self.id = ""
9950 self.title = ""
9951 self.description = ""
9952 self.__schEntry = None
9953 self.duration = timedelta( minutes=15 )
9954 self.speakers = []
9955 self.speakerText = ""
9957 self.materials = {}
9958 self.__materialGenerator = Counter() # Provides material unique
9959 # identifiers whithin the current
9960 self.poster = None # contribution
9961 self.paper = None
9962 self.slides = None
9963 self.video = None
9964 self.poster = None
9965 self.minutes = None
9966 self._authorGen = Counter()
9967 self._keywords = ""
9969 @return_ascii
9970 def __repr__(self):
9971 if self.parent:
9972 parent_id = self.parent.getId()
9973 event_id = self.getConference().getId() if self.getConference() else None
9974 else:
9975 parent_id = None
9976 event_id = None
9977 return '<SubContribution({}, {}, {}.{})>'.format(self.getId(), self.getTitle(), event_id, parent_id)
9979 def updateNonInheritingChildren(self, elem, delete=False):
9980 self.getOwner().updateNonInheritingChildren(elem, delete)
9982 def getAccessController(self):
9983 return self.getOwner().getAccessController()
9985 def getKeywords(self):
9986 try:
9987 return self._keywords
9988 except:
9989 self._keywords = ""
9990 return ""
9992 def setKeywords(self, keywords):
9993 self._keywords = keywords
9995 def getLogInfo(self):
9996 data = {}
9998 data["subject"] = self.getTitle()
9999 data["id"] = self.id
10000 data["title"] = self.title
10001 data["parent title"] = self.getParent().getTitle()
10002 data["description"] = self.description
10003 data["duration"] = "%s"%self.duration
10004 data["minutes"] = self.minutes
10006 for sp in self.speakers :
10007 data["speaker %s"%sp.getId()] = sp.getFullName()
10009 return data
10012 def clone(self, deltaTime, parent, options):
10013 sCont = SubContribution()
10014 sCont.setParent(parent)
10015 sCont.setTitle(self.getTitle())
10016 sCont.setDescription(self.getDescription())
10017 sCont.setKeywords(self.getKeywords())
10018 dur = self.getDuration()
10019 hours = dur.seconds / 3600
10020 minutes = (dur.seconds % 3600) / 60
10021 sCont.setDuration(hours, minutes)
10022 sCont.setReportNumberHolder(self.getReportNumberHolder().clone(self))
10024 # There is no _order attribute in this class
10026 if options.get("authors", False) :
10027 for s in self.getSpeakerList() :
10028 sCont.newSpeaker(s.clone())
10029 sCont.setSpeakerText(self.getSpeakerText())
10031 if options.get("materials", False) :
10032 for m in self.getMaterialList() :
10033 sCont.addMaterial(m.clone(sCont))
10034 if self.getPaper() is not None:
10035 sCont.setPaper(self.getPaper().clone(sCont))
10036 if self.getSlides() is not None:
10037 sCont.setSlides(self.getSlides().clone(sCont))
10038 if self.getVideo() is not None:
10039 sCont.setVideo(self.getVideo().clone(sCont))
10040 if self.getPoster() is not None:
10041 sCont.setPoster(self.getPoster().clone(sCont))
10042 if self.getMinutes() is not None:
10043 sCont.setMinutes(self.getMinutes().clone(sCont))
10046 sCont.notifyModification()
10047 return sCont
10049 def notifyModification(self, raiseEvent = True):
10050 parent = self.getParent()
10051 if parent:
10052 parent.setModificationDate()
10053 if raiseEvent:
10054 signals.event.subcontribution_data_changed.send(self)
10055 self._p_changed = 1
10057 def getCategoriesPath(self):
10058 return self.getConference().getCategoriesPath()
10060 def getLocator( self ):
10061 """Gives back a globaly unique identification encapsulated in a Locator
10062 object for the contribution instance
10065 lconf = self.getOwner().getLocator()
10066 lconf["subContId"] = self.getId()
10067 return lconf
10070 def setId( self, newId ):
10071 self.id = newId
10073 def getId( self ):
10074 return self.id
10076 def getUniqueId( self ):
10077 """returns (string) the unique identifier of the item"""
10078 """used mainly in the web session access key table"""
10079 return "%ssc%s" % (self.getParent().getUniqueId(),self.id)
10081 def setTitle( self, newTitle ):
10082 old_title = self.title
10083 self.title = newTitle.strip()
10084 if old_title != self.title:
10085 signals.event.subcontribution_title_changed.send(self, old=old_title, new=self.title)
10086 self.notifyModification()
10088 def getTitle( self ):
10089 if self.title.strip() == "":
10090 return "(no title)"
10091 return self.title
10093 def setDescription( self, newDesc ):
10094 self.description = newDesc.strip()
10095 self.notifyModification()
10097 def getDescription( self ):
10098 return self.description
10100 def setParent(self,parent):
10101 self.parent = parent
10102 if self.parent == None:
10103 return
10105 def getParent( self ):
10106 return self.parent
10108 def setOwner(self, owner):
10109 self.setParent(owner)
10111 def getOwner( self ):
10112 return self.getParent()
10114 def getConference( self ):
10115 return self.parent.getConference()
10117 def getSession( self ):
10118 return self.parent.getSession()
10120 def getContribution(self):
10121 return self.parent
10123 def getDuration( self ):
10124 return self.duration
10126 def setDuration( self, hours, minutes=0, dur=0 ):
10127 if dur!=0:
10128 self.duration=dur
10129 else:
10130 hours = int( hours )
10131 minutes = int( minutes )
10132 self.duration = timedelta(hours=hours,minutes=minutes )
10133 self.notifyModification()
10135 def getLocation( self ):
10136 return self.getOwner().getLocation()
10138 def getRoom( self ):
10139 return self.getOwner().getRoom()
10141 def getSpeakerById( self, id ):
10144 for spk in self.speakers:
10145 if spk.getId() == id:
10146 return spk
10147 return None
10149 def newSpeaker( self, spk ):
10152 self.speakers.append( spk )
10153 try:
10154 if self._authorGen:
10155 pass
10156 except AttributeError:
10157 self._authorGen=Counter()
10158 newId = spk.getId()
10159 if newId == "":
10160 newId = str( self._authorGen.newCount() )
10161 spk.includeInSubContrib(self, newId)
10162 if self.getConference() is not None:
10163 self.getConference().indexSpeaker(spk)
10164 self.notifyModification()
10166 def removeSpeaker( self, spk ):
10169 if spk not in self.speakers:
10170 return
10171 self.speakers.remove( spk )
10172 if self.getConference() is not None:
10173 self.getConference().unindexSpeaker(spk)
10174 spk.delete()
10175 self.notifyModification()
10177 def recoverSpeaker(self, spk):
10178 self.newSpeaker(spk)
10179 spk.recover()
10181 def isSpeaker( self, spk):
10184 return spk in self._speakers
10186 def getSpeakerList ( self ):
10189 return self.speakers
10191 def getSpeakerText( self ):
10192 #to be removed
10193 try:
10194 if self.speakerText:
10195 pass
10196 except AttributeError, e:
10197 self.speakerText = ""
10198 return self.speakerText
10200 def setSpeakerText( self, newText ):
10201 self.speakerText = newText.strip()
10203 def appendSpeakerText( self, newText ):
10204 self.setSpeakerText( "%s, %s"%(self.getSpeakerText(), newText.strip()) )
10206 # """
10207 # There is no _order attribute in this class -
10208 # the methods below are either obsolate or the feature has not been implemented
10209 # """
10210 # def setOrder( self, order ):
10211 # self._order = order
10212 # self.notifyModification()
10214 # def getOrder(self):
10215 # return self._order
10217 def canIPAccess( self, ip ):
10218 return self.getOwner().canIPAccess(ip)
10220 def isProtected( self ):
10221 return self.hasProtectedOwner()
10223 def getAccessProtectionLevel( self ):
10224 return self.getOwner().getAccessProtectionLevel()
10226 def hasAnyProtection( self ):
10227 """Tells whether a subContribution has any kind of protection over it:
10228 access or domain protection.
10230 return self.getOwner().hasAnyProtection()
10232 def getManagerList( self ):
10233 return self.parent.getManagerList()
10235 def hasProtectedOwner( self ):
10236 if self.getOwner() != None:
10237 return self.getOwner().isProtected()
10238 return False
10240 def getAccessKey( self ):
10241 return self.getOwner().getAccessKey()
10243 def getModifKey( self ):
10244 return self.getConference().getModifKey()
10246 def canView( self, aw ):
10247 """tells whether the specified user has access to the current object
10248 or any of its sub-objects
10250 if self.canAccess( aw ):
10251 return True
10252 return False
10254 def isAllowedToAccess( self, user ):
10255 return self.parent.isAllowedToAccess( user )
10257 def canAccess( self, aw ):
10258 return self.getOwner().canAccess(aw)
10260 def canModify(self, aw_or_user):
10261 if hasattr(aw_or_user, 'getUser'):
10262 aw_or_user = aw_or_user.getUser()
10263 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
10265 def canUserModify( self, av ):
10266 """Tells whether a user is allowed to modify the current contribution:
10267 only if the user is granted to modify the contribution or the user
10268 can modify any of its upper objects (i.e. conference or session).
10270 return self.getParent().canUserModify( av )
10272 def canUserSubmit( self, av ):
10273 return self.getOwner().canUserSubmit( av )
10275 def getAllowedToAccessList( self ):
10276 """Currently the SubContribution class has no access list.
10277 But instead of returning the owner Contribution's access list,
10278 I am returning an empty list. Methods such as getRecursiveAllowedToAccess()
10279 will call the owner Contribution anyway.
10281 return []
10283 def addMaterial( self, newMat ):
10284 newMat.setId( str(self.__materialGenerator.newCount()) )
10285 newMat.setOwner( self )
10286 self.materials[ newMat.getId() ] = newMat
10287 self.notifyModification()
10289 def removeMaterial( self, mat):
10290 if mat.getId() in self.materials.keys():
10291 mat.delete()
10292 self.materials[mat.getId()].setOwner(None)
10293 del self.materials[ mat.getId() ]
10294 self.notifyModification()
10295 elif mat.getId().lower() == 'paper':
10296 self.removePaper()
10297 self.notifyModification()
10298 elif mat.getId().lower() == 'slides':
10299 self.removeSlides()
10300 self.notifyModification()
10301 elif mat.getId().lower() == 'minutes':
10302 self.removeMinutes()
10303 self.notifyModification()
10304 elif mat.getId().lower() == 'video':
10305 self.removeVideo()
10306 self.notifyModification()
10307 elif mat.getId().lower() == 'poster':
10308 self.removePoster()
10309 self.notifyModification()
10311 def recoverMaterial(self, recMat):
10312 # Id must already be set in recMat.
10313 recMat.setOwner( self )
10314 self.materials[ recMat.getId() ] = recMat
10315 recMat.recover()
10316 self.notifyModification()
10318 def getMaterialRegistry(self):
10320 Return the correct material registry for this type
10322 from MaKaC.webinterface.materialFactories import SubContributionMFRegistry
10323 return SubContributionMFRegistry
10325 def getMaterialById( self, matId ):
10326 if matId.lower() == 'paper':
10327 return self.getPaper()
10328 elif matId.lower() == 'slides':
10329 return self.getSlides()
10330 elif matId.lower() == 'video':
10331 return self.getVideo()
10332 elif matId.lower() == 'poster':
10333 return self.getPoster()
10334 elif matId.lower() == 'minutes':
10335 return self.getMinutes()
10336 elif self.materials.has_key(matId):
10337 return self.materials[ matId ]
10338 return None
10340 def getMaterialList( self ):
10341 return self.materials.values()
10343 def getAllMaterialList(self, sort=True):
10344 l = self.getMaterialList()
10345 if self.getPaper():
10346 l.append( self.getPaper() )
10347 if self.getSlides():
10348 l.append( self.getSlides() )
10349 if self.getVideo():
10350 l.append( self.getVideo() )
10351 if self.getMinutes():
10352 l.append( self.getMinutes() )
10353 if self.getPoster():
10354 l.append( self.getPoster() )
10355 if sort:
10356 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
10357 return l
10359 def setPaper( self, newPaper ):
10360 if self.getPaper() != None:
10361 raise MaKaCError( _("The paper for this subcontribution has already been set"), _("Contribution"))
10362 self.paper=newPaper
10363 self.paper.setOwner( self )
10364 self.notifyModification()
10366 def removePaper( self ):
10367 if self.getPaper() is None:
10368 return
10369 self.paper.delete()
10370 self.paper.setOwner(None)
10371 self.paper = None
10372 self.notifyModification()
10374 def recoverPaper(self, p):
10375 self.setPaper(p)
10376 p.recover()
10378 def getPaper( self ):
10379 return self.paper
10381 def setSlides( self, newSlides ):
10382 if self.getSlides() != None:
10383 raise MaKaCError( _("The slides for this subcontribution have already been set"), _("Contribution"))
10384 self.slides=newSlides
10385 self.slides.setOwner( self )
10386 self.notifyModification()
10388 def removeSlides( self ):
10389 if self.getSlides() is None:
10390 return
10391 self.slides.delete()
10392 self.slides.setOwner( None )
10393 self.slides = None
10394 self.notifyModification()
10396 def recoverSlides(self, s):
10397 self.setSlides(s)
10398 s.recover()
10400 def getSlides( self ):
10401 return self.slides
10403 def setVideo( self, newVideo ):
10404 if self.getVideo() != None:
10405 raise MaKaCError( _("the video for this subcontribution has already been set"))
10406 self.video=newVideo
10407 self.video.setOwner( self )
10408 self.notifyModification()
10410 def removeVideo( self ):
10411 if self.getVideo() is None:
10412 return
10413 self.video.delete()
10414 self.video.setOwner(None)
10415 self.video = None
10416 self.notifyModification()
10418 def recoverVideo(self, v):
10419 self.setVideo(v)
10420 v.recover()
10422 def getVideo( self ):
10423 try:
10424 if self.video:
10425 pass
10426 except AttributeError:
10427 self.video = None
10428 return self.video
10430 def setPoster( self, newPoster ):
10431 if self.getPoster() != None:
10432 raise MaKaCError( _("the poster for this subcontribution has already been set"))
10433 self.poster=newPoster
10434 self.poster.setOwner( self )
10435 self.notifyModification()
10437 def removePoster( self ):
10438 if self.getPoster() is None:
10439 return
10440 self.poster.delete()
10441 self.poster.setOwner(None)
10442 self.poster = None
10443 self.notifyModification()
10445 def recoverPoster(self, p):
10446 self.setPoster(p)
10447 p.recover()
10449 def getPoster( self ):
10450 try:
10451 if self.poster:
10452 pass
10453 except AttributeError:
10454 self.poster = None
10455 return self.poster
10457 def setMinutes( self, newMinutes ):
10458 if self.getMinutes() != None:
10459 raise MaKaCError( _("the Minutes for this subcontribution has already been set"))
10460 self.minutes=newMinutes
10461 self.minutes.setOwner( self )
10462 self.notifyModification()
10464 def createMinutes( self ):
10465 if self.getMinutes() != None:
10466 raise MaKaCError( _("The minutes for this subcontribution have already been created"), _("Sub Contribution"))
10467 self.minutes = Minutes()
10468 self.minutes.setOwner( self )
10469 self.notifyModification()
10470 return self.minutes
10472 def removeMinutes( self ):
10473 if self.getMinutes() is None:
10474 return
10475 self.minutes.delete()
10476 self.minutes.setOwner( None )
10477 self.minutes = None
10478 self.notifyModification()
10480 def recoverMinutes(self, min):
10481 self.removeMinutes() # To ensure that the current minutes are put in
10482 # the trash can.
10483 self.minutes = min
10484 self.minutes.setOwner( self )
10485 min.recover()
10486 self.notifyModification()
10487 return self.minutes
10489 def getMinutes( self ):
10490 #To be removed
10491 try:
10492 if self.minutes:
10493 pass
10494 except AttributeError, e:
10495 self.minutes = None
10496 return self.minutes
10498 def getMasterSchedule( self ):
10499 return self.getOwner().getSchedule()
10501 def delete(self):
10502 signals.event.subcontribution_deleted.send(self, parent=self.getOwner())
10504 while len(self.getSpeakerList()) > 0:
10505 self.removeSpeaker(self.getSpeakerList()[0])
10506 for mat in self.getMaterialList():
10507 self.removeMaterial(mat)
10508 self.removePaper()
10509 self.removeSlides()
10510 self.removeVideo()
10511 self.removePoster()
10512 self.removeMinutes()
10513 TrashCanManager().add(self)
10515 #self.unindex()
10517 def recover(self):
10518 TrashCanManager().remove(self)
10520 def getReportNumberHolder(self):
10521 try:
10522 if self._reportNumberHolder:
10523 pass
10524 except AttributeError, e:
10525 self._reportNumberHolder=ReportNumberHolder(self)
10526 return self._reportNumberHolder
10528 def setReportNumberHolder(self, rnh):
10529 self._reportNumberHolder=rnh
10531 class Material(CommonObjectBase):
10532 """This class represents a set of electronic documents (resources) which can
10533 be attached to a conference, a session or a contribution.
10534 A material can be of several types (achieved by specialising this class)
10535 and is like a container of files which have some relation among them.
10536 It contains the minimal set of attributes to store basic meta data and
10537 provides useful operations to access and manage it.
10538 Attributes:
10539 owner -- (Conference, Session or Contribution) Object to which the
10540 material is attached to
10541 id -- (string) Material unique identifier. Normally used to uniquely
10542 identify a material within a conference, session or contribution
10543 title -- (string) Material denomination
10544 description -- (string) Longer text describing in more detail material
10545 intentions
10546 type -- (string) String identifying the material classification
10547 resources -- (PMapping) Collection of resouces grouped within the
10548 material. Dictionary of references to Resource objects indexed
10549 by their unique relative id.
10552 fossilizes(IMaterialMinimalFossil, IMaterialFossil)
10554 def __init__( self, materialData=None ):
10555 self.id = "not assigned"
10556 self.__resources = {}
10557 self.__resourcesIdGen = Counter()
10558 self.title = ""
10559 self.description = ""
10560 self.type = ""
10561 self.owner = None
10562 self.__ac = AccessController(self)
10563 self._mainResource = None
10565 def __cmp__(self, other):
10566 if type(self) is not type(other):
10567 # This is actually dangerous and the ZODB manual says not to do this
10568 # because it relies on memory order. However, this branch should never
10569 # be taken anyway since we do not store different types in the same set
10570 # or use them as keys.
10571 return cmp(hash(self), hash(other))
10572 if self.getConference() == other.getConference():
10573 if self.getId().isdigit() and other.getId().isdigit():
10574 return cmp(int(self.getId()), int(other.getId()))
10575 else:
10576 return cmp(self.getId(), other.getId())
10577 return cmp(self.getConference(), other.getConference())
10579 def updateNonInheritingChildren(self, elem, delete=False):
10580 # We do not want to store the inherited children in a Category because the funcionallity is not used
10581 if not isinstance(self.getOwner(), Category):
10582 self.getAccessController().updateNonInheritingChildren(elem, delete)
10583 self.notify_protection_to_owner(elem, delete)
10585 def notify_protection_to_owner(self, elem, delete=False):
10586 self.getOwner().updateNonInheritingChildren(elem, delete)
10588 def setValues( self, params ):
10589 """Sets all the values of the current material object from a diccionary
10590 containing the following key-value pairs:
10591 title-(str)
10592 description-(str)
10593 Please, note that this method sets ALL values which means that if
10594 the given dictionary doesn't contain any of the keys the value
10595 will set to a default value.
10597 self.setTitle(params.get("title", "NO TITLE ASSIGNED"))
10598 self.setDescription( params.get( "description", "" ) )
10599 self.notifyModification()
10601 def clone ( self, owner):
10602 mat = type(self)()
10603 mat.setTitle(self.getTitle())
10604 mat.setDescription(self.getDescription())
10605 mat.notifyModification()
10607 mat.setId(self.getId())
10608 mat.setOwner(owner)
10609 mat.setType(self.getType())
10611 mat.setProtection(self.getAccessController()._getAccessProtection())
10612 mat.setAccessKey(self.getAccessKey())
10613 rlist = self.getResourceList()
10614 for r in rlist:
10615 newres = r.clone(mat)
10616 mat.addResource(newres)
10618 mat.setMainResource(self.getMainResource())
10620 return mat
10622 def notifyModification( self ):
10623 parent = self.getOwner()
10624 if parent:
10625 parent.notifyModification(raiseEvent = False)
10626 self._p_changed = 1
10628 def getLocator( self ):
10629 if self.owner == None:
10630 return Locator()
10631 lconf = self.owner.getLocator()
10632 lconf["materialId"] = self.getId()
10633 return lconf
10635 def setId( self, newId ):
10636 self.id = str(newId).strip()
10638 def getId( self ):
10639 return self.id
10641 def getUniqueId( self ):
10642 """returns (string) the unique identifier of the item"""
10643 """used mainly in the web session access key table"""
10644 return "%sm%s" % (self.getOwner().getUniqueId(),self.id)
10646 def setOwner(self, newOwner):
10647 self.owner = newOwner
10649 def getOwner( self ):
10650 return self.owner
10652 def getCategory( self ):
10653 if isinstance(self.getOwner(), Category):
10654 return self.getOwner()
10655 return None
10657 def getConference( self ):
10658 owner = self.getOwner()
10659 if owner is None or isinstance(owner, Category):
10660 return None
10661 elif isinstance(owner, Conference):
10662 return owner
10663 else:
10664 return owner.getConference()
10666 def getSession( self ):
10667 if self.getContribution():
10668 return self.getContribution().getSession()
10669 if isinstance(self.getOwner(), Session):
10670 return self.getOwner()
10671 if isinstance(self.getOwner(), SubContribution):
10672 return self.getOwner().getSession()
10673 return None
10675 def getContribution( self ):
10676 if self.getSubContribution():
10677 return self.getSubContribution().getContribution()
10678 if isinstance(self.getOwner(), Contribution):
10679 return self.getOwner()
10680 return None
10682 def getSubContribution( self ):
10683 if isinstance(self.getOwner(), SubContribution):
10684 return self.getOwner()
10685 return None
10687 @Updates (['MaKaC.conference.Material',
10688 'MaKaC.conference.Minutes',
10689 'MaKaC.conference.Paper',
10690 'MaKaC.conference.Slides',
10691 'MaKaC.conference.Video',
10692 'MaKaC.conference.Poster',
10693 'MaKaC.conference.Reviewing'],'title')
10694 def setTitle( self, newTitle ):
10695 self.title = newTitle.strip()
10696 self.notifyModification()
10698 def getTitle( self ):
10699 return self.title
10701 @Updates (['MaKaC.conference.Material',
10702 'MaKaC.conference.Minutes',
10703 'MaKaC.conference.Paper',
10704 'MaKaC.conference.Slides',
10705 'MaKaC.conference.Video',
10706 'MaKaC.conference.Poster',
10707 'MaKaC.conference.Reviewing'], 'description')
10708 def setDescription( self, newDescription ):
10709 self.description = newDescription.strip()
10710 self.notifyModification()
10712 def getDescription( self ):
10713 return self.description
10715 def setType( self, newType ):
10716 self.type = newType.strip()
10717 self.notifyModification()
10719 def getType( self ):
10720 return self.type
10722 def getReviewingState(self):
10723 """ Returns the reviewing state of a material.
10724 The state is represented by an integer:
10725 0 : there's no reviewing state because the material does not belong to a contribution, or the conference
10726 has not reviewing module enabled, or the module is enabled but the mode is "no reviewing"
10727 1 : the material is not subject to reviewing, because this kind of material is not reviewable in the conference
10728 2 : the material is subject to reviewing, but has not been submitted yet by the author
10729 3 : the material is subject to reviewing, has been submitted by the author, but has not been judged yet
10730 4 : the material is subject to reviewing, has been submitted by the author, and has been judged as Accepted
10731 5 : the material is subject to reviewing, has been submitted by the author, and has been judged as Rejected
10733 if isinstance(self.owner, Contribution):
10734 conference = self.owner.getConference()
10735 if conference.getConfPaperReview().getChoice() == ConferencePaperReview.NO_REVIEWING: #conference has no reviewing process
10736 return 0
10737 else: #conference has reviewing
10738 #if self.id in reviewableMaterials: #material is reviewable
10739 if isinstance(self, Reviewing): #material is reviewable
10740 lastReview = self.owner.getReviewManager().getLastReview()
10741 if lastReview.isAuthorSubmitted(): #author has submitted
10742 refereeJudgement = lastReview.getRefereeJudgement()
10743 if refereeJudgement.isSubmitted(): #referee has submitted judgement
10744 if refereeJudgement.getJudgement() == "Accept":
10745 return 4
10746 elif refereeJudgement.getJudgement() == "Reject":
10747 return 5
10748 else:
10749 #we should never arrive here because referee judgements that are 'To be corrected'
10750 #or a custom state should imply a new review being created, so the state is back to 2
10751 raise MaKaCError("RefereeJudgement should be 'Accept' or 'Reject' in this method")
10752 else: #referee has not submitted judgement
10753 return 3
10754 else: #author has not submitted
10755 return 2
10756 else: #material is not reviewable
10757 return 1
10758 else: #material does not belong to a contribution
10759 return 0
10761 def _getRepository( self ):
10762 dbRoot = DBMgr.getInstance().getDBConnection().root()
10763 try:
10764 fr = dbRoot["local_repositories"]["main"]
10765 except KeyError, e:
10766 fr = fileRepository.MaterialLocalRepository()
10767 dbRoot["local_repositories"] = OOBTree()
10768 dbRoot["local_repositories"]["main"] = fr
10769 return fr
10771 def hasFile( self, name ):
10772 for f in self.getResourceList():
10773 if f.getName() == name:
10774 return True
10775 return False
10777 def addResource( self, newRes, forcedFileId = None ):
10778 newRes.setOwner( self )
10779 newRes.setId( str( self.__resourcesIdGen.newCount() ) )
10780 newRes.archive( self._getRepository(), forcedFileId = forcedFileId )
10781 self.__resources[newRes.getId()] = newRes
10782 self.notifyModification()
10783 Logger.get('storage').debug("Finished storing resource %s for material %s" % (newRes.getId(), self.getLocator()))
10785 def getResourceList(self, sort=True):
10786 list = self.__resources.values()
10787 if sort:
10788 list.sort(utils.sortFilesByName)
10789 return list
10791 def getNbResources(self ):
10792 return len(self.__resources)
10794 def getResourceById( self, id ):
10795 return self.__resources[id]
10797 def removeResource( self, res ):
10798 if res.getId() in self.__resources.keys():
10799 del self.__resources[ res.getId() ]
10800 res.delete()
10801 self.notifyModification()
10802 if self.getMainResource() is not None and \
10803 self._mainResource.getId() == res.getId():
10804 self._mainResource = None
10806 def recoverResource(self, recRes):
10807 recRes.setOwner(self)
10808 self.__resources[recRes.getId()] = recRes
10809 recRes.recover()
10810 self.notifyModification()
10812 def getMainResource(self):
10813 try:
10814 if self._mainResource:
10815 pass
10816 except AttributeError:
10817 self._mainResource = None
10818 return self._mainResource
10820 def setMainResource(self, mr):
10821 self._mainResource = mr
10823 def delete(self):
10824 self.__ac.unlinkAvatars('access')
10825 for res in self.getResourceList():
10826 self.removeResource( res )
10827 if self.getReviewingState():
10828 self.owner._reviewManager = ReviewManager(self.owner)
10829 self.notify_protection_to_owner(self, delete=True)
10830 TrashCanManager().add(self)
10832 def recover(self):
10833 TrashCanManager().remove(self)
10835 def isProtected(self):
10836 # tells if a material is protected or not
10837 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
10839 def getAccessProtectionLevel( self ):
10840 return self.__ac.getAccessProtectionLevel()
10842 def isItselfProtected( self ):
10843 return self.__ac.isItselfProtected()
10846 def hasProtectedOwner( self ):
10847 if self.getOwner() != None:
10848 return self.getOwner().isProtected()
10849 return False
10852 @Updates (['MaKaC.conference.Material',
10853 'MaKaC.conference.Minutes',
10854 'MaKaC.conference.Paper',
10855 'MaKaC.conference.Slides',
10856 'MaKaC.conference.Video',
10857 'MaKaC.conference.Poster',
10858 'MaKaC.conference.Reviewing'], 'protection', lambda(x): int(x))
10860 def setProtection( self, private ):
10861 self.__ac.setProtection( private )
10862 self.notify_protection_to_owner(self)
10863 self._p_changed = 1
10865 def isHidden( self ):
10866 return self.__ac.isHidden()
10868 @Updates (['MaKaC.conference.Material',
10869 'MaKaC.conference.Minutes',
10870 'MaKaC.conference.Paper',
10871 'MaKaC.conference.Slides',
10872 'MaKaC.conference.Video',
10873 'MaKaC.conference.Poster',
10874 'MaKaC.conference.Reviewing'], 'hidden')
10875 def setHidden( self, hidden ):
10876 self.__ac.setHidden( hidden )
10877 self._p_changed = 1
10880 @Updates (['MaKaC.conference.Material',
10881 'MaKaC.conference.Minutes',
10882 'MaKaC.conference.Paper',
10883 'MaKaC.conference.Slides',
10884 'MaKaC.conference.Video',
10885 'MaKaC.conference.Poster',
10886 'MaKaC.conference.Reviewing'], 'accessKey')
10888 def setAccessKey( self, pwd="" ):
10889 self.__ac.setAccessKey(pwd)
10890 self._p_changed = 1
10892 def getAccessKey( self ):
10893 return self.__ac.getAccessKey()
10895 def grantAccess( self, prin ):
10896 self.__ac.grantAccess( prin )
10897 if isinstance(prin, AvatarUserWrapper):
10898 prin.linkTo(self, "access")
10899 self._p_changed = 1
10901 def revokeAccess( self, prin ):
10902 self.__ac.revokeAccess( prin )
10903 if isinstance(prin, AvatarUserWrapper):
10904 prin.unlinkTo(self, "access")
10905 self._p_changed = 1
10907 def canView( self, aw ):
10908 """tells whether the specified user has access to the current object
10909 or any of its sub-objects
10911 if self.isHidden() and not self.canAccess( aw ):
10912 return False
10913 else:
10914 return True
10916 def isAllowedToAccess( self, user ):
10917 return (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user )) or self.__ac.canUserAccess( user ) or self.canUserModify(user)
10919 def canAccess( self, aw ):
10921 # Allow harvesters (Invenio, offline cache) to access
10922 # protected pages
10923 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
10924 return True
10925 #####################################################
10927 # Managers have always access
10928 if self.canModify(aw):
10929 return True
10931 canUserAccess = self.isAllowedToAccess(aw.getUser())
10932 canIPAccess = self.canIPAccess(request.remote_addr)
10933 if not self.isProtected():
10934 return canUserAccess or canIPAccess
10935 else:
10936 canKeyAccess = self.canKeyAccess(aw)
10937 return canUserAccess or canKeyAccess
10939 def canKeyAccess(self, aw):
10940 key = session.get('accessKeys', {}).get(self.getUniqueId())
10941 if self.getAccessKey():
10942 # Material has an access key => require this key
10943 if not key:
10944 return False
10945 return self.__ac.canKeyAccess(key)
10946 elif self.getConference():
10947 # If it has no key we check the conference's key
10948 conf_key = session.get('accessKeys', {}).get(self.getConference().getUniqueId())
10949 return self.getConference().canKeyAccess(aw, conf_key)
10950 return False
10952 def grantModification( self, prin ):
10953 self.__ac.grantModification( prin )
10954 if isinstance(prin, AvatarUserWrapper):
10955 prin.linkTo(self, "manager")
10956 self._p_changed = 1
10958 def revokeModification( self, prin ):
10959 self.__ac.revokeModification( prin )
10960 if isinstance(prin, AvatarUserWrapper):
10961 prin.unlinkTo(self, "manager")
10962 self._p_changed = 1
10964 def canModify(self, aw_or_user):
10965 if hasattr(aw_or_user, 'getUser'):
10966 aw_or_user = aw_or_user.getUser()
10967 return self.canUserModify(aw_or_user) or (self.getConference() and self.getConference().canKeyModify())
10969 def canUserModify( self, user ):
10970 """Tells whether a user is allowed to modify the current contribution:
10971 only if the user is granted to modify the contribution or the user
10972 can modify any of its upper objects (i.e. conference or session).
10974 return self.getOwner().canUserModify( user )
10976 def getModifKey( self ):
10977 return self.getConference().getModifKey()
10979 def getManagerList( self ):
10980 return self.__ac.getModifierList()
10982 def getAllowedToAccessList( self ):
10983 return self.__ac.getAccessList()
10985 def requireDomain( self, dom ):
10986 self.__ac.requireDomain( dom )
10987 self._p_changed = 1
10989 def freeDomain( self, dom ):
10990 self.__ac.freeDomain( dom )
10991 self._p_changed = 1
10993 def getDomainList( self ):
10994 return self.__ac.getRequiredDomainList()
10996 def getAccessController(self):
10997 return self.__ac
10999 def isBuiltin(self):
11000 return False
11002 class BuiltinMaterial(Material):
11004 Non-customizable material types
11006 def isBuiltin(self):
11007 return True
11010 class Reviewing(BuiltinMaterial):
11012 def __init__( self, materialData = None ):
11013 Material.__init__( self, materialData )
11014 self.id = "reviewing"
11016 def setId( self, newId ):
11017 return
11019 def getContribution(self):
11020 if isinstance(self.getOwner(), Review):
11021 return self.getOwner().getContribution()
11022 return Material.getContribution(self)
11024 class Paper(BuiltinMaterial):
11026 def __init__( self, materialData = None ):
11027 Material.__init__( self, materialData )
11028 self.id = "paper"
11030 def setId( self, newId ):
11031 return
11035 class Slides(BuiltinMaterial):
11037 def __init__( self, materialData = None ):
11038 Material.__init__( self, materialData )
11039 self.id = "slides"
11041 def setId( self, newId ):
11042 return
11046 class Video(BuiltinMaterial):
11048 def __init__( self, materialData = None ):
11049 Material.__init__( self, materialData )
11050 self.id = "video"
11052 def setId( self, newId ):
11053 return
11055 class Poster(BuiltinMaterial):
11057 def __init__( self, materialData = None ):
11058 Material.__init__( self, materialData )
11059 self.id = "poster"
11061 def setId( self, newId ):
11062 return
11064 class Minutes(BuiltinMaterial):
11066 def __init__( self, materialData = None ):
11067 Material.__init__( self, materialData )
11068 self.id = "minutes"
11069 self.title = "Minutes"
11070 self.file = None
11072 def clone ( self, owner):
11073 mat = Minutes()
11074 mat.setTitle(self.getTitle())
11075 mat.setDescription(self.getDescription())
11076 mat.notifyModification()
11078 mat.setId(self.getId())
11079 mat.setOwner(owner)
11080 mat.setType(self.getType())
11082 mat.setProtection(self.getAccessController()._getAccessProtection())
11083 mat.setAccessKey(self.getAccessKey())
11084 lrep = self._getRepository()
11085 flist = lrep.getFiles()
11086 rlist = self.getResourceList()
11087 for r in rlist :
11088 if r.getId()=="minutes":
11089 mat.setText(self.getText())
11090 elif isinstance(r,Link):
11091 newlink = Link()
11092 newlink.setOwner(mat)
11093 newlink.setName(r.getName())
11094 newlink.setDescription(r.getDescription())
11095 newlink.setURL(r.getURL())
11096 mat.addResource(newlink)
11097 elif isinstance(r,LocalFile):
11098 newfile = LocalFile()
11099 newfile.setOwner(mat)
11100 newfile.setName(r.getName())
11101 newfile.setDescription(r.getDescription())
11102 newfile.setFilePath(r.getFilePath())
11103 newfile.setFileName(r.getFileName())
11104 mat.addResource(newfile)
11105 else :
11106 raise Exception( _("Unexpected object type in Resource List : ")+str(type(r)))
11108 mat.setMainResource(self.getMainResource())
11110 return mat
11112 def setId( self, newId ):
11113 return
11115 def setTitle( self, newTitle ):
11116 self.title = newTitle.strip()
11117 self.notifyModification()
11119 def _setFile( self, forcedFileId = None ):
11120 #XXX: unsafe; it must be changed by mkstemp when migrating to python 2.3
11121 tmpFileName = tempfile.mktemp()
11122 fh = open(tmpFileName, "w")
11123 fh.write(" ")
11124 fh.close()
11125 self.file = LocalFile()
11126 self.file.setId("minutes")
11127 self.file.setName("minutes")
11128 self.file.setFilePath(tmpFileName)
11129 self.file.setFileName("minutes.txt")
11130 self.file.setOwner(self)
11131 self.file.archive(self._getRepository(), forcedFileId = forcedFileId)
11133 def setText( self, text, forcedFileId = None ):
11134 if self.file:
11135 self.file.delete()
11136 self._setFile(forcedFileId = forcedFileId)
11137 self.file.replaceContent( text )
11138 self.getOwner().notifyModification()
11140 def getText( self ):
11141 if not self.file:
11142 return ""
11143 return self.file.readBin()
11145 def getResourceList(self, sort=True):
11146 res = Material.getResourceList(self, sort=sort)
11147 if self.file:
11148 res.insert(0, self.file)
11149 return res
11151 def getResourceById( self, id ):
11152 if id.strip() == "minutes":
11153 return self.file
11154 return Material.getResourceById( self, id )
11156 def removeResource(self, res):
11157 Material.removeResource(self, res)
11158 if self.file is not None and res.getId().strip() == "minutes":
11159 self.file = None
11160 res.delete()
11161 self.notifyModification()
11163 def recoverResource(self, recRes):
11164 if recRes.getId() == "minutes":
11165 recRes.setOwner(self)
11166 self.file = recRes
11167 recRes.recover()
11168 self.notifyModification()
11169 else:
11170 Material.recoverResource(self, recRes)
11173 class Resource(CommonObjectBase):
11174 """This is the base class for representing individual resources which can
11175 be included in material containers for lately being attached to
11176 conference objects (i.e. conferences, sessions or contributions). This
11177 class provides basic data and operations to handle this resources.
11178 Resources can be of serveral types (files, links, ...) which means
11179 different specialisations of this class.
11180 Attributes:
11181 id -- (string) Allows to assign the resource a unique identifier. It
11182 is normally used to uniquely identify the resource among other
11183 resources included in a certain material.
11184 name -- (string) Short description about the purpose or the contents
11185 of the resource.
11186 description - (string) detailed and varied information about the
11187 resource.
11188 __owner - (Material) reference to the material object in which the
11189 current resource is included.
11192 fossilizes(IResourceMinimalFossil, IResourceFossil)
11194 def __init__( self, resData = None ):
11195 self.id = "not assigned"
11196 self.name = ""
11197 self.description = ""
11198 self._owner = None
11199 self.__ac = AccessController(self)
11200 self.pdfConversionRequestDate = None
11202 def __cmp__(self, other):
11203 if type(self) is not type(other):
11204 # This is actually dangerous and the ZODB manual says not to do this
11205 # because it relies on memory order. However, this branch should never
11206 # be taken anyway since we do not store different types in the same set
11207 # or use them as keys.
11208 return cmp(hash(self), hash(other))
11209 if self.getConference() == other.getConference():
11210 return cmp(self.getId(), other.getId())
11211 return cmp(self.getConference(), other.getConference())
11213 def clone( self, conf, protection=True ):
11214 res = self.__class__()
11215 res.setName(self.getName())
11216 res.setDescription(self.getDescription())
11217 res.setOwner(conf)
11218 res.notifyModification()
11219 res.setId(self.getId())
11221 if protection:
11222 res.setProtection(self.getAccessController()._getAccessProtection())
11223 #res.__ac = self.getAccessController()
11225 return res
11227 def notifyModification( self ):
11228 parent = self.getOwner()
11229 if parent:
11230 parent.setModificationDate()
11231 self._p_changed = 1
11233 def getLocator( self ):
11234 if self._owner == None:
11235 return Locator()
11236 lconf = self._owner.getLocator()
11237 lconf["resId"] = self.getId()
11238 return lconf
11240 def setId( self, newId ):
11241 self.id = newId.strip()
11243 def getId( self ):
11244 return self.id
11246 def getUniqueId( self ):
11247 """returns (string) the unique identifier of the item
11248 used mainly in the web session access key table
11249 for resources, it is the same as the father material since
11250 only the material can be protected with an access key"""
11251 return self.getOwner().getUniqueId()
11253 def setOwner(self, newOwner):
11254 self._owner = newOwner
11256 def getOwner( self ):
11257 return self._owner
11259 def getCategory( self ):
11260 #raise "%s:%s:%s"%(self.getOwner(), Material, isinstance(self.getOwner, Material))
11262 if isinstance(self.getOwner(), Category):
11263 return self.getOwner()
11264 if isinstance(self.getOwner(), Material):
11265 return self.getOwner().getCategory()
11266 return None
11268 def getConference( self ):
11269 # this check owes itself to the fact that some
11270 # protection checking functions call getConference()
11271 # directly on resources, without caring whether they
11272 # are owned by Conferences or Categories
11273 if self._owner is None or isinstance(self._owner, Category):
11274 return None
11275 else:
11276 return self._owner.getConference()
11278 def getSession( self ):
11279 return self._owner.getSession()
11281 def getContribution( self ):
11282 return self._owner.getContribution()
11284 def getSubContribution( self ):
11285 return self._owner.getSubContribution()
11287 @Updates (['MaKaC.conference.Link',
11288 'MaKaC.conference.LocalFile'], 'name')
11289 def setName( self, newName ):
11290 self.name = newName.strip()
11291 self.notifyModification()
11293 def getName( self ):
11294 return self.name
11296 @Updates (['MaKaC.conference.Link',
11297 'MaKaC.conference.LocalFile'], 'description')
11298 def setDescription( self, newDesc ):
11299 self.description = newDesc.strip()
11300 self.notifyModification()
11302 def getDescription( self ):
11303 return self.description
11305 def archive( self, repository = None, forcedFileId = None ):
11306 """performs necessary operations to ensure the archiving of the
11307 resource. By default is doing nothing as the persistence of the
11308 system already ensures the archiving of the basic resource data"""
11309 return
11311 def delete(self):
11312 if self._owner is not None:
11313 self.notify_protection_to_owner(delete=True)
11314 self._owner.removeResource(self)
11315 self.__ac.unlinkAvatars('access')
11316 self._owner = None
11317 TrashCanManager().add(self)
11319 def recover(self):
11320 TrashCanManager().remove(self)
11322 def isProtected(self):
11323 # tells if a resource is protected or not
11324 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
11326 def getAccessProtectionLevel( self ):
11327 return self.__ac.getAccessProtectionLevel()
11329 def isItselfProtected( self ):
11330 return self.__ac.isItselfProtected()
11332 def hasProtectedOwner( self ):
11333 if self.getOwner() != None:
11334 return self.getOwner().isProtected()
11335 return False
11337 def notify_protection_to_owner(self, delete=False):
11338 # Resources can be attached to other objects (e.g. Registrant),
11339 # but we wish to trigger the notification only when attached to materials (except paper reviewing)
11340 if isinstance(self.getOwner(), Material) and not isinstance(self.getOwner(), Reviewing):
11341 self.getOwner().updateNonInheritingChildren(self, delete)
11343 @Updates (['MaKaC.conference.Link',
11344 'MaKaC.conference.LocalFile'],'protection', lambda(x): int(x))
11346 def setProtection( self, private ):
11347 self.__ac.setProtection( private )
11348 self.notify_protection_to_owner()
11350 def grantAccess( self, prin ):
11351 self.__ac.grantAccess( prin )
11352 if isinstance(prin, AvatarUserWrapper):
11353 prin.linkTo(self, "access")
11355 def revokeAccess( self, prin ):
11356 self.__ac.revokeAccess( prin )
11357 if isinstance(prin, AvatarUserWrapper):
11358 prin.unlinkTo(self, "access")
11360 def canView( self, aw ):
11361 """tells whether the specified user has access to the current object
11362 or any of its sub-objects
11364 return self.canAccess( aw )
11366 def isAllowedToAccess( self, user ):
11367 return self.__ac.canUserAccess( user ) or self.canUserModify( user ) or (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user ))
11369 def canAccess( self, aw ):
11370 # Allow harvesters (Invenio, offline cache) to access
11371 # protected pages
11372 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
11373 return True
11374 #####################################################
11376 # Managers have always access
11377 if self.canModify(aw):
11378 return True
11380 if not self.canIPAccess(request.remote_addr) and not self.canUserModify(aw.getUser()) and \
11381 not self.isAllowedToAccess(aw.getUser()):
11382 return False
11383 if not self.isProtected():
11384 return True
11385 flag = self.isAllowedToAccess( aw.getUser() )
11386 return flag or self.canKeyAccess(aw) or self.getOwner().canKeyAccess(aw) or \
11387 (self.getConference() != None and self.getConference().canKeyAccess(aw) and self.getAccessKey() == "") or \
11388 (self.getConference() != None and self.getConference().canKeyAccess(aw) and self.getAccessKey() == self.getConference().getAccessKey())
11390 def grantModification( self, prin ):
11391 self.__ac.grantModification( prin )
11393 def revokeModification( self, prin ):
11394 self.__ac.revokeModification( prin )
11396 def canModify(self, aw_or_user):
11397 if hasattr(aw_or_user, 'getUser'):
11398 aw_or_user = aw_or_user.getUser()
11399 return self.canUserModify(aw_or_user) or (self.getConference() and self.getConference().canKeyModify())
11401 def canUserModify( self, user ):
11402 """Tells whether a user is allowed to modify the current contribution:
11403 only if the user is granted to modify the contribution or the user
11404 can modify any of its upper objects (i.e. conference or session).
11406 return self.getOwner().canUserModify( user )
11408 def getModifKey( self ):
11409 return self.getConference().getModifKey()
11411 def getManagerList( self ):
11412 return self.__ac.getModifierList()
11414 def getAllowedToAccessList( self ):
11415 return self.__ac.getAccessList()
11417 def getURL( self ):
11418 return ""
11420 def requireDomain( self, dom ):
11421 self.__ac.requireDomain( dom )
11423 def freeDomain( self, dom ):
11424 self.__ac.freeDomain( dom )
11426 def getDomainList( self ):
11427 return self.__ac.getRequiredDomainList()
11429 def getAccessController(self):
11430 return self.__ac
11432 def getAccessKey(self):
11433 if self.getOwner() is not None:
11434 return self.getOwner().getAccessKey()
11435 return ""
11437 def canKeyAccess(self, aw):
11438 accessKey = self.getAccessKey()
11439 key = session.get('accessKeys', {}).get(self.getUniqueId())
11440 if not key:
11441 return False
11442 elif accessKey and key == accessKey:
11443 return True
11444 elif not accessKey and key == self.getConference().getAccessKey():
11445 return True
11446 return False
11448 def getReviewingState(self):
11449 """ Returns the reviewing state of a resource, which is the reviewing state of the material to which it belongs.
11450 The state is represented by an integer:
11451 0 : there's no reviewing state because the resource doesn't belong to a material,
11452 the material does not belong to a contribution, or the conference does not have reviewing.
11453 1 : the material is not subject to reviewing, because this kind of material is not reviewable in the conference
11454 2 : the material is subject to reviewing, but has not been submitted yet by the author
11455 3 : the material is subject to reviewing, has been submitted by the author, but has not been judged yet
11456 4 : the material is subject to reviewing, has been submitted by the author, and has been judged as Accepted
11457 5 : the material is subject to reviewing, has been submitted by the author, and has been judged as Rejected
11459 if isinstance(self.getOwner(), Material):
11460 return self.getOwner().getReviewingState()
11461 else: #ressource does not belong to a material
11462 return 0
11464 def setPDFConversionRequestDate( self, newPdfConversionRequestDate ):
11465 self.pdfConversionRequestDate = newPdfConversionRequestDate
11467 def getPDFConversionStatus(self):
11469 if not hasattr(self, "pdfConversionRequestDate"):
11470 self.pdfConversionRequestDate = None
11472 if self.pdfConversionRequestDate is not None and self.pdfConversionRequestDate + timedelta(seconds=50) > nowutc() :
11473 return 'converting'
11474 return None
11476 class Link(Resource):
11477 """Specialises Resource class in order to represent web links. Objects of
11478 this class will contain necessary information to include in a conference
11479 object links to internet resources through a URL.
11480 Params:
11481 url -- (string) Contains the URL to the internet target resource.
11484 fossilizes(ILinkMinimalFossil, ILinkFossil)
11486 def __init__( self, resData = None ):
11487 Resource.__init__( self, resData )
11488 self.url = ""
11490 def clone( self, conf ):
11491 link = Resource.clone(self, conf)
11492 link.setURL(self.getURL())
11493 return link
11495 @Updates ('MaKaC.conference.Link', 'url')
11496 def setURL( self, newURL ):
11497 self.url = newURL.strip()
11498 self.notifyModification()
11500 def getURL( self ):
11501 return self.url
11503 def getLocator(self):
11504 locator = Resource.getLocator(self)
11505 locator['fileExt'] = 'link'
11506 return locator
11508 class LocalFile(Resource):
11509 """Specialises Resource class in order to represent files which can be
11510 stored in the system. The user can choose to use the system as an
11511 archive of electronic files so he may want to attach a file which is
11512 in his computer to a conference so it remains there and must be kept
11513 in the system. This object contains the file basic metadata and provides
11514 the necessary operations to ensure the corresponding file is archived
11515 (it uses one of the file repositories of the system to do so) and keeps
11516 the reference for being able to access it afterwards.
11517 Params:
11518 fileName -- (string) Name of the file. Normally the original name of
11519 the user submitted file is kept.
11520 filePath -- (string) If it is set, it contains a local path to the
11521 file submitted by the user and uploaded in the system. This
11522 attribute is only temporary used so it keeps a pointer to a
11523 temporary uploaded file.
11524 __repository -- (FileRep) Once a file is archived, it is kept in a
11525 FileRepository for long term. This attribute contains a pointer
11526 to the file repository where the file is kept.
11527 __archivedId -- (string) It contains a unique identifier for the file
11528 inside the repository where it is archived.
11531 fossilizes(ILocalFileMinimalFossil, ILocalFileFossil, ILocalFileExtendedFossil, ILocalFileAbstractMaterialFossil)
11533 def __init__( self, resData = None ):
11534 Resource.__init__( self, resData )
11535 self.fileName= ""
11536 self.fileType = ""
11537 self.filePath = ""
11538 self.__repository = None
11539 self.__archivedId = ""
11541 def clone( self, conf, protection=True ):
11542 localfile = Resource.clone(self, conf, protection)
11543 localfile.setFilePath(self.getFilePath())
11544 localfile.setFileName(self.getFileName())
11545 return localfile
11547 def getLocator(self):
11548 locator = Resource.getLocator(self)
11549 if self.fileName == 'minutes.txt' and isinstance(self._owner, Minutes):
11550 # Hack to get a html extension when viewing minutes
11551 locator['fileExt'] = 'html'
11552 else:
11553 try:
11554 locator['fileExt'] = (self.fileType.lower() or
11555 os.path.splitext(self.fileName)[1].lower().lstrip('.') or None)
11556 except Exception:
11557 locator['fileExt'] = 'bin' # no extension => use a dummy
11558 return locator
11560 def setFileName( self, newFileName, checkArchive=True ):
11561 """While the file is not archived sets the file name of the current
11562 object to the one specified (if a full path is specified the
11563 base name is extracted) replacing on it blanks by underscores.
11565 if checkArchive and self.isArchived():
11566 raise MaKaCError( _("The file name of an archived file cannot be changed"), _("File Archiving"))
11567 #Using os.path.basename is not enough as it only extract filenames
11568 # correclty depending on the server platform. So we need to convert
11569 # to the server platform and apply the basename extraction. As I
11570 # couldn't find a python function for this this is done manually
11571 # although it can contain errors
11572 #On windows basename function seems to work properly with unix file
11573 # paths
11574 if newFileName.count("/"):
11575 #unix filepath
11576 newFileName = newFileName.split("/")[-1]
11577 else:
11578 #windows file path: there "/" is not allowed on windows paths
11579 newFileName = newFileName.split("\\")[-1]
11580 self.fileName = newFileName.strip().replace(" ", "_")
11582 def getFileName( self ):
11583 return self.fileName
11585 def getFileType( self ):
11586 fileExtension = os.path.splitext( self.getFileName() )[1]
11587 if fileExtension != "":
11588 fileExtension = fileExtension[1:]
11589 cfg = Config.getInstance()
11590 if cfg.getFileType( fileExtension ) != "":
11591 return cfg.getFileType( fileExtension )
11592 else:
11593 return fileExtension
11595 def setFilePath( self, filePath ):
11596 if self.isArchived():
11597 raise MaKaCError( _("The path of an archived file cannot be changed"), _("File Archiving"))
11598 if not os.access( filePath.strip(), os.F_OK ):
11599 raise Exception( _("File does not exist : %s")%filePath.strip())
11600 self.filePath = filePath.strip()
11602 def getCreationDate( self):
11603 return self.__repository.getCreationDate(self.__archivedId)
11605 def getFilePath( self ):
11606 if not self.isArchived():
11607 return self.filePath
11608 return self.__repository.getFilePath(self.__archivedId)
11610 def getSize( self ):
11611 if not self.isArchived():
11612 return int(os.stat(self.getFilePath())[stat.ST_SIZE])
11613 return self.__repository.getFileSize( self.__archivedId )
11615 def setArchivedId( self, rep, id ):
11616 self.__repository = rep
11617 self.__archivedId = id
11619 def getRepositoryId( self ):
11620 return self.__archivedId
11622 def setRepositoryId(self, id):
11623 self.__archivedId = id
11625 def isArchived( self ):
11626 return self.__repository != None and self.__archivedId != ""
11628 def readBin( self ):
11629 if not self.isArchived():
11630 raise MaKaCError( _("File not available until it has been archived") , _("File Archiving"))
11631 return self.__repository.readFile( self.__archivedId )
11633 def replaceContent( self, newContent ):
11634 if not self.isArchived():
11635 raise MaKaCError( _("File not available until it has been archived") , _("File Archiving"))
11636 self.__repository.replaceContent( self.__archivedId, newContent )
11638 def archive( self, repository=None, forcedFileId = None ):
11639 if self.isArchived():
11640 raise Exception( _("File is already archived"))
11641 if not repository:
11642 raise Exception( _("Destination repository not set"))
11643 if self.filePath == "":
11644 return _("Nothing to archive")
11645 repository.storeFile( self, forcedFileId = forcedFileId)
11646 self.filePath = ""
11647 self.notifyModification()
11649 def unArchive(self):
11650 # Not used.
11651 self.__repository = None
11652 self.__archivedId = ""
11654 def recover(self):
11655 if not self.isArchived():
11656 raise Exception( _("File is not archived, so it cannot be recovered."))
11657 if not self.__repository:
11658 raise Exception( _("Destination repository not set."))
11659 self.__repository.recoverFile(self)
11660 Resource.recover(self)
11661 self.notifyModification()
11663 def delete( self ):
11664 if not self.isArchived():
11665 os.remove( self.getFilePath() )
11666 try:
11667 self.__repository.retireFile( self )
11668 except AttributeError, e:
11669 pass
11670 Resource.delete( self )
11672 def getRepository(self):
11673 return self.__repository
11675 def __str__( self ):
11676 return self.getFileName()
11679 class TCIndex( Persistent ):
11680 """Index for conference track coordinators.
11682 This class allows to index conference track coordinators so the owner
11683 can answer optimally to the query if a user is coordinating
11684 any conference track.
11685 It is implemented by simply using a BTree where the Avatar id is used
11686 as key (because it is unique and non variable) and a list of
11687 coordinated tracks is kept as keys. It is the responsability of the
11688 index owner (conference) to keep it up-to-date i.e. notify track
11689 coordinator additions and removals.
11692 def __init__( self ):
11693 self._idx = OOBTree()
11696 def getTracks( self, av ):
11697 """Gives a list with the tracks a user is coordinating.
11699 if av == None:
11700 return []
11701 return self._idx.get( av.getId(), [] )
11703 def indexCoordinator( self, av, track ):
11704 """Registers in the index a coordinator of a track.
11706 if av == None or track == None:
11707 return
11708 if not self._idx.has_key( av.getId() ):
11709 l = []
11710 else:
11711 l = self._idx[av.getId()]
11712 if track not in l:
11713 l.append(track)
11714 # necessary, otherwise ZODB won't know it needs to update the BTree
11715 self._idx[av.getId()] = l
11716 self.notifyModification()
11718 def unindexCoordinator( self, av, track ):
11719 if av == None or track == None:
11720 return
11721 l = self._idx.get( av.getId(), [] )
11722 if track in l:
11723 l.remove( track )
11724 self._idx[av.getId()] = l
11725 self.notifyModification()
11727 def notifyModification(self):
11728 self._p_changed = 1
11731 class Track(CoreObject):
11733 def __init__( self ):
11734 self.conference = None
11735 self.id = "not assigned"
11736 self.title = ""
11737 self.description = ""
11738 self.subTracks = {}
11739 self.__SubTrackGenerator = Counter()
11740 self._abstracts = OOBTree()
11741 self._coordinators = []
11742 self._contributions = OOBTree()
11743 self._code=""
11745 def __cmp__(self, other):
11746 if type(self) is not type(other):
11747 # This is actually dangerous and the ZODB manual says not to do this
11748 # because it relies on memory order. However, this branch should never
11749 # be taken anyway since we do not store different types in the same set
11750 # or use them as keys.
11751 return cmp(hash(self), hash(other))
11752 if self.getConference() == other.getConference():
11753 return cmp(self.getId(), other.getId())
11754 return cmp(self.getConference(), other.getConference())
11756 def clone(self, conference):
11757 tr = Track()
11758 tr.setConference(conference)
11759 tr.setTitle(self.getTitle())
11760 tr.setCode(self.getCode())
11761 tr.setDescription(self.getDescription())
11763 for co in self.getCoordinatorList() :
11764 tr.addCoordinator(co)
11766 for subtr in self.getSubTrackList() :
11767 tr.addSubTrack(subtr.clone())
11769 return tr
11772 def delete( self ):
11773 """Deletes a track from the system. All the associated abstracts will
11774 also be notified so the track is no longer associated to them.
11776 #XXX: Should we allow to delete a track when there are some abstracts
11777 # or contributions submitted for it?!?!?!?!
11779 # we must notify each abstract in the track about the deletion of the
11780 # track
11781 while len(self._abstracts)>0:
11782 k = self._abstracts.keys()[0]
11783 abstract = self._abstracts[k]
11784 del self._abstracts[k]
11785 abstract.removeTrack( self )
11787 # we must notify each contribution in the track about the deletion of the
11788 # track
11789 while len(self._contributions)>0:
11790 k = self._contributions.keys()[0]
11791 contrib = self._contributions[k]
11792 del self._contributions[k]
11793 contrib.removeTrack( self )
11795 # we must delete and unindex all the possible track coordinators
11796 while len(self._coordinators)>0:
11797 self.removeCoordinator(self._coordinators[0])
11799 # we must notify the conference about the track deletion
11800 if self.conference:
11801 conf = self.conference
11802 self.conference = None
11803 conf.removeTrack( self )
11805 TrashCanManager().add(self)
11807 def recover(self):
11808 TrashCanManager().remove(self)
11810 def canModify(self, aw_or_user):
11811 return self.conference.canModify(aw_or_user)
11813 def canUserModify( self, av ):
11814 return self.conference.canUserModify( av )
11816 def canView( self, aw ):
11817 return self.conference.canView( aw )
11819 def notifyModification( self ):
11820 parent = self.getConference()
11821 if parent:
11822 parent.setModificationDate()
11823 self._p_changed = 1
11825 def getLocator( self ):
11826 """Gives back a globaly unique identification encapsulated in a Locator
11827 object for the track instance
11829 if self.conference == None:
11830 return Locator()
11831 lconf = self.conference.getLocator()
11832 lconf["trackId"] = self.getId()
11833 return lconf
11835 def setConference(self, conference):
11836 self.conference = conference
11838 def getConference( self ):
11839 return self.conference
11841 def getOwner( self ):
11842 return self.getConference()
11844 def setId( self, newId ):
11845 self.id = str(newId)
11847 def getId( self ):
11848 return self.id
11850 def setTitle( self, newTitle ):
11851 self.title = newTitle
11852 self.notifyModification()
11854 def getTitle( self ):
11855 return self.title
11857 def setDescription(self, newDescription ):
11858 self.description = newDescription
11859 self.notifyModification()
11861 def getDescription(self):
11862 return self.description
11864 def getCode(self):
11865 try:
11866 if self._code:
11867 pass
11868 except AttributeError:
11869 self._code=self.id
11870 return self._code
11872 def setCode(self,newCode):
11873 self._code=str(newCode).strip()
11875 def __generateNewSubTrackId( self ):
11876 return str(self.__SubTrackGenerator.newCount())
11878 def addSubTrack( self, newSubTrack ):
11879 """Registers the contribution passed as parameter within the session
11880 assigning it a unique id.
11882 if newSubTrack in self.subTracks.values():
11883 return
11884 subTrackId = newSubTrack.getId()
11885 if subTrackId == "not assigned":
11886 subTrackId = self.__generateNewSubTrackId()
11887 self.subTracks[subTrackId] = newSubTrack
11888 newSubTrack.setTrack( self )
11889 newSubTrack.setId( subTrackId )
11890 self.notifyModification()
11892 def removeSubTrack( self, subTrack ):
11893 """Removes the indicated contribution from the session
11895 if subTrack in self.subTracks.values():
11896 del self.subTracks[ subTrack.getId() ]
11897 subTrack.setTrack( None )
11898 subTrack.delete()
11899 self.notifyModification()
11901 def recoverSubTrack(self, subTrack):
11902 self.addSubTrack(subTrack)
11903 subTrack.recover()
11905 def newSubTrack( self ):
11906 st = SubTrack()
11907 self.addSubTrack( st )
11908 return st
11910 def getSubTrackById( self, id ):
11911 if self.subTracks.has_key( id ):
11912 return self.subTracks[ id ]
11913 return None
11915 def getSubTrackList( self ):
11916 return self.subTracks.values()
11918 def getAbstractList( self ):
11921 try:
11922 if self._abstracts:
11923 pass
11924 except AttributeError:
11925 self._abstracts = OOBTree()
11926 return self._abstracts.values()
11928 def getAbstractById( self, id ):
11929 try:
11930 if self._abstracts:
11931 pass
11932 except AttributeError:
11933 self._abstracts = OOBTree()
11934 return self._abstracts.get(str(id).strip())
11936 def hasAbstract( self, abstract ):
11939 try:
11940 if self._abstracts:
11941 pass
11942 except AttributeError:
11943 self._abstracts = OOBTree()
11944 return self._abstracts.has_key( abstract.getId() )
11946 def addAbstract( self, abstract ):
11947 """Adds an abstract to the track abstract list.
11949 Notice that this method doesn't notify the abstract about the track
11950 addition.
11952 if not self.hasAbstract( abstract ):
11953 self._abstracts[ abstract.getId() ] = abstract
11954 #abstract.addTrack( self )
11956 def removeAbstract( self, abstract ):
11957 """Removes an abstract from the track abstract list.
11959 Notice that this method doesn't notify the abstract about the track
11960 removal.
11962 if self.hasAbstract( abstract ):
11963 del self._abstracts[ abstract.getId() ]
11964 #abstract.removeTrack( self )
11966 def addCoordinator( self, av ):
11967 """Grants coordination privileges to user.
11969 Arguments:
11970 av -- (AvatarUserWrapper) the user to which
11971 coordination privileges must be granted.
11974 try:
11975 if self._coordinators:
11976 pass
11977 except AttributeError, e:
11978 self._coordinators = []
11979 self.notifyModification()
11981 if not (av in self._coordinators):
11982 self._coordinators.append( av )
11983 self.getConference().addTrackCoordinator( self, av )
11984 av.linkTo(self, "coordinator")
11985 self.notifyModification()
11987 def removeCoordinator( self, av ):
11988 """Revokes coordination privileges to user.
11990 Arguments:
11991 av -- (AvatarUserWrapper) user for which coordination privileges
11992 must be revoked
11994 try:
11995 if self._coordinators:
11996 pass
11997 except AttributeError, e:
11998 self._coordinators = []
11999 self.notifyModification()
12001 if av in self._coordinators:
12002 self._coordinators.remove( av )
12003 self.getConference().removeTrackCoordinator( self, av )
12004 av.unlinkTo(self, "coordinator")
12005 self.notifyModification()
12007 def isCoordinator( self, av ):
12008 """Tells whether the specified user is a coordinator of the track.
12010 Arguments:
12011 av -- (AvatarUserWrapper) user to be checke
12013 Return value: (boolean)
12015 try:
12016 if self._coordinators:
12017 pass
12018 except AttributeError, e:
12019 self._coordinators = []
12021 return av in self._coordinators
12023 def getCoordinatorList( self ):
12024 """Return all users which have privileges to coordinate the track.
12026 Return value: (list)
12028 try:
12029 if self._coordinators:
12030 pass
12031 except AttributeError, e:
12032 self._coordinators = []
12034 return self._coordinators
12036 def canCoordinate( self, aw ):
12037 """Tells if a user has coordination privileges.
12039 Only track coordinators have coordination privileges over a track.
12041 Params:
12042 aw -- (MaKaC.accessControl.AccessWrapper) User access
12043 information for which the coordination privileges must be
12044 checked.
12046 Return value: (boolean)
12048 return self.isCoordinator( aw.getUser() ) or self.canModify( aw )
12050 def addContribution( self, newContrib ):
12053 try:
12054 if self._contributions:
12055 pass
12056 except AttributeError:
12057 self._contributions = OOBTree()
12058 if self._contributions.has_key( newContrib.getId() ):
12059 return
12060 self._contributions[ newContrib.getId() ] = newContrib
12061 newContrib.setTrack( self )
12063 def getModifKey( self ):
12064 return self.getConference().getModifKey()
12066 def removeContribution( self, contrib ):
12069 try:
12070 if self._contributions:
12071 pass
12072 except AttributeError:
12073 self._contributions = OOBTree()
12074 if not self._contributions.has_key( contrib.getId() ):
12075 return
12076 del self._contributions[ contrib.getId() ]
12077 contrib.setTrack( None )
12079 def hasContribution( self, contrib ):
12080 try:
12081 if self._contributions:
12082 pass
12083 except AttributeError:
12084 self._contributions = OOBTree()
12085 return self._contributions.has_key( contrib.getId() )
12087 def getContributionList(self):
12088 try:
12089 if self._contributions:
12090 pass
12091 except AttributeError:
12092 self._contributions = OOBTree()
12093 return self._contributions.values()
12095 def canUserCoordinate( self, av ):
12096 return self.isCoordinator( av ) or self.canUserModify( av )
12099 class SubTrack(CoreObject):
12101 def __init__( self ):
12102 self.track = None
12103 self.id = "not assigned"
12104 self.title = ""
12105 self.description = ""
12107 def clone(self):
12108 sub = SubTrack()
12109 sub.setDescription(self.getDescription())
12110 sub.setTitle(self.getTitle())
12112 return sub
12115 def delete(self):
12116 TrashCanManager().add(self)
12118 def recover(self):
12119 TrashCanManager().remove(self)
12121 def canModify(self, aw_or_user):
12122 return self.track.canModify(aw_or_user)
12124 def canView( self, aw ):
12125 return self.track.canView( aw )
12127 def notifyModification( self ):
12128 parent = self.getTrack()
12129 if parent:
12130 parent.setModificationDate()
12131 self._p_changed = 1
12133 def getLocator( self ):
12134 """Gives back a globaly unique identification encapsulated in a Locator
12135 object for the session instance
12137 if self.track == None:
12138 return Locator()
12139 lconf = self.track.getLocator()
12140 lconf["subTrackId"] = self.getId()
12141 return lconf
12143 def setTrack(self, track):
12144 self.track = track
12145 if track == None:
12146 return
12148 def getTrack( self ):
12149 return self.track
12151 def getOwner( self ):
12152 return self.getTrack()
12154 def setId( self, newId ):
12155 self.id = str(newId)
12157 def getId( self ):
12158 return self.id
12160 def setTitle( self, newTitle ):
12161 self.title = newTitle
12162 self.notifyModification()
12164 def getTitle( self ):
12165 return self.title
12167 def setDescription(self, newDescription ):
12168 self.description = newDescription
12169 self.notifyModification()
12171 def getDescription(self):
12172 return self.description
12175 class ContributionType(Persistent):
12177 def __init__(self, name, description, conference):
12178 self._id = ""
12179 self._name = name
12180 self._description = description
12181 self._conference = conference
12183 def getId(self):
12184 return self._id
12186 def setId(self, id):
12187 self._id = id
12189 def getName(self):
12190 return self._name
12192 def setName(self, name):
12193 self._name = name
12195 def getDescription(self):
12196 return self._description
12198 def setDescription(self, desc):
12199 self._description = desc
12201 def getConference(self):
12202 return self._conference
12204 def setConference(self, conf):
12205 self._conference = conf
12207 def getLocator( self ):
12208 if self._conference == None:
12209 return Locator()
12210 lconf = self._conference.getLocator()
12211 lconf["contribTypeId"] = self.getId()
12212 return lconf
12214 def canModify(self, aw_or_user):
12215 return self._conference.canModify(aw_or_user)
12217 def delete(self):
12218 self.setConference(None)
12219 TrashCanManager().add(self)
12221 def recover(self):
12222 TrashCanManager().remove(self)
12224 def clone(self, conference ):
12225 type = ContributionType(self.getName(), self.getDescription(),conference)
12226 return type
12229 class BOAConfig(Persistent):
12230 """Contains the configuration of the Book of Abstracts of a conference
12232 sortByTypes = {"number": L_("ID"),
12233 "name": L_("Title"),
12234 "sessionTitle": L_("Session title"),
12235 "speaker": L_("Presenter"),
12236 "schedule": L_("Schedule")}
12238 correspondingAuthorTypes = {"none": L_("Nobody"),
12239 "submitter": L_("Submitter"),
12240 "speakers": L_("Speakers")}
12242 def __init__(self,conf):
12243 self._conf=conf
12244 self._text=""
12245 self._showIds= False
12246 self._sortBy = "number"
12247 self._correspondingAuthor = "submitter"
12248 self._modificationDS = nowutc()
12249 self._cache = False
12251 def getText(self):
12252 return self._text
12254 def setText(self,newText):
12255 self._text=newText.strip()
12256 self._notifyModification()
12258 def getShowIds(self):
12259 if not hasattr(self, "_showIds"):
12260 self._showIds=False
12261 return self._showIds
12263 def setShowIds(self,showIds):
12264 self._showIds=showIds
12265 self._notifyModification()
12267 def getSortBy(self):
12268 if not hasattr(self, "_sortBy"):
12269 self._sortBy="number"
12270 return self._sortBy
12272 def setSortBy(self,sortBy):
12273 self._sortBy=sortBy
12274 self._notifyModification()
12276 @staticmethod
12277 def getSortByTypes():
12278 return BOAConfig.sortByTypes
12280 def getCorrespondingAuthor(self):
12281 if not hasattr(self, "_correspondingAuthor"):
12282 self._correspondingAuthor = "submitter"
12283 return self._correspondingAuthor
12285 def setCorrespondingAuthor(self, correspondingAuthor):
12286 self._correspondingAuthor = correspondingAuthor
12287 self._notifyModification()
12289 @staticmethod
12290 def getCorrespondingAuthorTypes():
12291 return BOAConfig.correspondingAuthorTypes
12293 def isCacheEnabled(self):
12294 if not hasattr(self, '_cache'):
12295 self._cache = False
12296 return self._cache
12298 def setCache(self, value):
12299 self._cache = value;
12301 def _notifyModification(self):
12302 self._modificationDS = nowutc()
12304 @property
12305 def lastChanged(self):
12306 if not hasattr(self, '_modificationDS'):
12307 self._modificationDS = nowutc()
12308 return self._modificationDS
12311 class EventCloner(object):
12312 """Base class to let plugins/modules plug into the event cloning mechanism"""
12314 @staticmethod
12315 def get_plugin_items(event):
12316 """Returns the items/checkboxes for the clone options provided by EventCloner"""
12317 plugin_options = []
12318 for plugin_cloner in values_from_signal(signals.event_management.clone.send(event), single_value=True):
12319 with plugin_context(plugin_cloner.plugin):
12320 for name, (title, enabled, checked) in plugin_cloner.get_options().iteritems():
12321 full_name = plugin_cloner.full_option_name(name)
12322 plugin_options.append((
12323 title,
12324 """<li><input type="checkbox" name="cloners" id="cloner-{0}" value="{0}" {2} {3}>{1}</li>"""
12325 .format(full_name, title,
12326 'disabled' if not enabled else '',
12327 'checked' if checked and enabled else '')
12329 return '\n'.join(x[1] for x in sorted(plugin_options))
12331 @staticmethod
12332 def clone_event(old_event, new_event):
12333 """Calls the various cloning methods"""
12334 selected = set(request.values.getlist('cloners'))
12335 for plugin_cloner in values_from_signal(signals.event_management.clone.send(old_event), single_value=True):
12336 with plugin_context(plugin_cloner.plugin):
12337 selected_options = {name for name, (_, enabled, _) in plugin_cloner.get_options().iteritems()
12338 if enabled and plugin_cloner.full_option_name(name) in selected}
12339 plugin_cloner.clone(new_event, selected_options)
12341 def __init__(self, event, plugin=None):
12342 self.event = event
12343 self.plugin = plugin
12345 def full_option_name(self, option):
12346 return '{}-{}'.format(self.__module__, option)
12348 def get_options(self):
12349 """Returns a dict containing the clone options.
12351 :return: dict mapping option names to ``title, enabled, checked`` tuples
12353 raise NotImplementedError
12355 def clone(self, new_event, options):
12356 """Performs the actual cloning.
12358 This method is always called, even if no options are selected!
12360 :param new_event: The new event created during the clone
12361 :param options: A set containing the options provided by
12362 this class which the user has selected
12364 raise NotImplementedError