Remove old materials from categories
[cds-indico.git] / indico / MaKaC / conference.py
blobd3b9c835f097864c8edd39680339945690f33b5d
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.events.models.legacy_mapping import LegacyEventMapping
42 from indico.modules.categories.models.legacy_mapping import LegacyCategoryMapping
43 from indico.modules.rb.models.rooms import Room
44 from indico.modules.rb.models.locations import Location
45 from indico.modules.users import User
46 from indico.modules.users.legacy import AvatarUserWrapper
47 from indico.modules.groups.legacy import GroupWrapper
48 from indico.util.caching import memoize_request
49 from indico.util.i18n import L_
50 from indico.util.string import safe_upper, safe_slice, fix_broken_string, return_ascii, is_legacy_id, to_unicode
51 from MaKaC.review import AbstractFieldContent
54 import re, os
55 import tempfile
56 import copy
57 import stat
58 from datetime import datetime, timedelta
59 from flask import session, request, has_request_context
61 from MaKaC.contributionReviewing import ReviewManager
62 from MaKaC.paperReviewing import ConferencePaperReview as ConferencePaperReview
63 from MaKaC.abstractReviewing import ConferenceAbstractReview as ConferenceAbstractReview
65 from pytz import timezone
66 from pytz import all_timezones
68 from persistent import Persistent
69 from BTrees.OOBTree import OOBTree, OOTreeSet
70 from BTrees.OIBTree import OIBTree,OISet,union
71 import MaKaC
72 from MaKaC.common import indexes
73 from MaKaC.common.timezoneUtils import nowutc, maxDatetime
74 import MaKaC.fileRepository as fileRepository
75 from MaKaC.schedule import (ConferenceSchedule, SessionSchedule, SlotSchTypeFactory, ContribSchEntry,
76 LinkedTimeSchEntry, BreakTimeSchEntry)
77 import MaKaC.review as review
78 from MaKaC.common import utils
79 from MaKaC.common.Counter import Counter
80 from MaKaC.common.ObjectHolders import ObjectHolder
81 from MaKaC.common.Locators import Locator
82 from MaKaC.accessControl import AccessController
83 from MaKaC.errors import MaKaCError, TimingError, ParentTimingError, EntryTimingError, NotFoundError, FormValuesError
84 from MaKaC import registration
85 from MaKaC.evaluation import Evaluation
86 from MaKaC.trashCan import TrashCanManager
87 from MaKaC.user import AvatarHolder
88 from MaKaC.common import pendingQueues
89 from MaKaC.common.info import HelperMaKaCInfo
90 from MaKaC.participant import Participation
91 from MaKaC.badge import BadgeTemplateManager
92 from MaKaC.poster import PosterTemplateManager
93 from MaKaC.common import mail
94 from MaKaC.i18n import _
95 from MaKaC.common.PickleJar import Updates
96 from MaKaC.schedule import ScheduleToJson
97 from MaKaC.webinterface import urlHandlers
99 from indico.core.logger import Logger
100 from MaKaC.common.contextManager import ContextManager
101 import zope.interface
103 from indico.core import signals
104 from indico.util.date_time import utc_timestamp, format_datetime, format_human_timedelta
105 from indico.core.index import IIndexableByStartDateTime, IUniqueIdProvider, Catalog
106 from indico.core.db import DBMgr, db
107 from indico.core.db.event import SupportInfo
108 from indico.core.config import Config
109 from indico.modules.events.logs import EventLogEntry, EventLogRealm, EventLogKind
110 from indico.modules.attachments.util import get_attached_items
111 from indico.util.date_time import utc_timestamp
112 from indico.util.signals import values_from_signal
113 from indico.util.redis import write_client as redis_write_client
114 from indico.util.user import unify_user_args
115 from indico.util.redis import avatar_links
116 from indico.web.flask.util import url_for
119 class CoreObject(Persistent):
121 CoreObjects are Persistent objects that are employed by Indico's core
124 zope.interface.implements(IUniqueIdProvider,
125 IIndexableByStartDateTime)
127 def setModificationDate(self, date=None):
129 Method called to notify the current object has been modified.
131 if not date:
132 date = nowutc()
133 self._modificationDS = date
135 def __conform__(self, proto):
137 if proto == IIndexableByStartDateTime:
138 return utc_timestamp(self.getStartDate())
139 else:
140 return None
143 class Locatable:
145 Inherited by objects that imply a physical location:
146 * Conferences
147 * Sessions
148 * SessionSlots
149 * Contributions
150 * SubContributions
153 @property
154 def rb_room(self):
155 if not self.getLocation() or not self.getRoom():
156 return None
158 location = self.getLocation().getName()
159 room = self.getRoom().getName()
161 if not location or not room:
162 return None
164 return (Room.query
165 .join(Room.location)
166 .options(lazyload(Room.owner))
167 .filter(Room.name == fix_broken_string(room, True),
168 Location.name == fix_broken_string(location, True))
169 .first())
171 def getLocationParent(self):
173 Returns the object the location info should be inherited from
174 (Overridden)
176 raise Exception("Unimplemented method")
178 def getLocation(self):
179 if self.getOwnLocation():
180 return self.getOwnLocation()
181 return self.getInheritedLocation()
183 def getOwnLocation(self):
184 if len(self.places) > 0:
185 return self.places[0]
186 return None
188 def getInheritedLocation(self):
189 return self.getLocationParent().getLocation()
191 def getOwnRoom(self):
192 if len(self.rooms) > 0:
193 return self.rooms[0]
194 return None
196 def getRoom(self):
197 if self.getOwnRoom():
198 return self.getOwnRoom()
199 return self.getInheritedRoom()
201 def getInheritedRoom(self):
202 return self.getLocationParent().getRoom()
204 def setLocation(self, newLocation):
205 oldLocation = self.getOwnLocation()
206 if newLocation is None:
207 if len(self.places) > 0:
208 del self.places[0]
209 elif len(self.places) > 0:
210 self.places[0] = newLocation
211 else:
212 self.places.append(newLocation)
213 self.notifyModification()
215 def setRoom(self, newRoom):
216 oldRoom = self.getOwnRoom()
217 if newRoom is None:
218 if len(self.rooms) > 0:
219 del self.rooms[0]
220 elif len(self.rooms) > 0:
221 self.rooms[0] = newRoom
222 else:
223 self.rooms.append(newRoom)
224 self.notifyModification()
227 class CommonObjectBase(CoreObject, Fossilizable):
229 This class is for holding commonly used methods that are used by several classes.
230 It is inherited by the following classes:
231 * Category
232 * Conference
233 * Session
234 * Contribution
235 * SubContribution
236 * Material
237 * Resource
240 def getRecursiveManagerList(self):
241 av_set = set()
243 # Get the AccessProtectionLevel for this
244 apl = self.getAccessProtectionLevel()
246 if apl == -1:
247 pass
248 elif apl == 1:
249 for av in self.getManagerList():
250 av_set.add(av)
251 for av in self.getOwner().getRecursiveManagerList():
252 av_set.add(av)
253 else:
254 for av in self.getManagerList():
255 av_set.add(av)
257 if self.getOwner():
258 for av in self.getOwner().getRecursiveManagerList():
259 av_set.add(av)
261 return list(av_set)
263 def getRecursiveAllowedToAccessList(self, onlyManagers=False):
264 """Returns a set of Avatar resp. Group objects for those people resp.
265 e-groups allowed to access this object as well as all parent objects.
268 # Initialize set of avatars/groups: this will hold those
269 # people/groups explicitly
270 # allowed to access this object
271 av_set = set()
273 # Get the AccessProtectionLevel for this
274 apl = self.getAccessProtectionLevel()
276 # If this object is "absolutely public", then return an empty set
277 if apl == -1:
278 pass
280 # If this object is protected "all by itself", then get the list of
281 # people/groups allowed to access it, plus managers of owner(s)
282 elif apl == 1:
283 al = self.getAllowedToAccessList() + self.getManagerList() + \
284 self.getOwner().getRecursiveManagerList()
285 if al is not None:
286 for av in al:
287 av_set.add(av)
289 # If access settings are inherited (and PRIVATE) from its owners, look at those.
290 elif apl == 0 and self.isProtected():
291 # If event is protected, then get list of people/groups allowed
292 # to access, and add that to the set of avatars.
293 al = self.getAllowedToAccessList() + self.getManagerList()
294 if al is not None:
295 for av in al:
296 av_set.add(av)
298 # Add list of avatars/groups allowed to access parents objects.
299 owner = self.getOwner()
300 if owner is not None:
301 owner_al = owner.getRecursiveAllowedToAccessList(onlyManagers=True)
302 if owner_al is not None:
303 for av in owner_al:
304 av_set.add(av)
306 # return set containing whatever avatars/groups we may have collected
307 return av_set
309 def canIPAccess(self, ip):
310 domains = self.getAccessController().getAnyDomainProtection()
311 if domains:
312 return any(domain.belongsTo(ip) for domain in domains)
313 else:
314 return True
316 @property
317 @memoize_request
318 def attached_items(self):
320 CAUTION: this won't return empty directories (used by interface), nor things the
321 current user can't see
323 if isinstance(self, (Contribution, Session, SubContribution, Conference, Category)):
324 return get_attached_items(self, include_empty=False, include_hidden=False, preload_event=True)
325 else:
326 raise ValueError("Object of type '{}' cannot have attachments".format(type(self)))
329 class CategoryManager(ObjectHolder):
330 idxName = "categories"
331 counterName = "CATEGORY"
333 def getById(self, id_, quiet=False):
334 orig_id = id_ = str(id_)
335 if is_legacy_id(id_):
336 mapping = LegacyCategoryMapping.find_first(legacy_category_id=id_)
337 id_ = str(mapping.category_id) if mapping is not None else None
338 category = self._getIdx().get(id_) if id_ is not None else None
339 if category is None and not quiet:
340 raise KeyError(id_ if id_ is not None else orig_id)
341 return category
343 def add(self, category):
344 ObjectHolder.add(self, category)
345 # Add category to the name index
346 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
347 nameIdx.index(category)
349 def remove(self, category):
350 ObjectHolder.remove(self, category)
351 # remove category from the name index
352 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
353 nameIdx.unindex(category)
354 Catalog.getIdx('categ_conf_sd').remove_category(category.getId())
356 def _newId(self):
358 returns a new id for the category
359 the id must not already exist in the collection
361 id = ObjectHolder._newId(self)
362 while self.hasKey(id):
363 id = ObjectHolder._newId(self)
364 return id
366 def getRoot(self):
367 root = DBMgr.getInstance().getDBConnection().root()
368 if not root.has_key("rootCategory"):
369 r = Category()
370 r.setName("Home")
371 self.add(r)
372 root["rootCategory"] = r
373 return root["rootCategory"]
375 def getDefaultConference(self):
376 dconf = HelperMaKaCInfo.getMaKaCInfoInstance().getDefaultConference()
377 if dconf == None:
378 return HelperMaKaCInfo.getMaKaCInfoInstance().setDefaultConference(DefaultConference())
379 else:
380 return dconf
383 class Category(CommonObjectBase):
384 fossilizes(ICategoryFossil)
386 def __init__(self):
388 self.id = ""
389 self.name = ""
390 self.description = ""
391 self.subcategories = {}
392 self.conferences = OOTreeSet()
393 self._numConferences = 0
394 self.owner = None
395 self._defaultStyle = {"simple_event": "", "meeting": ""}
396 self._order = 0
397 self.__ac = AccessController(self)
398 self.__confCreationRestricted = 1
399 self.__confCreators = []
400 self._visibility = 999
401 self._statistics = {"events": None, "contributions": None, "resources": None,
402 "updated": None}
403 self._icon = None
404 self._tasksAllowed = False
405 self._tasks = {}
406 self._taskIdGenerator = 0
407 self._tasksPublic = True
408 self._tasksCommentPublic = True
409 self._tasksManagers = []
410 self._tasksCommentators = []
411 self._taskAccessList = []
412 self._timezone = ""
413 self._notifyCreationList = ""
415 def __cmp__(self, other):
416 if type(self) is not type(other):
417 # This is actually dangerous and the ZODB manual says not to do this
418 # because it relies on memory order. However, this branch should never
419 # be taken anyway since we do not store different types in the same set
420 # or use them as keys.
421 return cmp(hash(self), hash(other))
422 return cmp(self.getId(), other.getId())
424 @return_ascii
425 def __repr__(self):
426 path = '/'.join(self.getCategoryPathTitles()[:-1])
427 return '<Category({0}, {1}, {2})>'.format(self.getId(), self.getName(), path)
429 @property
430 def url(self):
431 if self.isRoot():
432 return url_for('misc.index')
433 else:
434 return url_for('category.categoryDisplay', self)
436 def getAccessController(self):
437 return self.__ac
439 def updateNonInheritingChildren(self, elem, delete=False):
440 pass
442 def getNotifyCreationList(self):
443 """ self._notifyCreationList is a string containing the list of
444 email addresses to send an email to when a new event is created"""
445 try:
446 return self._notifyCreationList
447 except:
448 self._notifyCreationList = ""
449 return self._notifyCreationList
451 def setNotifyCreationList(self, value):
452 self._notifyCreationList = value
454 def getUniqueId(self):
455 return "cat%s" % self.getId()
457 def setPaper(self, newPaper):
458 if self.getPaper() is not None:
459 raise MaKaCError(_("The paper for this conference has already been set"), _("Conference"))
460 self.paper = newPaper
461 self.paper.setOwner(self)
462 self.notifyModification()
464 def removePaper(self):
465 if self.paper is None:
466 return
467 self.paper.delete()
468 self.paper.setOwner(None)
469 self.paper = None
470 self.notifyModification()
472 def recoverPaper(self, p):
473 self.setPaper(p)
474 p.recover()
476 def getPaper(self):
477 try:
478 if self.paper:
479 pass
480 except AttributeError:
481 self.paper = None
482 return self.paper
484 def setSlides(self, newSlides):
485 if self.getSlides() is not None:
486 raise MaKaCError(_("The slides for this conference have already been set"), _("Conference"))
487 self.slides = newSlides
488 self.slides.setOwner(self)
489 self.notifyModification()
491 def removeSlides(self):
492 if self.slides is None:
493 return
494 self.slides.delete()
495 self.slides.setOwner(None)
496 self.slides = None
497 self.notifyModification()
499 def recoverSlides(self, s):
500 self.setSlides(s)
501 s.recover()
503 def getSlides(self):
504 try:
505 if self.slides:
506 pass
507 except AttributeError:
508 self.slides = None
509 return self.slides
511 def setVideo(self, newVideo):
512 if self.getVideo() is not None:
513 raise MaKaCError(_("The video for this conference has already been set"), _("Conference"))
514 self.video = newVideo
515 self.video.setOwner(self)
516 self.notifyModification()
518 def removeVideo(self):
519 if self.getVideo() is None:
520 return
521 self.video.delete()
522 self.video.setOwner(None)
523 self.video = None
524 self.notifyModification()
526 def recoverVideo(self, v):
527 self.setVideo(v)
528 v.recover()
530 def getVideo(self):
531 try:
532 if self.video:
533 pass
534 except AttributeError:
535 self.video = None
536 return self.video
538 def setPoster(self, newPoster):
539 if self.getPoster() is not None:
540 raise MaKaCError(_("the poster for this conference has already been set"), _("Conference"))
541 self.poster = newPoster
542 self.poster.setOwner(self)
543 self.notifyModification()
545 def removePoster(self):
546 if self.getPoster() is None:
547 return
548 self.poster.delete()
549 self.poster.setOwner(None)
550 self.poster = None
551 self.notifyModification()
553 def recoverPoster(self, p):
554 self.setPoster(p)
555 p.recover()
557 def getPoster(self):
558 try:
559 if self.poster:
560 pass
561 except AttributeError:
562 self.poster = None
563 return self.poster
565 def getTaskList(self):
566 try:
567 return self._tasks.values()
568 except:
569 self._tasks = {}
570 return self._tasks.values()
572 def getTitle(self):
573 return self.name
575 def getTasks(self):
576 try:
577 return self._tasks
578 except:
579 self._tasks = {}
580 return self._tasks
582 def getTask(self, taskId):
583 return self.getTasks().get(taskId, None)
585 def _getTasksAllowed(self):
586 try:
587 return self._tasksAllowed
588 except:
589 self._tasksAllowed = False
590 return self._tasksAllowed
592 def tasksAllowed(self):
593 if self.hasSubcategories():
594 return False
595 return self._getTasksAllowed()
597 def setTasksAllowed(self):
598 if self.hasSubcategories():
599 return False
600 self._getTasksAllowed()
601 self._tasksAllowed = True
602 self.notifyModification()
603 return True
605 def setTasksForbidden(self):
606 if len(self.getTaskList()) > 0:
607 return False
608 self._getTasksAllowed()
609 self._tasksAllowed = False
610 self.notifyModification()
611 return False
613 def _getNewTaskId(self):
614 try:
615 if self._taskIdGenerator:
616 pass
617 except:
618 self._taskIdGenerator = 0
619 self._taskIdGenerator = self._taskIdGenerator + 1
620 return self._taskIdGenerator
622 def newTask(self, user):
623 if user is None:
624 return None
625 newTask = task.Task(self, self._getNewTaskId(), user)
626 self.getTasks()["%s" % newTask.getId()] = newTask
627 self.notifyModification()
628 return newTask
630 def tasksPublic(self):
631 try:
632 return self._tasksPublic
633 except:
634 self._tasksPublic = True
635 return self._tasksPublic
637 def setTasksPublic(self):
638 self.tasksPublic()
639 self._tasksPublic = True
641 def setTasksPrivate(self):
642 self.tasksPublic()
643 self._tasksPublic = False
645 def tasksCommentPublic(self):
646 try:
647 return self._tasksCommentPublic
648 except:
649 self._tasksCommentPublic = True
650 return self._tasksCommentPublic
652 def setTasksCommentPublic(self):
653 self.tasksCommentPublic()
654 self._tasksCommentPublic = True
656 def setTasksCommentPrivate(self):
657 self.tasksCommentPublic()
658 self._tasksCommentPublic = False
660 def getTasksManagerList(self):
661 try:
662 return self._tasksManagers
663 except:
664 self._tasksManagers = []
665 self._p_changed = 1
666 return self._tasksManagers
668 def getTasksManager(self, index):
669 length = len(self.getTasksManagerList())
670 if index < 0 or index >= length:
671 return None
672 return self._tasksManagers[index]
674 def addTasksManager(self, user):
675 if user is None:
676 return False
677 self.getTasksManagerList().append(user)
678 self._p_changed = 1
679 return True
681 def removeTasksManager(self, index):
682 length = len(self.getTasksManagerList())
683 if index < 0 or index >= length:
684 return False
685 del self.getTasksManagerList()[index]
686 self._p_changed = 1
687 return True
689 def getTasksCommentatorList(self):
690 try:
691 return self._tasksCommentators
692 except:
693 self._tasksCommentators = []
694 self._p_changed = 1
695 return self._tasksCommentators
697 def getTasksCommentator(self, index):
698 length = len(self.getTasksCommentatorList())
699 if index < 0 or index >= length:
700 return None
701 return self._tasksCommentators[index]
703 def addTasksCommentator(self, user):
704 if user is None:
705 return False
706 self.getTasksCommentatorList().append(user)
707 self._p_changed = 1
708 return True
710 def removeTasksCommentator(self, index):
711 length = len(self.getTasksCommentatorList())
712 if index < 0 or index >= length:
713 return False
714 del self._tasksCommentators[index]
715 self._p_changed = 1
716 return True
718 def getTasksAccessList(self):
719 try:
720 return self._tasksAccessList
721 except:
722 self._tasksAccessList = []
723 self._p_changed = 1
724 return self._tasksAccessList
726 def getTasksAccessPerson(self, index):
727 length = len(self.getTasksAccessList())
728 if index < 0 or index >= length:
729 return None
730 return self._tasksAccessList[index]
732 def addTasksAccessPerson(self, user):
733 if user is None:
734 return False
735 self.getTasksAccessList().append(user)
736 self._p_changed = 1
737 return True
739 def removeTasksAccessPerson(self, index):
740 length = len(self.getTasksAccessList())
741 if index < 0 or index >= length:
742 return False
743 del self.getTasksAccessList()[index]
744 self._p_changed = 1
745 return True
747 def hasSubcategories(self):
748 return len(self.subcategories.values()) > 0
750 def getVisibility(self):
752 Returns category visibility, considering that it can be
753 restricted by parent categories
755 owner = self.getOwner()
756 visibility = int(self._visibility)
758 # visibility can be restricted by parent categories
759 if owner:
760 return max(0, min(visibility, owner.getVisibility() + 1))
761 else:
762 return visibility
764 def setVisibility(self, visibility=999):
765 self._visibility = int(visibility)
766 self._reindex()
768 def isSuggestionsDisabled(self):
769 try:
770 return self._suggestions_disabled
771 except AttributeError:
772 self._suggestions_disabled = False
773 return False
775 def setSuggestionsDisabled(self, value):
776 self._suggestions_disabled = value
778 def _reindex(self):
779 catIdx = indexes.IndexesHolder().getIndex('category')
780 catIdx.reindexCateg(self)
781 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
782 catDateIdx.reindexCateg(self)
783 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
784 catDateAllIdx.reindexCateg(self)
786 def isRoot(self):
787 #to be improved
788 return self.owner is None
790 def getDefaultStyle(self, type):
791 try:
792 return self._defaultStyle[type]
793 except:
794 return ""
796 def setDefaultStyle(self, type, style, subcatsStyle=False):
797 try:
798 self._defaultStyle[type] = style
799 except:
800 self._defaultStyle = {"simple_event": "", "meeting": ""}
801 self._defaultStyle[type] = style
802 self.notifyModification()
803 #raise str(subcatsStyle)
804 if subcatsStyle:
806 categ = self.getSubCategoryList()
808 for cat in categ:
809 cat.setDefaultStyle(type, style, subcatsStyle)
811 ##################################
812 # Fermi timezone awareness #
813 ##################################
814 def getTimezone(self):
815 try:
816 if self._timezone not in all_timezones:
817 self.setTimezone('UTC')
818 return self._timezone
819 except:
820 self.setTimezone('UTC')
821 return 'UTC'
823 def setTimezone(self, tz):
824 self._timezone = tz
826 def changeConfTimezones(self, tz):
827 for conference in self.getConferenceList():
828 conference.moveToTimezone(tz)
830 ##################################
831 # Fermi timezone awareness(end) #
832 ##################################
834 def getOrder(self):
835 try:
836 return self._order
837 except:
838 self._order = 0
839 return 0
841 def setOrder(self, order):
842 self._order = order
844 def getId(self):
845 return self.id
847 def setId(self, newId):
848 self.id = str(newId.strip())
850 def getLocator(self):
851 """Gives back (Locator) a globaly unique identification encapsulated
852 in a Locator object for the category instance """
853 d = Locator()
854 d["categId"] = self.getId()
855 return d
857 def getCategory(self):
858 return self
860 def getOwner(self):
861 return self.owner
863 def setOwner(self, newOwner):
864 if self.getOwner() is not None and newOwner is not None and self.getOwner() != newOwner:
865 self.move(newOwner)
866 else:
867 self.owner = newOwner
869 def getCategoryPath(self):
870 if self.isRoot():
871 return [self.getId()]
872 else:
873 l = self.getOwner().getCategoryPath()
874 l.append(self.getId())
875 return l
877 def iterParents(self):
878 categ = self
879 while not categ.isRoot():
880 categ = categ.getOwner()
881 yield categ
883 def getCategoryPathTitles(self):
884 # Breadcrumbs
885 breadcrumbs = []
886 cat = self
887 while cat:
888 breadcrumbs.insert(0, cat.getTitle())
889 cat = cat.getOwner()
890 return breadcrumbs
892 def delete(self, deleteConferences=0):
893 """removes completely a category (and all its sub-items) from the
894 system"""
896 oldOwner = self.getOwner()
898 if self.isRoot():
899 raise MaKaCError(_("Root category cannot be deleted"), _("Category"))
900 if not deleteConferences:
901 if self.getNumConferences() > 0:
902 raise MaKaCError(_("This category still contains some conferences, please remove them first"), _("Category"))
903 for subcateg in self.getSubCategoryList():
904 subcateg.delete(deleteConferences)
905 for conference in self.getConferenceList():
906 self.removeConference(conference, delete=True)
907 self.getOwner()._removeSubCategory(self)
908 CategoryManager().remove(self)
909 for prin in self.__ac.getAccessList():
910 if isinstance(prin, AvatarUserWrapper):
911 prin.unlinkTo(self, "access")
912 for prin in self.__ac.getModifierList():
913 if isinstance(prin, AvatarUserWrapper):
914 prin.unlinkTo(self, "manager")
915 TrashCanManager().add(self)
917 signals.category.deleted.send(self)
919 return
921 def move(self, newOwner):
922 oldOwner = self.getOwner()
923 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
924 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
926 catDateIdx.unindexCateg(self)
927 catDateAllIdx.unindexCateg(self)
929 self.getOwner()._removeSubCategory(self)
930 newOwner._addSubCategory(self)
931 self._reindex()
932 catDateIdx.indexCateg(self)
933 catDateAllIdx.indexCateg(self)
935 signals.category.moved.send(self, old_parent=oldOwner, new_parent=newOwner)
937 def getName(self):
938 return self.name
940 def setName(self, newName):
941 oldName = self.name
942 self.name = newName.strip()
944 # Reindex when name changes
945 nameIdx = indexes.IndexesHolder().getIndex('categoryName')
946 nameIdx.unindex(self)
947 nameIdx.index(self)
949 signals.category.title_changed.send(self, old=oldName, new=newName)
951 def getDescription(self):
952 return self.description
954 def setDescription(self, newDesc):
955 self.description = newDesc.strip()
957 def moveConference(self, conf, toCateg):
959 Moves a conference from this category to another one
961 self.removeConference(conf)
962 toCateg._addConference(conf)
963 signals.event.moved.send(conf, old_parent=self, new_parent=toCateg)
965 def _addSubCategory(self, newSc):
966 #categories can only contain either conferences either other categories
967 # but can never contain both. For the moment an exception is raised
968 # but this could be replaced by the following policy: if a
969 # sub-category is to be added to a category already containing
970 # conferences then the conferes are moved into the new sub-category
971 # and it is added to target category.
972 #first, check that the category is registered if not raise an exception
973 if len(self.conferences) > 0:
974 for conf in self.getConferenceList():
975 self.moveConference(conf, newSc)
977 if len(self.conferences) > 0:
978 raise MaKaCError(_("Cannot add subcategory: the current category already contains events"), _("Category"))
979 newSc.setOwner(self)
980 self.subcategories[newSc.getId()] = newSc
981 self._incNumConfs(newSc.getNumConferences())
983 def _removeSubCategory(self, sc):
984 """if the given subcategory belongs to the current category it removes
985 it from the subcategories list (don't use this method, use delete
986 instead)
988 if sc in self.getSubCategoryList():
989 self._decNumConfs(sc.getNumConferences())
990 del self.subcategories[sc.getId()]
991 sc.setOwner(None)
993 def newSubCategory(self, protection):
994 cm = CategoryManager()
995 sc = Category()
996 cm.add(sc)
998 # set the protection
999 sc.setProtection(protection)
1001 Catalog.getIdx('categ_conf_sd').add_category(sc.getId())
1002 signals.category.created.send(sc, parent=self)
1004 self._addSubCategory(sc)
1005 sc.setOrder(self.getSubCategoryList()[-1].getOrder() + 1)
1007 return sc
1009 def _incNumConfs(self, num=1):
1010 """Increases the number of conferences for the current category in a given number.
1011 WARNING: Only Categories must use this method!!!"""
1012 self._numConferences = self.getNumConferences()
1013 self._numConferences += num
1014 if self.getOwner() is not None:
1015 self.getOwner()._incNumConfs(num)
1017 def _decNumConfs(self, num=1):
1018 """Decreases the number of conferences for the current category in a given number.
1019 WARNING: Only Categories must use this method!!!"""
1020 self._numConferences = self.getNumConferences()
1021 self._numConferences -= num
1022 if self.getOwner() is not None:
1023 self.getOwner()._decNumConfs(num)
1025 def _addConference(self, newConf):
1026 if len(self.subcategories) > 0:
1027 raise MaKaCError(_("Cannot add event: the current category already contains some sub-categories"), _("Category"))
1028 if newConf.getId() == "":
1029 raise MaKaCError(_("Cannot add to a category an event which is not registered"), _("Category"))
1030 self.conferences.insert(newConf)
1031 newConf.addOwner(self)
1032 self._incNumConfs(1)
1033 self.indexConf(newConf)
1035 def getAccessKey(self):
1036 return ""
1038 def getModifKey(self):
1039 return ""
1041 def indexConf(self, conf):
1042 # Specific for category changes, calls Conference.indexConf()
1043 # (date-related indexes)
1044 catIdx = indexes.IndexesHolder().getIndex('category')
1045 catIdx.indexConf(conf)
1046 conf.indexConf()
1048 def unindexConf(self, conf):
1049 catIdx = indexes.IndexesHolder().getIndex('category')
1050 catIdx.unindexConf(conf)
1051 conf.unindexConf()
1053 def newConference(self, creator, id="", creationDate=None, modificationDate=None):
1054 conf = Conference(creator, id, creationDate, modificationDate)
1055 ConferenceHolder().add(conf)
1056 self._addConference(conf)
1057 conf.linkCreator()
1059 signals.event.created.send(conf, parent=self)
1061 return conf
1063 def removeConference(self, conf, notify=True, delete=False):
1064 if not (conf in self.conferences):
1065 return
1067 self.unindexConf(conf)
1069 self.conferences.remove(conf)
1070 if delete:
1071 conf.delete()
1072 conf.removeOwner(self, notify)
1073 self._decNumConfs(1)
1075 def getSubCategoryList(self):
1076 subcategs = self.subcategories.values()
1077 cl = []
1078 for categ in subcategs:
1079 cl.append("%04s%s-%s" % (categ.getOrder(), categ.getName().replace("-", ""), categ.getId()))
1080 cl.sort()
1081 res = []
1082 for c in cl:
1083 id = c.split("-")[1]
1084 res.append(self.subcategories[id])
1085 return res
1087 def iteritems(self, *args):
1088 return self.conferences.iteritems(*args)
1090 def itervalues(self, *args):
1091 return self.conferences.itervalues(*args)
1093 def getConferenceList(self, sortType=1):
1094 """returns the list of conferences included in the current category.
1095 Thanks to the used structure the list is sorted by date.
1096 We can choose other sorting types:
1098 sortType=1--> By date
1099 sortType=2--> Alphabetically
1100 sortType=3--> Alphabetically - Reversed
1103 res = sorted(self.conferences, cmp=Conference._cmpByDate)
1105 if sortType == 2:
1106 res.sort(Conference._cmpTitle)
1107 elif sortType == 3:
1108 res.sort(Conference._cmpTitle)
1109 res = reversed(res)
1110 return res
1112 def iterConferences(self):
1113 """returns the iterator for conferences.
1115 return self.conferences
1117 def iterAllConferences(self):
1118 """returns the iterator for conferences in all subcategories.
1120 for conf in self.conferences:
1121 yield conf
1123 for subcateg in self.subcategories.itervalues():
1124 for conf in subcateg.iterAllConferences():
1125 yield conf
1127 def getAllConferenceList(self):
1128 """returns the list of all conferences included in the current category
1129 and in all its subcategories"""
1130 res = self.getConferenceList()
1131 subcategs = self.getSubCategoryList()
1132 if subcategs != []:
1133 for subcateg in subcategs:
1134 res.extend(subcateg.getAllConferenceList())
1135 return res
1137 def getRelativeEvent(self, which, conf=None):
1138 index = Catalog.getIdx('categ_conf_sd').getCategory(self.getId())
1139 if which == 'first':
1140 return list(index[index.minKey()])[0]
1141 elif which == 'last':
1142 return list(index[index.maxKey()])[-1]
1143 elif which in ('next', 'prev'):
1144 categIter = index.itervalues()
1145 if conf:
1146 prev = None
1147 for c in categIter:
1148 if c == conf:
1149 break
1150 prev = c
1151 nextEvt = next(categIter, None)
1152 if which == 'next':
1153 return nextEvt
1154 else:
1155 return prev
1156 else:
1157 raise AttributeError("'conf' parameter missing")
1158 else:
1159 raise AttributeError("Unknown argument value: '%s'" % which)
1161 def _setNumConferences(self):
1162 self._numConferences = 0
1163 if self.conferences:
1164 self._incNumConfs(len(self.conferences))
1165 else:
1166 for sc in self.getSubCategoryList():
1167 self._incNumConfs(sc.getNumConferences())
1169 def getNumConferences(self):
1170 """returns the total number of conferences contained in the current
1171 category and all its sub-categories (if any)"""
1172 #this new approach will speed up considerably the counting of category
1173 # conferences. However, it will give non accurate results for
1174 # conferences within many categories (a conference will be counted
1175 # twice in parent categories).
1176 # Besides this approach will generate much more conflict errors. This
1177 # can be reduced by simply isolating the counter in a separate object.
1178 try:
1179 if self._numConferences:
1180 pass
1181 except AttributeError:
1182 self._setNumConferences()
1183 return self._numConferences
1185 def _getRepository(self):
1186 dbRoot = DBMgr.getInstance().getDBConnection().root()
1187 try:
1188 fr = dbRoot["local_repositories"]["main"]
1189 except KeyError, e:
1190 fr = fileRepository.MaterialLocalRepository()
1191 dbRoot["local_repositories"] = OOBTree()
1192 dbRoot["local_repositories"]["main"] = fr
1193 return fr
1195 def removeResource(self, res):
1196 pass
1198 def setIcon(self, iconFile):
1199 iconFile.setOwner(self)
1200 iconFile.setId("icon")
1201 iconFile.archive(self._getRepository())
1202 iconFile.setProtection(-1)
1203 if self.getIcon() is not None:
1204 self._icon.delete()
1205 self._icon = iconFile
1206 self.notifyModification()
1208 def getIcon(self):
1209 try:
1210 if self._icon:
1211 pass
1212 except AttributeError, e:
1213 self._icon = None
1214 return self._icon
1216 def getIconURL(self):
1217 if self.getIcon() is None:
1218 return ""
1219 return self._icon.getURL()
1221 def removeIcon(self):
1222 if self.getIcon() is None:
1223 return
1224 self._icon.delete()
1225 self._icon = None
1226 self.notifyModification()
1228 def recoverIcon(self, icon):
1229 icon.setOwner(self)
1230 if self.getIcon() is not None:
1231 self._icon.delete()
1232 self._icon = icon
1233 icon.recover()
1234 self.notifyModification()
1236 def getManagerList(self):
1237 return self.__ac.getModifierList()
1239 def grantModification(self, prin):
1240 self.__ac.grantModification(prin)
1241 if isinstance(prin, AvatarUserWrapper):
1242 prin.linkTo(self, "manager")
1244 def revokeModification(self, prin):
1245 self.__ac.revokeModification(prin)
1246 if isinstance(prin, AvatarUserWrapper):
1247 prin.unlinkTo(self, "manager")
1249 def canModify(self, aw_or_user):
1250 if hasattr(aw_or_user, 'getUser'):
1251 aw_or_user = aw_or_user.getUser()
1252 return self.canUserModify(aw_or_user)
1254 def canUserModify(self, av):
1255 inherited = 0
1256 if self.getOwner() is not None:
1257 inherited = self.getOwner().canUserModify(av)
1258 return inherited or self.__ac.canModify(av)
1260 def getAllowedToAccessList(self):
1261 return self.__ac.getAccessList()
1263 def canKeyAccess(self, aw):
1264 # Categories don't allow access keys
1265 return False
1267 def isProtected(self):
1268 return self.__ac.isProtected()
1270 def getAccessProtectionLevel(self):
1271 return self.__ac.getAccessProtectionLevel()
1273 def isItselfProtected(self):
1274 return self.__ac.isItselfProtected()
1276 def hasAnyProtection(self):
1277 if self.__ac.isProtected() or len(self.getDomainList()) > 0:
1278 return True
1279 if self.getAccessProtectionLevel() == -1: # PUBLIC
1280 return False
1281 if self.getOwner() is not None:
1282 return self.getOwner().hasAnyProtection()
1283 return False
1285 def setProtection(self, private):
1287 Allows to change the category's access protection
1290 oldProtection = 1 if self.isProtected() else -1
1292 self.__ac.setProtection(private)
1293 if oldProtection != private:
1294 signals.category.protection_changed.send(self, old=oldProtection, new=private)
1296 def hasProtectedOwner(self):
1297 return self.__ac._getFatherProtection()
1299 def isAllowedToAccess(self, av):
1300 """Says whether an avatar can access a category independently of it is
1301 or not protected or domain filtered
1303 if self.__ac.canUserAccess(av) or self.canUserModify(av):
1304 return True
1305 if not self.isItselfProtected() and self.getOwner():
1306 return self.getOwner().isAllowedToAccess(av)
1308 def canView(self, aw):
1309 if self.canAccess(aw):
1310 return True
1311 for conf in self.getConferenceList():
1312 if conf.canView(aw):
1313 return True
1314 for subcateg in self.getSubCategoryList():
1315 if subcateg.canView(aw):
1316 return True
1317 return False
1319 def canAccess(self, aw):
1320 if not self.hasAnyProtection():
1321 return True
1322 if not self.isProtected():
1323 # domain checking only triggered if the category is PUBLIC
1324 return self.canIPAccess(request.remote_addr) or \
1325 self.isAllowedToCreateConference(aw.getUser()) or \
1326 self.isAllowedToAccess(aw.getUser())
1327 return self.isAllowedToCreateConference(aw.getUser()) or \
1328 self.isAllowedToAccess(aw.getUser())
1330 def grantAccess(self, prin):
1331 self.__ac.grantAccess(prin)
1332 if isinstance(prin, AvatarUserWrapper):
1333 prin.linkTo(self, "access")
1335 def revokeAccess(self, prin):
1336 self.__ac.revokeAccess(prin)
1337 if isinstance(prin, AvatarUserWrapper):
1338 prin.unlinkTo(self, "access")
1340 def isConferenceCreationRestricted(self):
1341 return self.__confCreationRestricted
1343 def restrictConferenceCreation(self):
1344 self.__confCreationRestricted = 1
1346 def allowConferenceCreation(self):
1347 self.__confCreationRestricted = 0
1349 def grantConferenceCreation(self, prin):
1350 if prin not in self.__confCreators:
1351 self.__confCreators.append(prin)
1352 if isinstance(prin, AvatarUserWrapper):
1353 prin.linkTo(self, "creator")
1354 self._p_changed = 1
1356 def revokeConferenceCreation(self, prin):
1357 if prin in self.__confCreators:
1358 self.__confCreators.remove(prin)
1359 if isinstance(prin, AvatarUserWrapper):
1360 prin.unlinkTo(self, "creator")
1361 self._p_changed = 1
1363 def getConferenceCreatorList(self):
1364 return self.__confCreators
1366 def isAllowedToCreateConference(self, av):
1368 if self.canUserModify(av):
1369 return 1
1371 # Avatar is directly in the list
1372 if av in self.__confCreators:
1373 return 1
1375 # Otherwise, if it is a member of one of the groups in the list...
1376 for group in self.__confCreators:
1377 if isinstance(group, GroupWrapper):
1378 if group.containsUser(av):
1379 return 1
1380 return 0
1382 def canCreateConference(self, av):
1383 if not self.isConferenceCreationRestricted():
1384 return 1
1385 return self.isAllowedToCreateConference(av)
1387 def requireDomain(self, dom):
1388 self.__ac.requireDomain(dom)
1389 signals.category.domain_access_granted.send(self, domain=dom)
1391 def freeDomain(self, dom):
1392 self.__ac.freeDomain(dom)
1393 signals.category.domain_access_revoked.send(self, domain=dom)
1396 def getDomainList(self):
1397 return self.__ac.getRequiredDomainList()
1399 def getStatistics(self):
1400 try:
1401 if self._statistics:
1402 pass
1403 except AttributeError, e:
1404 self._statistics = {}
1405 return self._statistics
1407 def notifyModification(self, raiseEvent=True):
1408 """Method called to notify the current category has been modified.
1410 if raiseEvent:
1411 signals.category.data_changed.send(self)
1412 self._p_changed = 1
1415 class CustomLocation(Persistent):
1417 def __init__(self, **locationData):
1418 self.name = ""
1419 self.address = ""
1420 self.room = ""
1422 def setValues(self, data):
1423 self.setName(data.get("name", ""))
1424 self.setAddress(data.get("address", ""))
1425 self.setRoom(data.get("room", ""))
1427 def getValues(self):
1428 d = {}
1429 d["name"] = self.getName()
1430 d["address"] = self.getAddress()
1431 d["room"] = self.getRoom()
1432 return d
1434 def clone(self):
1435 newCL = CustomLocation()
1436 newCL.setValues(self.getValues())
1437 return newCL
1439 def setName(self, newName):
1440 self.name = newName
1442 def getName(self):
1443 return self.name
1445 def setAddress(self, newAddress):
1446 self.address = newAddress
1448 def getAddress(self):
1449 return self.address
1451 def setRoom(self, newRoom):
1452 self.room = newRoom
1454 def getRoom(self):
1455 return self.room
1458 class CustomRoom(Persistent):
1460 def __init__(self):
1461 self.name = ""
1463 def setValues(self, data):
1464 self.setName(data.get("name", ""))
1465 self.setFullName(data.get("fullName"))
1467 def getValues(self):
1468 d = {}
1469 d["name"] = self.getName()
1470 d["fullName"] = self.getFullName()
1471 return d
1473 def getId(self):
1474 return "Custom"
1476 def clone(self):
1477 newCR = CustomRoom()
1478 newCR.setValues(self.getValues())
1479 return newCR
1481 def setName(self, newName):
1482 self.name = newName.strip()
1484 def getName(self):
1485 return self.name
1487 def retrieveFullName(self, location):
1488 if not location:
1489 return
1490 room = Room.find_first(Room.name == fix_broken_string(self.name, True),
1491 Location.name == fix_broken_string(location, True),
1492 _join=Room.location)
1493 self.fullName = room.full_name if room else None
1495 def setFullName(self, newFullName):
1496 self.fullName = newFullName
1498 def getFullName(self):
1499 if not hasattr(self, 'fullName'):
1500 self.fullName = None
1501 return self.fullName
1504 class ConferenceParticipation(Persistent, Fossilizable):
1506 fossilizes(IConferenceParticipationFossil, IConferenceParticipationMinimalFossil)
1508 def __init__(self):
1509 self._firstName=""
1510 self._surName=""
1511 self._email=""
1512 self._affiliation=""
1513 self._address=""
1514 self._phone=""
1515 self._title=""
1516 self._fax=""
1518 def _notifyModification( self ):
1519 pass
1521 def setValues(self, data):
1522 self.setFirstName(data.get("firstName", ""))
1523 self.setFamilyName(data.get("familyName",""))
1524 self.setAffiliation(data.get("affilation",""))
1525 self.setAddress(data.get("address",""))
1526 self.setEmail(data.get("email",""))
1527 self.setFax(data.get("fax",""))
1528 self.setTitle(data.get("title",""))
1529 self.setPhone(data.get("phone",""))
1530 self._notifyModification()
1532 def getValues(self):
1533 data={}
1534 data["firstName"]=self.getFirstName()
1535 data["familyName"]=self.getFamilyName()
1536 data["affilation"]=self.getAffiliation()
1537 data["address"]=self.getAddress()
1538 data["email"]=self.getEmail()
1539 data["fax"]=self.getFax()
1540 data["title"]=self.getTitle()
1541 data["phone"]=self.getPhone()
1542 return data
1544 def setId(self, newId):
1545 self._id = newId
1547 def getId( self ):
1548 return self._id
1550 def setDataFromAvatar(self,av):
1551 # av is an Avatar object.
1552 if av is None:
1553 return
1554 self.setFirstName(av.getName())
1555 self.setFamilyName(av.getSurName())
1556 self.setEmail(av.getEmail())
1557 self.setAffiliation(av.getOrganisation())
1558 self.setAddress(av.getAddress())
1559 self.setPhone(av.getTelephone())
1560 self.setTitle(av.getTitle())
1561 self.setFax(av.getFax())
1562 self._notifyModification()
1564 def setDataFromOtherCP(self,cp):
1565 # cp is a ConferenceParticipation object.
1566 if cp is None:
1567 return
1568 self.setFirstName(cp.getFirstName())
1569 self.setFamilyName(cp.getFamilyName())
1570 self.setEmail(cp.getEmail())
1571 self.setAffiliation(cp.getAffiliation())
1572 self.setAddress(cp.getAddress())
1573 self.setPhone(cp.getPhone())
1574 self.setTitle(cp.getTitle())
1575 self.setFax(cp.getFax())
1576 self._notifyModification()
1578 def delete( self ):
1579 TrashCanManager().add(self)
1581 def recover(self):
1582 TrashCanManager().remove(self)
1584 @Updates (['MaKaC.conference.ConferenceParticipation',
1585 'MaKaC.conference.SessionChair',
1586 'MaKaC.conference.SlotChair'], 'firstName')
1587 def setFirstName(self,newName):
1588 tmp=newName.strip()
1589 if tmp==self._firstName:
1590 return
1591 self._firstName=tmp
1592 self._notifyModification()
1594 def getFirstName( self ):
1595 return self._firstName
1597 @Updates (['MaKaC.conference.ConferenceParticipation',
1598 'MaKaC.conference.SessionChair',
1599 'MaKaC.conference.SlotChair'], 'familyName')
1600 def setFamilyName(self,newName):
1601 tmp=newName.strip()
1602 if tmp==self._surName:
1603 return
1604 self._surName=tmp
1605 self._notifyModification()
1607 def getFamilyName( self ):
1608 return self._surName
1610 @Updates (['MaKaC.conference.ConferenceParticipation',
1611 'MaKaC.conference.SessionChair',
1612 'MaKaC.conference.SlotChair'], 'email')
1613 def setEmail(self,newMail):
1614 tmp=newMail.strip()
1615 if tmp==self._email:
1616 return
1617 self._email=newMail.strip()
1618 self._notifyModification()
1620 def getEmail( self ):
1621 return self._email
1623 @Updates (['MaKaC.conference.ConferenceParticipation',
1624 'MaKaC.conference.SessionChair',
1625 'MaKaC.conference.SlotChair'], 'affiliation')
1626 def setAffiliation(self,newAffil):
1627 self._affiliation=newAffil.strip()
1628 self._notifyModification()
1630 def getAffiliation(self):
1631 return self._affiliation
1633 @Updates (['MaKaC.conference.ConferenceParticipation',
1634 'MaKaC.conference.SessionChair',
1635 'MaKaC.conference.SlotChair'], 'address')
1636 def setAddress(self,newAddr):
1637 self._address=newAddr.strip()
1638 self._notifyModification()
1640 def getAddress(self):
1641 return self._address
1643 @Updates (['MaKaC.conference.ConferenceParticipation',
1644 'MaKaC.conference.SessionChair',
1645 'MaKaC.conference.SlotChair'], 'phone')
1646 def setPhone(self,newPhone):
1647 self._phone=newPhone.strip()
1648 self._notifyModification()
1650 def getPhone(self):
1651 return self._phone
1653 @Updates (['MaKaC.conference.ConferenceParticipation',
1654 'MaKaC.conference.SessionChair',
1655 'MaKaC.conference.SlotChair'], 'title')
1656 def setTitle(self,newTitle):
1657 self._title=newTitle.strip()
1658 self._notifyModification()
1660 def getTitle(self):
1661 return self._title
1663 @Updates (['MaKaC.conference.ConferenceParticipation',
1664 'MaKaC.conference.SessionChair',
1665 'MaKaC.conference.SlotChair'], 'fax')
1666 def setFax(self,newFax):
1667 self._fax=newFax.strip()
1668 self._notifyModification()
1670 def getFax(self):
1671 return self._fax
1673 def getFullName( self ):
1674 res = self.getFamilyName()
1675 if self.getFirstName() != "":
1676 if res.strip() != "":
1677 res = "%s, %s"%( res, self.getFirstName() )
1678 else:
1679 res = self.getFirstName()
1680 if self.getTitle() != "":
1681 res = "%s %s"%( self.getTitle(), res )
1682 return res
1684 def getFullNameNoTitle( self ):
1685 res = self.getFamilyName()
1686 if self.getFirstName() != "":
1687 if res.strip() != "":
1688 res = "%s, %s"%( res, self.getFirstName() )
1689 else:
1690 res = self.getFirstName()
1691 return res
1693 def getDirectFullName( self ):
1694 res = "%s %s"%( self.getFirstName(), self.getFamilyName() )
1695 res=res.strip()
1696 if self.getTitle() != "":
1697 res = "%s %s"%( self.getTitle(), res )
1698 return res
1700 def getAbrName(self):
1701 res = self.getFamilyName()
1702 if self.getFirstName():
1703 if res:
1704 res = "%s, " % res
1705 res = "%s%s." % (res, safe_upper(safe_slice(self.getFirstName(), 0, 1)))
1706 return res
1708 @staticmethod
1709 def _cmpFamilyName( cp1, cp2 ):
1710 o1 = "%s %s"%(cp1.getFamilyName(), cp1.getFirstName())
1711 o2 = "%s %s"%(cp2.getFamilyName(), cp2.getFirstName())
1712 o1=o1.lower().strip()
1713 o2=o2.lower().strip()
1714 return cmp( o1, o2 )
1717 class ConferenceChair(ConferenceParticipation, Fossilizable):
1719 fossilizes(IConferenceParticipationFossil)
1721 def __init__(self):
1722 self._conf=None
1723 self._id=""
1724 ConferenceParticipation.__init__(self)
1726 def _notifyModification( self ):
1727 if self._conf != None:
1728 self._conf.notifyModification()
1730 def clone(self):
1731 newCC=ConferenceChair()
1732 newCC.setValues(self.getValues())
1733 return newCC
1735 def getConference(self):
1736 return self._conf
1738 def getId(self):
1739 return self._id
1741 def includeInConference(self,conf,id):
1742 if self.getConference()==conf and self.getId()==id.strip():
1743 return
1744 self._conf=conf
1745 self._id=id
1747 def delete( self ):
1748 self._conf=None
1749 ConferenceParticipation.delete(self)
1751 def getLocator(self):
1752 if self.getConference() is None:
1753 return None
1754 loc=self.getConference().getLocator()
1755 loc["chairId"]=self.getId()
1756 return loc
1758 class SubmitterIndex(Persistent):
1759 """Index for contribution submitters.
1761 This class allows to index users with submission privileges over the
1762 conference contributions so the owner can answer optimally to the query
1763 if a user has any submission privilege over any contribution
1764 of the conference.
1765 It is implemented by simply using a BTree where the Avatar id is used
1766 as key (because it is unique and non variable) and a list of
1767 contributions over which he has submission privileges is kept as values.
1768 It is the responsability of the index owner (conference contributions)
1769 to keep it up-to-date i.e. notify conference sumitters additions and
1770 removals.
1773 def __init__( self ):
1774 self._idx = OOBTree()
1775 self._idxEmail = OOBTree()
1777 def _getIdxEmail(self):
1778 try:
1779 return self._idxEmail
1780 except:
1781 self._idxEmail = OOBTree()
1782 return self._idxEmail
1784 def getContributions(self,av):
1785 """Gives a list with the contributions over which a user has
1786 coordination privileges
1788 if av == None:
1789 return []
1790 ret = self._idx.get(av.getId(),[])
1791 if not ret:
1792 self._moveEmailtoId(av)
1793 ret = self._idx.get(av.getId(),[])
1794 return ret
1796 def index(self,av,contrib):
1797 """Registers in the index a submitter of a contribution.
1799 if av==None or contrib==None:
1800 return
1801 if not self._idx.has_key(av.getId()):
1802 l=[]
1803 self._idx[av.getId()]=l
1804 else:
1805 l=self._idx[av.getId()]
1806 if contrib not in l:
1807 l.append(contrib)
1808 self._idx[av.getId()]=l
1810 def indexEmail(self, email, contrib):
1811 if not email or not contrib:
1812 return
1813 if not self._getIdxEmail().has_key(email):
1814 l = [contrib]
1815 self._getIdxEmail()[email] = l
1816 else:
1817 l = self._getIdxEmail()[email]
1818 if not contrib in l:
1819 l.append(contrib)
1820 self._getIdxEmail()[email] = l
1823 def unindex(self,av,contrib):
1824 if av==None or contrib==None:
1825 return
1826 l=self._idx.get(av.getId(),[])
1827 if contrib in l:
1828 l.remove(contrib)
1829 self._idx[av.getId()]=l
1831 def unindexEmail(self, email, contrib):
1832 if not email or not contrib:
1833 return
1834 if self._getIdxEmail().has_key(email):
1835 l = self._getIdxEmail()[email]
1836 if contrib in l:
1837 l.remove(contrib)
1838 if l == []:
1839 del self._getIdxEmail()[email]
1840 else:
1841 self._getIdxEmail()[email] = l
1843 def _moveEmailtoId(self, av):
1844 id = av.getId()
1845 email = av.getEmail()
1846 if not self._idx.has_key(id):
1847 if self._getIdxEmail().has_key(email):
1848 self._idx[id] = self._getIdxEmail()[email]
1849 del self._getIdxEmail()[email]
1852 class ReportNumberHolder(Persistent):
1854 def __init__(self, owner):
1855 self._owner=owner
1856 self._reports={}
1858 def getOwner(self):
1859 return self._owner
1861 def addReportNumber(self, system, number):
1862 if system in self.getReportNumberKeys() or system in Config.getInstance().getReportNumberSystems().keys():
1863 try:
1864 if not number in self._reports[system]:
1865 self._reports[system].append(number)
1866 except:
1867 self._reports[system]=[ number ]
1868 self.notifyModification()
1870 def removeReportNumber(self, system, number):
1871 if self.hasReportNumbersBySystem(system):
1872 if number in self._reports[system]:
1873 self._reports[system].remove(number)
1874 self.notifyModification()
1876 def removeReportNumberById(self, id):
1877 try:
1878 rn = self.listReportNumbers()[int(id)]
1879 self.removeReportNumber(rn[0], rn[1])
1880 except:
1881 pass
1883 def hasReportNumbersBySystem(self, system):
1884 return self._reports.has_key(system)
1886 def getReportNumbersBySystem(self, system):
1887 if self.hasReportNumbersBySystem(system):
1888 return self._reports[system]
1889 return None
1891 def getReportNumberKeys(self):
1892 return self._reports.keys()
1894 def listReportNumbersOnKey(self, key):
1895 reports=[]
1896 if key in self._reports.keys():
1897 # compatibility with previous versions
1898 if type(self._reports[key]) is str:
1899 self._reports[key] = [ self._reports[key] ]
1900 for number in self._reports[key]:
1901 reports.append([key, number])
1902 return reports
1904 def hasReportNumberOnSystem(self, system, number):
1905 if self.hasReportNumbersBySystem(system):
1906 if number in self._reports[system]:
1907 return True
1908 return False
1910 def listReportNumbers(self):
1911 reports=[]
1912 keys = self._reports.keys()
1913 keys.sort()
1914 for key in keys:
1915 # compatibility with previous versions
1916 if type(self._reports[key]) is str:
1917 self._reports[key] = [ self._reports[key] ]
1918 for number in self._reports[key]:
1919 reports.append([key, number])
1920 return reports
1922 def clone(self, owner):
1923 newR=ReportNumberHolder(owner)
1924 for key in self._reports.keys():
1925 for number in self._reports[key]:
1926 newR.addReportNumber(key, number)
1927 return newR
1929 def notifyModification(self):
1930 self._p_changed=1
1931 if self.getOwner() != None:
1932 self.getOwner().notifyModification()
1935 class Conference(CommonObjectBase, Locatable):
1936 """This class represents the real world conferences themselves. Objects of
1937 this class will contain basic data about the confence and will provide
1938 access to other objects representing certain parts of the conferences
1939 (ex: contributions, sessions, ...).
1942 fossilizes(IConferenceFossil, IConferenceMinimalFossil, IConferenceEventInfoFossil)
1944 def __init__(self, creator, id="", creationDate = None, modificationDate = None):
1945 """Class constructor. Initialise the class attributes to the default
1946 values.
1947 Params:
1948 confData -- (Dict) Contains the data the conference object has to
1949 be initialised to.
1951 #IndexedObject.__init__(self)
1952 if creator == None:
1953 raise MaKaCError( _("A creator must be specified when creating a new Event"), _("Event"))
1954 self.__creator = creator
1955 self.id = id
1956 self.title = ""
1957 self.description = ""
1958 self.places = []
1959 self.rooms = []
1960 ###################################
1961 # Fermi timezone awareness #
1962 ###################################
1963 self.startDate = nowutc()
1964 self.endDate = nowutc()
1965 self.timezone = ""
1966 ###################################
1967 # Fermi timezone awareness(end) #
1968 ###################################
1969 self._screenStartDate = None
1970 self._screenEndDate = None
1971 self.contactInfo =""
1972 self.chairmanText = ""
1973 self.chairmans = []
1974 self._chairGen=Counter()
1975 self._chairs=[]
1976 self.sessions = {}
1977 self.__sessionGenerator = Counter() # Provides session unique
1978 # identifiers for this conference
1979 self.contributions = {}
1980 self.__contribGenerator = Counter() # Provides contribution unique
1981 # identifiers for this conference
1982 self.programDescription = ""
1983 self.program = []
1984 self.__programGenerator = Counter() # Provides track unique
1985 # identifiers for this conference
1986 self.__ac = AccessController(self)
1987 self.materials = {}
1988 self.__materialGenerator = Counter() # Provides material unique
1989 # identifiers for this conference
1990 self.paper = None
1991 self.slides = None
1992 self.video = None
1993 self.poster = None
1994 self.__schedule=None
1995 self.__owners = []
1996 if creationDate:
1997 self._creationDS = creationDate
1998 else:
1999 self._creationDS = nowutc() #creation timestamp
2000 if modificationDate:
2001 self._modificationDS = modificationDate
2002 else:
2003 self._modificationDS = nowutc() #modification timestamp
2005 self.abstractMgr = review.AbstractMgr(self)
2006 self._logo = None
2007 self._trackCoordinators = TCIndex() #index for the track coordinators
2008 self._supportInfo = SupportInfo(self, "Support")
2009 self._contribTypes = {}
2010 self.___contribTypeGenerator = Counter()
2011 self._authorIdx=AuthorIndex()
2012 self._speakerIdx=AuthorIndex()
2013 self._primAuthIdx=_PrimAuthIdx(self)
2014 self._sessionCoordinators=SCIndex()
2015 self._sessionCoordinatorRights = []
2016 self._submitterIdx=SubmitterIndex()
2017 self._boa=BOAConfig(self)
2018 self._registrationForm = registration.RegistrationForm(self)
2019 self._evaluationCounter = Counter()
2020 self._evaluations = [Evaluation(self)]
2021 self._registrants = {} #key=registrantId; value=Registrant
2022 self._bookings = {}
2023 self._registrantGenerator = Counter()
2024 self._accessKey=""
2025 self._modifKey=""
2026 self._closed = False
2027 self._visibility = 999
2028 self._pendingQueuesMgr=pendingQueues.ConfPendingQueuesMgr(self)
2029 self._sections = []
2030 self._participation = Participation(self)
2031 self._reportNumberHolder=ReportNumberHolder(self)
2032 self._enableSessionSlots = False
2033 self._enableSessions = False
2034 self._autoSolveConflict = True
2035 self.__badgeTemplateManager = BadgeTemplateManager(self)
2036 self.__posterTemplateManager = PosterTemplateManager(self)
2037 self._keywords = ""
2038 self._confPaperReview = ConferencePaperReview(self)
2039 self._confAbstractReview = ConferenceAbstractReview(self)
2040 self._orgText = ""
2041 self._comments = ""
2042 self._sortUrlTag = ""
2044 self._observers = []
2046 @return_ascii
2047 def __repr__(self):
2048 return '<Conference({0}, {1}, {2})>'.format(self.getId(), self.getTitle(), self.getStartDate())
2050 @property
2051 def has_legacy_id(self):
2052 """Returns True if the event has a broken legacy ID.
2054 These IDs are not compatible with new code since they are not
2055 numeric or have a leading zero, resulting in different events
2056 with the same numeric event id.
2058 return is_legacy_id(self.id)
2060 @property
2061 def all_manager_emails(self):
2062 """Returns the emails of all managers, including the creator"""
2063 emails = {self.getCreator().getEmail()} | {u.getEmail() for u in self.getManagerList()}
2064 return {e for e in emails if e}
2066 @property
2067 @memoize_request
2068 def note(self):
2069 from indico.modules.events.notes.models.notes import EventNote
2070 return EventNote.get_for_linked_object(self)
2072 @unify_user_args
2073 def log(self, realm, kind, module, summary, user=None, type_=u'simple', data=None):
2074 """Creates a new log entry for the event
2076 :param realm: A value from :class:`.EventLogRealm` indicating
2077 the realm of the action.
2078 :param kind: A value from :class:`.EventLogKind` indicating
2079 the kind of the action that was performed.
2080 :param module: A human-friendly string describing the module
2081 related to the action.
2082 :param summmary: A one-line summary describing the logged action.
2083 :param user: The user who performed the action.
2084 :param type_: The type of the log entry. This is used for custom
2085 rendering of the log message/data
2086 :param data: JSON-serializable data specific to the log type.
2088 In most cases the ``simple`` log type is fine. For this type,
2089 any items from data will be shown in the detailed view of the
2090 log entry. You may either use a dict (which will be sorted)
2091 alphabetically or a list of ``key, value`` pairs which will
2092 be displayed in the given order.
2094 db.session.add(EventLogEntry(event_id=int(self.id), user=user, realm=realm, kind=kind, module=module,
2095 type=type_, summary=summary, data=data or {}))
2097 @staticmethod
2098 def _cmpByDate(self, toCmp):
2099 res = cmp(self.getStartDate(), toCmp.getStartDate())
2100 if res != 0:
2101 return res
2102 else:
2103 return cmp(self, toCmp)
2105 def __cmp__(self, toCmp):
2106 if isinstance(toCmp, Conference):
2107 return cmp(self.getId(), toCmp.getId())
2108 else:
2109 return cmp(hash(self), hash(toCmp))
2111 def __eq__(self, toCmp):
2112 return self is toCmp
2114 def __ne__(self, toCmp):
2115 return not(self is toCmp)
2117 def setUrlTag(self, tag):
2118 self._sortUrlTag = tag
2120 def getUrlTag(self):
2121 try:
2122 return self._sortUrlTag
2123 except:
2124 self._sortUrlTag = ""
2125 return self._sortUrlTag
2127 def setComments(self,comm=""):
2128 self._comments = comm.strip()
2130 def getComments(self):
2131 try:
2132 if self._comments:
2133 pass
2134 except AttributeError,e:
2135 self.setComments()
2136 return self._comments
2138 def getConfPaperReview(self):
2139 if not hasattr(self, "_confPaperReview"):
2140 self._confPaperReview = ConferencePaperReview(self)
2141 return self._confPaperReview
2143 def getConfAbstractReview(self):
2144 if not hasattr(self, "_confAbstractReview"):
2145 self._confAbstractReview = ConferenceAbstractReview(self)
2146 return self._confAbstractReview
2148 def getOrgText( self ):
2149 try:
2150 return self._orgText
2151 except:
2152 self.setOrgText()
2153 return ""
2155 def setOrgText( self, org="" ):
2156 self._orgText = org
2158 def cleanCache( self ):
2159 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
2160 ScheduleToJson.cleanConferenceCache(self)
2161 ContextManager.set('clean%s'%self.getUniqueId(), True)
2163 def updateNonInheritingChildren(self, elem, delete=False):
2164 self.getAccessController().updateNonInheritingChildren(elem, delete)
2166 def getKeywords(self):
2167 try:
2168 return self._keywords
2169 except:
2170 self._keywords = ""
2171 return ""
2173 def setKeywords(self, keywords):
2174 self._keywords = keywords
2176 # Room booking related ===================================================
2178 def getRoomBookingList(self):
2179 """Returns list of bookings for this conference."""
2180 # In case anyone wonders why this method is still here: Various fossils expect/use it.
2181 if not self.getId().isdigit():
2182 return []
2183 return Reservation.find_all(event_id=int(self.getId()))
2185 # ========================================================================
2187 def getParticipation(self):
2188 try :
2189 if self._participation :
2190 pass
2191 except AttributeError :
2192 self._participation = Participation(self)
2193 return self._participation
2195 def getType( self ):
2196 import MaKaC.webinterface.webFactoryRegistry as webFactoryRegistry
2197 wr = webFactoryRegistry.WebFactoryRegistry()
2198 wf = wr.getFactory(self)
2199 if wf != None:
2200 type = wf.getId()
2201 else:
2202 type = "conference"
2203 return type
2205 def getVerboseType( self ):
2206 # Like getType, but returns "Lecture" instead of "simple_type"
2207 type = self.getType()
2208 if type == "simple_event":
2209 type = "lecture"
2210 return type.capitalize()
2213 def getEnableSessionSlots(self):
2214 #try :
2215 # if self._enableSessionSlots :
2216 # pass
2217 #except AttributeError :
2218 # self._enableSessionSlots = True
2219 #if self.getType() == "conference":
2220 # return True
2221 #return self._enableSessionSlots
2222 return True
2224 def getEnableSessions(self):
2225 try :
2226 if self._enableSessions :
2227 pass
2228 except AttributeError :
2229 self._enableSessions = True
2230 if self.getType() == "conference":
2231 return True
2232 return self._enableSessions
2234 def enableSessionSlots(self):
2235 self._enableSessionSlots = True
2237 def disableSessionSlots(self):
2238 self._enableSessionSlots = False
2240 def enableSessions(self):
2241 self._enableSessions = True
2243 def disableSessions(self):
2244 self._enableSessions = False
2246 def setValues(self, confData):
2248 Sets SOME values of the current conference object from a dictionary
2249 containing the following key-value pairs:
2250 visibility-(str)
2251 title-(str)
2252 description-(str)
2253 supportEmail-(str)
2254 contactInfo-(str)
2255 locationName-(str) => name of the location, if not specified
2256 it will be set to the conference location name.
2257 locationAddress-(str)
2258 roomName-(str) => name of the room, if not specified it will
2259 be set to the conference room name.
2260 Please, note that this method sets SOME values which means that if
2261 needed it can be completed to set more values. Also note that if
2262 the given dictionary doesn't contain all the values, the missing
2263 ones will be set to the default values.
2265 self.setVisibility(confData.get("visibility", "999"))
2266 self.setTitle(confData.get("title", _("NO TITLE ASSIGNED")))
2267 self.setDescription(confData.get("description", ""))
2268 self.getSupportInfo().setEmail(confData.get("supportEmail", ""))
2269 self.setContactInfo(confData.get("contactInfo", ""))
2270 if confData.get("locationName", "").strip() == "":
2271 self.setLocation(None)
2272 else:
2273 #if the location name is defined we must set a new location (or
2274 # modify the existing one) for the conference
2275 loc = self.getLocation()
2276 if not loc:
2277 loc = CustomLocation()
2278 self.setLocation(loc)
2279 loc.setName(confData["locationName"])
2280 loc.setAddress(confData.get("locationAddress", ""))
2281 #same as for the location
2282 if confData.get("roomName", "").strip() == "":
2283 self.setRoom(None)
2284 else:
2285 room = self.getRoom()
2286 if not room:
2287 room = CustomRoom()
2288 self.setRoom(room)
2289 room.setName(confData["roomName"])
2290 self.notifyModification()
2292 def getVisibility ( self ):
2293 try:
2294 return int(self._visibility)
2295 except:
2296 self._visibility = 999
2297 return 999
2299 def getFullVisibility( self ):
2300 return max(0,min(self.getVisibility(), self.getOwnerList()[0].getVisibility()))
2302 def setVisibility( self, visibility=999 ):
2303 self._visibility = int(visibility)
2304 catIdx = indexes.IndexesHolder().getIndex('category')
2305 catIdx.reindexConf(self)
2306 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2307 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2308 catDateIdx.reindexConf(self)
2309 catDateAllIdx.reindexConf(self)
2311 def isClosed( self ):
2312 try:
2313 return self._closed
2314 except:
2315 self._closed = False
2316 return False
2318 def setClosed( self, closed=True ):
2319 self._closed = closed
2321 def indexConf( self ):
2322 # called when event dates change
2323 # see also Category.indexConf()
2325 calIdx = indexes.IndexesHolder().getIndex('calendar')
2326 calIdx.indexConf(self)
2327 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2328 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2329 catDateIdx.indexConf(self)
2330 catDateAllIdx.indexConf(self)
2331 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
2332 nameIdx.index(self)
2334 Catalog.getIdx('categ_conf_sd').index_obj(self)
2336 def unindexConf( self ):
2337 calIdx = indexes.IndexesHolder().getIndex('calendar')
2338 calIdx.unindexConf(self)
2339 catDateIdx = indexes.IndexesHolder().getIndex('categoryDate')
2340 catDateAllIdx = indexes.IndexesHolder().getIndex('categoryDateAll')
2341 catDateIdx.unindexConf(self)
2342 catDateAllIdx.unindexConf(self)
2343 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
2344 nameIdx.unindex(self)
2346 Catalog.getIdx('categ_conf_sd').unindex_obj(self)
2348 def __generateNewContribTypeId( self ):
2349 """Returns a new unique identifier for the current conference sessions
2351 try:
2352 return str(self.___contribTypeGenerator.newCount())
2353 except:
2354 self.___contribTypeGenerator = Counter()
2355 return str(self.___contribTypeGenerator.newCount())
2357 def addContribType(self, ct):
2358 try:
2359 if self._contribTypes:
2360 pass
2361 except:
2362 self._contribTypes = {}
2363 if ct in self._contribTypes.values():
2364 return
2365 id = ct.getId()
2366 if id == "":
2367 id = self.__generateNewContribTypeId()
2368 ct.setId(id)
2369 self._contribTypes[id] = ct
2370 self.notifyModification()
2372 def newContribType(self, name, description):
2373 ct = ContributionType(name, description, self)
2374 self.addContribType(ct)
2375 return ct
2377 def getContribTypeList(self):
2378 try:
2379 return self._contribTypes.values()
2380 except:
2381 self._contribTypes = {}
2382 self.notifyModification()
2383 return self._contribTypes.values()
2385 def getContribTypeById(self, id):
2386 try:
2387 if self._contribTypes:
2388 pass
2389 except:
2390 self._contribTypes = {}
2391 self.notifyModification()
2392 if id in self._contribTypes.keys():
2393 return self._contribTypes[id]
2394 return None
2396 def removeContribType(self, ct):
2397 try:
2398 if self._contribTypes:
2399 pass
2400 except:
2401 self._contribTypes = {}
2402 if not ct in self._contribTypes.values():
2403 return
2404 del self._contribTypes[ct.getId()]
2405 for cont in self.getContributionList():
2406 if cont.getType() == ct:
2407 cont.setType(None)
2408 ct.delete()
2409 self.notifyModification()
2411 def recoverContribType(self, ct):
2412 ct.setConference(self)
2413 self.addContribType(ct)
2414 ct.recover()
2416 def _getRepository( self ):
2417 dbRoot = DBMgr.getInstance().getDBConnection().root()
2418 try:
2419 fr = dbRoot["local_repositories"]["main"]
2420 except KeyError, e:
2421 fr = fileRepository.MaterialLocalRepository()
2422 dbRoot["local_repositories"] = OOBTree()
2423 dbRoot["local_repositories"]["main"] = fr
2424 return fr
2426 def removeResource( self, res ):
2427 pass
2429 def getURL(self):
2430 cid = self.getUrlTag()
2431 if not cid:
2432 cid = self.getId()
2433 return Config.getInstance().getShortEventURL() + cid
2435 def setLogo( self, logoFile ):
2436 logoFile.setOwner( self )
2437 logoFile.setId( "logo" )
2438 logoFile.archive( self._getRepository() )
2439 if self._logo != None:
2440 self._logo.delete()
2441 self._logo = logoFile
2442 self.notifyModification()
2444 def getLogo( self ):
2445 return self._logo
2447 def getLogoURL( self ):
2448 try:
2449 if self._logo == None:
2450 return ""
2451 return self._logo.getURL()
2452 except AttributeError:
2453 self._logo = None
2454 return ""
2456 def removeLogo(self):
2457 if self._logo is None:
2458 return
2459 self._logo.delete()
2460 self._logo = None
2461 self.notifyModification()
2463 def recoverLogo(self, logo):
2464 logo.setOwner(self)
2465 if self._logo != None:
2466 self._logo.delete()
2467 self._logo = logo
2468 logo.recover()
2469 self.notifyModification()
2471 def getSession(self):
2472 return None
2474 def getContribution(self):
2475 return None
2477 def getSubContribution(self):
2478 return None
2480 def getAbstractMgr(self):
2481 return self.abstractMgr
2483 def notifyModification( self, date = None, raiseEvent = True):
2484 """Method called to notify the current conference has been modified.
2486 self.setModificationDate()
2488 if raiseEvent:
2489 signals.event.data_changed.send(self, attr=None, old=None, new=None)
2491 self.cleanCache()
2492 self._p_changed=1
2494 def getModificationDate( self ):
2495 """Returns the date in which the conference was last modified"""
2496 return self._modificationDS
2498 def getAdjustedModificationDate( self, tz ):
2499 """Returns the date in which the conference was last modified"""
2500 return self._modificationDS.astimezone(timezone(tz))
2502 def getCreationDate( self ):
2503 """Returns the date in which the conference was created"""
2504 return self._creationDS
2506 def getAdjustedCreationDate( self, tz ):
2507 """Returns the date in which the conference was created"""
2508 return self._creationDS.astimezone(timezone(tz))
2510 def getCreator( self ):
2511 return self.__creator
2513 def setCreator( self, creator):
2514 if self.__creator:
2515 self.__creator.unlinkTo(self, "creator")
2516 creator.linkTo(self, "creator")
2517 self.__creator = creator
2519 def linkCreator(self):
2520 self.__creator.linkTo(self, "creator")
2522 def getId( self ):
2523 """returns (string) the unique identifier of the conference"""
2524 return self.id
2526 def getUniqueId( self ):
2527 """returns (string) the unique identiffier of the item"""
2528 """used mainly in the web session access key table"""
2529 return "a%s" % self.id
2531 def setId(self, newId):
2532 """changes the current unique identifier of the conference to the
2533 one which is specified"""
2534 self.id = str(newId)
2536 def getLocator( self ):
2537 """Gives back (Locator) a globaly unique identification encapsulated in
2538 a Locator object for the conference instance """
2539 d = Locator()
2540 d["confId"] = self.getId()
2541 return d
2543 def getOwner( self ):
2544 if self.getOwnerList() == []:
2545 return None
2546 return self.getOwnerList()[0]
2548 def getOwnerList( self ):
2549 return self.__owners
2551 def getOwnerPath( self ):
2552 l=[]
2553 owner = self.getOwnerList()[0]
2554 while owner != None and owner.getId() != "0":
2555 l.append(owner)
2556 owner = owner.getOwner()
2557 return l
2559 def getOwnerById( self, key ):
2560 """Returns one specific category which contains the conference.
2561 Params:
2562 - key: The "id" of the category.
2564 for owner in self.__owners:
2565 if key == owner.getId():
2566 return owner
2567 return None
2569 def addOwner( self, newOwner ):
2570 if newOwner == None:
2571 return
2572 self.__owners.append( newOwner )
2573 self.notifyModification()
2575 def removeOwner( self, owner, notify=True ):
2576 if not (owner in self.__owners):
2577 return
2578 self.__owners.remove( owner )
2579 owner.removeConference( self )
2580 if notify:
2581 self.notifyModification()
2583 def getCategoriesPath(self):
2584 return [self.getOwnerList()[0].getCategoryPath()]
2586 def notifyContributions(self):
2588 for c in self.getContributionList():
2589 # take care of subcontributions
2590 for sc in c.getSubContributionList():
2591 signals.event.subcontribution_deleted.send(sc, parent=c)
2593 signals.event.contribution_deleted.send(c, parent=self)
2595 def delete(self, user=None):
2596 """deletes the conference from the system.
2598 signals.event.deleted.send(self, user=user)
2600 self.notifyContributions()
2602 # will have to remove it from all the owners (categories) and the
2603 # conference registry
2604 ConferenceHolder().remove(self)
2605 for owner in self.__owners:
2606 owner.removeConference(self, notify=False)
2608 self.removeAllEvaluations()
2610 # For each conference we have a list of managers. If we delete the conference but we don't delete
2611 # the link in every manager to the conference then, when the manager goes to his "My profile" he
2612 # will see a link to a conference that doesn't exist. Therefore, we need to delete that link as well
2613 for manager in self.getManagerList():
2614 if isinstance(manager, AvatarUserWrapper):
2615 manager.unlinkTo(self, "manager")
2617 creator = self.getCreator()
2618 creator.unlinkTo(self, "creator")
2620 # Remove all links in redis
2621 if redis_write_client:
2622 avatar_links.delete_event(self)
2624 # Remote short URL mappings
2625 ShortURLMapper().remove(self)
2627 TrashCanManager().add(self)
2629 def getConference( self ):
2630 return self
2632 def getObservers(self):
2633 if not hasattr(self, "_observers"):
2634 self._observers = []
2635 return self._observers
2637 def setDates( self, sDate, eDate=None, check=1, moveEntries=0):
2639 Set the start/end date for a conference
2642 oldStartDate = self.getStartDate()
2643 oldEndDate = self.getEndDate()
2645 # do some checks first
2646 if sDate > eDate:
2647 # obvious case
2648 raise FormValuesError(_("Start date cannot be after the end date"), _("Event"))
2650 elif sDate == oldStartDate and eDate == oldEndDate:
2651 # if there's nothing to do (yet another obvious case)
2652 return
2654 # if we reached this point, it means either the start or
2655 # the end date (or both) changed
2656 # If only the end date was changed, moveEntries = 0
2657 if sDate == oldStartDate:
2658 moveEntries = 0
2660 # Pre-check for moveEntries
2661 if moveEntries == 1:
2662 # in case the entries are to be simply shifted
2663 # we should make sure the interval is big enough
2664 # just store the old values for later
2666 oldInterval = oldEndDate - oldStartDate
2667 newInterval = eDate - sDate
2669 entries = self.getSchedule().getEntries()
2670 if oldInterval > newInterval and entries:
2671 eventInterval = entries[-1].getEndDate() - entries[0].getStartDate()
2672 diff = entries[0].getStartDate() - oldStartDate
2673 if sDate + diff + eventInterval > eDate:
2674 raise TimingError(
2675 _("The start/end dates were not changed since the selected "
2676 "timespan is not large enough to accomodate the contained "
2677 "timetable entries and spacings."),
2678 explanation=_("You should try using a larger timespan."))
2680 # so, we really need to try changing something
2682 self.unindexConf()
2684 # set the dates
2685 self.setStartDate(sDate, check=0, moveEntries = moveEntries, index=False, notifyObservers = False)
2686 self.setEndDate(eDate, check=0, index=False, notifyObservers = False)
2688 # sanity check
2689 self._checkInnerSchedule()
2691 # reindex the conference
2692 self.indexConf()
2694 # notify observers
2695 old_data = (oldStartDate, oldEndDate)
2696 new_data = (self.getStartDate(), self.getEndDate())
2697 if old_data != new_data:
2698 signals.event.data_changed.send(self, attr='dates', old=old_data, new=new_data)
2700 def _checkInnerSchedule( self ):
2701 self.getSchedule().checkSanity()
2703 def setStartDate(self, sDate, check = 1, moveEntries = 0, index = True, notifyObservers = True):
2704 """ Changes the current conference starting date/time to the one specified by the parameters.
2706 if not sDate.tzname():
2707 raise MaKaCError("date should be timezone aware")
2708 if sDate == self.getStartDate():
2709 return
2710 ###################################
2711 # Fermi timezone awareness #
2712 ###################################
2713 if not indexes.BTREE_MIN_UTC_DATE <= sDate <= indexes.BTREE_MAX_UTC_DATE:
2714 raise FormValuesError(_("The start date must be between {} and {}.").format(
2715 format_datetime(indexes.BTREE_MIN_UTC_DATE),
2716 format_datetime(indexes.BTREE_MAX_UTC_DATE)))
2717 ###################################
2718 # Fermi timezone awareness #
2719 ###################################
2720 if check != 0:
2721 self.verifyStartDate(sDate)
2722 oldSdate = self.getStartDate()
2723 diff = sDate - oldSdate
2725 if index:
2726 self.unindexConf()
2727 self.startDate = sDate
2728 if moveEntries and diff is not None:
2729 # If the start date changed, we move entries inside the timetable
2730 self.getSchedule()._startDate=None
2731 self.getSchedule()._endDate=None
2732 #if oldSdate.date() != sDate.date():
2733 # entries = self.getSchedule().getEntries()[:]
2734 #else:
2735 # entries = self.getSchedule().getEntriesOnDay(sDate.astimezone(timezone(self.getTimezone())))[:]
2736 entries = self.getSchedule().getEntries()[:]
2737 self.getSchedule().moveEntriesBelow(diff, entries, check=check)
2738 #datetime object is non-mutable so we must "force" the modification
2739 # otherwise ZODB won't be able to notice the change
2740 self.notifyModification()
2741 if index:
2742 self.indexConf()
2744 # Update redis link timestamp
2745 if redis_write_client:
2746 avatar_links.update_event_time(self)
2748 #if everything went well, we notify the observers that the start date has changed
2749 if notifyObservers:
2750 if oldSdate != sDate:
2751 signals.event.data_changed.send(self, attr='start_date', old=oldSdate, new=sDate)
2753 def verifyStartDate(self, sdate, check=1):
2754 if sdate>self.getEndDate():
2755 raise MaKaCError( _("End date cannot be before the Start date"), _("Event"))
2757 def setStartTime(self, hours=0, minutes=0, notifyObservers = True):
2758 """ Changes the current conference starting time (not date) to the one specified by the parameters.
2761 sdate = self.getStartDate()
2762 self.startDate = datetime( sdate.year, sdate.month, sdate.day,
2763 int(hours), int(minutes) )
2764 self.verifyStartDate(self.startDate)
2765 self.notifyModification()
2767 def getStartDate(self):
2768 """returns (datetime) the starting date of the conference"""
2769 return self.startDate
2771 def getUnixStartDate(self):
2772 return datetimeToUnixTimeInt(self.startDate)
2774 ###################################
2775 # Fermi timezone awareness #
2776 ###################################
2778 def getAdjustedStartDate(self,tz=None):
2779 if not tz:
2780 tz = self.getTimezone()
2781 if tz not in all_timezones:
2782 tz = 'UTC'
2783 return self.getStartDate().astimezone(timezone(tz))
2785 ###################################
2786 # Fermi timezone awareness(end) #
2787 ###################################
2789 def setScreenStartDate(self, date):
2790 if date == self.getStartDate():
2791 date = None
2792 self._screenStartDate = date
2793 self.notifyModification()
2795 def getScreenStartDate(self):
2796 try:
2797 date = self._screenStartDate
2798 except:
2799 date = self._screenStartDate = None
2800 if date != None:
2801 return date
2802 else:
2803 return self.getStartDate()
2805 def getAdjustedScreenStartDate(self, tz=None):
2806 if not tz:
2807 tz = self.getTimezone()
2808 return self.getScreenStartDate().astimezone(timezone(tz))
2810 def calculateDayStartTime(self, day):
2811 """returns (date) the start date of the conference on a given day
2812 day is a tz aware datetime"""
2813 if self.getStartDate().astimezone(day.tzinfo).date() == day.date():
2814 return self.getStartDate().astimezone(day.tzinfo)
2815 return self.getSchedule().calculateDayStartDate(day)
2817 def verifyEndDate(self, edate):
2818 if edate<self.getStartDate():
2819 raise TimingError( _("End date cannot be before the start date"), _("Event"))
2820 if self.getSchedule().hasEntriesAfter(edate):
2821 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"))
2823 def setEndDate(self, eDate, check = 1, index = True, notifyObservers = True):
2824 """ Changes the current conference end date/time to the one specified by the parameters.
2826 if not eDate.tzname():
2827 raise MaKaCError("date should be timezone aware")
2828 if eDate == self.getEndDate():
2829 return
2830 if not indexes.BTREE_MIN_UTC_DATE <= eDate <= indexes.BTREE_MAX_UTC_DATE:
2831 raise FormValuesError(_("The end date must be between {} and {}.").format(
2832 format_datetime(indexes.BTREE_MIN_UTC_DATE),
2833 format_datetime(indexes.BTREE_MAX_UTC_DATE)))
2834 if check != 0:
2835 self.verifyEndDate(eDate)
2836 if index:
2837 self.unindexConf()
2839 oldEdate = self.endDate
2840 self.endDate = eDate
2841 #datetime object is non-mutable so we must "force" the modification
2842 # otherwise ZODB won't be able to notice the change
2843 self.notifyModification()
2844 if index:
2845 self.indexConf()
2847 #if everything went well, we notify the observers that the start date has changed
2848 if notifyObservers:
2849 if oldEdate != eDate:
2850 signals.event.data_changed.send(self, attr='end_date', old=oldEdate, new=eDate)
2852 def setEndTime(self, hours = 0, minutes = 0, notifyObservers = True):
2853 """ Changes the current conference end time (not date) to the one specified by the parameters.
2855 edate = self.getEndDate()
2856 self.endDate = datetime( edate.year, edate.month, edate.day, int(hours), int(minutes) )
2857 self.verifyEndDate(self.endDate)
2858 self.notifyModification()
2861 def getEndDate(self):
2862 """returns (datetime) the ending date of the conference"""
2863 return self.endDate
2865 ##################################
2866 # Fermi timezone awareness #
2867 ##################################
2869 def getAdjustedEndDate(self,tz=None):
2870 if not tz:
2871 tz = self.getTimezone()
2872 if tz not in all_timezones:
2873 tz = 'UTC'
2874 return self.getEndDate().astimezone(timezone(tz))
2876 ##################################
2877 # Fermi timezone awareness(end) #
2878 ##################################
2880 def setScreenEndDate(self, date):
2881 if date == self.getEndDate():
2882 date = None
2883 self._screenEndDate = date
2884 self.notifyModification()
2886 def getScreenEndDate(self):
2887 try:
2888 date = self._screenEndDate
2889 except:
2890 date = self._screenEndDate = None
2891 if date != None:
2892 return date
2893 else:
2894 return self.getEndDate()
2896 def getAdjustedScreenEndDate(self, tz=None):
2897 if not tz:
2898 tz = self.getTimezone()
2899 return self.getScreenEndDate().astimezone(timezone(tz))
2901 def isEndDateAutoCal( self ):
2902 """Says whether the end date has been explicitely set for the session
2903 or it must be calculated automatically
2905 return self._endDateAutoCal
2907 ####################################
2908 # Fermi timezone awareness #
2909 ####################################
2910 def setTimezone(self, tz):
2911 try:
2912 oldTimezone = self.timezone
2913 except AttributeError:
2914 oldTimezone = tz
2915 self.timezone = tz
2917 def getTimezone(self):
2918 try:
2919 return self.timezone
2920 except:
2921 return 'UTC'
2923 def moveToTimezone(self, tz):
2924 if self.getTimezone() == tz:
2925 return
2926 sd=self.getAdjustedStartDate()
2927 ed=self.getAdjustedEndDate()
2928 self.setTimezone(tz)
2929 try:
2930 sDate = timezone(tz).localize(datetime(sd.year, \
2931 sd.month, \
2932 sd.day, \
2933 sd.hour, \
2934 sd.minute))
2935 eDate = timezone(tz).localize(datetime(ed.year, \
2936 ed.month, \
2937 ed.day, \
2938 ed.hour, \
2939 ed.minute))
2940 except ValueError,e:
2941 raise MaKaCError("Error moving the timezone: %s"%e)
2942 self.setDates( sDate.astimezone(timezone('UTC')), \
2943 eDate.astimezone(timezone('UTC')),
2944 moveEntries=1)
2948 ####################################
2949 # Fermi timezone awareness(end) #
2950 ####################################
2952 def getTitle(self):
2953 """returns (String) the title of the conference"""
2954 return self.title
2956 def setTitle(self, title):
2957 """changes the current title of the conference to the one specified"""
2958 oldTitle = self.title
2960 self.title = title
2961 self.notifyModification()
2963 nameIdx = indexes.IndexesHolder().getIndex('conferenceTitle')
2964 nameIdx.unindex(self)
2965 nameIdx.index(self)
2967 if oldTitle != title:
2968 signals.event.data_changed.send(self, attr='title', old=oldTitle, new=title)
2970 def getDescription(self):
2971 """returns (String) the description of the conference"""
2972 return self.description
2974 def setDescription(self, desc):
2975 """changes the current description of the conference"""
2976 oldDescription = self.description
2977 self.description = desc
2978 if oldDescription != desc:
2979 signals.event.data_changed.send(self, attr='description', old=oldDescription, new=desc)
2980 self.notifyModification()
2982 def getSupportInfo(self):
2983 if not hasattr(self, "_supportInfo"):
2984 self._supportInfo = SupportInfo(self, "Support")
2985 return self._supportInfo
2987 def setSupportInfo(self, supportInfo):
2988 self._supportInfo = supportInfo
2990 def getChairmanText( self ):
2991 try:
2992 if self.chairmanText:
2993 pass
2994 except AttributeError, e:
2995 self.chairmanText = ""
2996 return self.chairmanText
2998 def setChairmanText( self, newText ):
2999 self.chairmanText = newText.strip()
3001 def appendChairmanText( self, newText ):
3002 self.setChairmanText( "%s, %s"%(self.getChairmanText(), newText.strip()) )
3003 self._chairGen=Counter()
3004 self._chairs=[]
3006 def _resetChairs(self):
3007 try:
3008 if self._chairs:
3009 return
3010 except AttributeError:
3011 self._chairs=[]
3012 for oc in self.chairmans:
3013 newChair=ConferenceChair()
3014 newChair.setDataFromAvatar(oc)
3015 self._addChair(newChair)
3017 def getChairList(self):
3018 """Method returning a list of the conference chairmans (Avatars)
3020 self._resetChairs()
3021 return self._chairs
3023 def _addChair(self,newChair):
3024 for chair in self._chairs:
3025 if newChair.getEmail() != "" and newChair.getEmail() == chair.getEmail():
3026 return
3027 try:
3028 if self._chairGen:
3029 pass
3030 except AttributeError:
3031 self._chairGen=Counter()
3032 id = newChair.getId()
3033 if id == "":
3034 id=int(self._chairGen.newCount())
3035 if isinstance(newChair,ConferenceChair):
3036 newChair.includeInConference(self,id)
3037 self._chairs.append(newChair)
3038 if isinstance(newChair, AvatarUserWrapper):
3039 newChair.linkTo(self, "chair")
3040 self.notifyModification()
3042 def addChair(self,newChair):
3043 """includes the specified user in the list of conference
3044 chairs"""
3045 self._resetChairs()
3046 self._addChair(newChair)
3048 def removeChair(self,chair):
3049 """removes the specified user from the list of conference
3050 chairs"""
3051 self._resetChairs()
3052 if chair not in self._chairs:
3053 return
3054 self._chairs.remove(chair)
3055 if isinstance(chair, AvatarUserWrapper):
3056 chair.unlinkTo(self, "chair")
3057 chair.delete()
3058 self.notifyModification()
3060 def recoverChair(self, ch):
3061 self.addChair(ch)
3062 ch.recover()
3064 def getChairById(self,id):
3065 id=int(id)
3066 for chair in self._chairs:
3067 if chair.getId()==id:
3068 return chair
3069 return None
3071 def getAllSessionsConvenerList(self) :
3072 dictionary = {}
3073 for session in self.getSessionList() :
3074 for convener in session.getConvenerList() :
3075 key = convener.getEmail()+" "+convener.getFirstName().lower()+" "+convener.getFamilyName().lower()
3076 dictionary.setdefault(key, set()).add(convener)
3077 for slot in session.getSlotList():
3078 for convener in slot.getConvenerList() :
3079 key = convener.getEmail()+" "+convener.getFirstName().lower()+" "+convener.getFamilyName().lower()
3080 dictionary.setdefault(key, set()).add(convener)
3082 return dictionary
3084 def getContactInfo(self):
3085 return self.contactInfo
3087 def setContactInfo(self, contactInfo):
3088 self.contactInfo = contactInfo
3089 self.notifyModification()
3091 def getLocationParent( self ):
3093 Returns the object from which the room/location
3094 information should be inherited.
3095 For Conferences, it's None, since they can't inherit
3096 from anywhere else.
3098 return None
3100 def getLocation( self ):
3101 return self.getOwnLocation()
3103 def getAddress( self ):
3104 if self.getOwnLocation():
3105 return self.getOwnLocation().getAddress()
3106 else:
3107 return None
3109 def getRoom( self ):
3110 return self.getOwnRoom()
3112 def getLocationList(self):
3113 """Method returning a list of "location" objects which contain the
3114 information about the different places the conference is gonna
3115 happen
3117 return self.places
3119 def getFavoriteRooms(self):
3120 roomList = []
3121 roomList.extend(self.getRoomList())
3122 #roomList.extend(map(lambda x: x._getName(), self.getBookedRooms()))
3124 return roomList
3126 def addLocation(self, newPlace):
3127 self.places.append( newPlace )
3128 self.notifyModification()
3130 def setAccessKey(self, accessKey=""):
3131 """sets the access key of the conference"""
3132 self._accessKey = accessKey
3133 self.notifyModification()
3135 def getAccessKey(self):
3136 try:
3137 return self._accessKey
3138 except AttributeError:
3139 self._accessKey = ""
3140 return self._accessKey
3142 def setModifKey(self, modifKey=""):
3143 """sets the modification key of the conference"""
3144 self._modifKey = modifKey
3145 self.notifyModification()
3147 def getModifKey(self):
3148 try:
3149 return self._modifKey
3150 except AttributeError:
3151 self._modifKey = ""
3152 return self._modifKey
3154 def __generateNewSessionId( self ):
3155 """Returns a new unique identifier for the current conference sessions
3157 return str(self.__sessionGenerator.newCount())
3159 def addSession(self, new_session, check=2, session_id=None):
3160 """Adds a new session object to the conference taking care of assigning
3161 a new unique id to it
3163 """check parameter:
3164 0: no check at all
3165 1: check and raise error in case of problem
3166 2: check and adapt the owner dates"""
3168 if self.hasSession(new_session):
3169 return
3170 if self.getSchedule().isOutside(new_session):
3171 if check == 1:
3172 raise MaKaCError(_("Cannot add this session (Start:%s - End:%s) "
3173 "Outside of the event's time table(Start:%s - End:%s)").format(
3174 new_session.getStartDate(),
3175 new_session.getEndDate(),
3176 self.getSchedule().getStartDate(),
3177 self.getSchedule().getEndDate()),
3178 "Event")
3179 elif check == 2:
3180 if self.getSchedule().getStartDate() > new_session.getStartDate():
3181 self.setStartDate(new_session.getStartDate())
3182 if self.getSchedule().getEndDate() < new_session.getEndDate():
3183 self.setEndDate(new_session.getEndDate())
3184 if session_id is not None:
3185 session_id = session_id
3186 # Keep ID counter up to date
3187 self.__sessionGenerator.sync(session_id)
3188 else:
3189 session_id = self.__generateNewSessionId()
3190 self.sessions[session_id] = new_session
3191 new_session.includeInConference(self, session_id)
3192 # keep the session coordinator index updated
3193 for sc in new_session.getCoordinatorList():
3194 self.addSessionCoordinator(new_session, sc)
3195 self.notifyModification()
3197 def hasSession(self,session):
3198 if session != None and session.getConference()==self and \
3199 self.sessions.has_key(session.getId()):
3200 return True
3201 return False
3203 def removeSession(self,session, deleteContributions=False):
3204 if self.hasSession(session):
3205 for sc in session.getCoordinatorList():
3206 self.removeSessionCoordinator(session,sc)
3208 if deleteContributions:
3209 for contrib in session.getContributionList():
3210 contrib.delete()
3212 del self.sessions[session.getId()]
3214 session.delete()
3215 self.notifyModification()
3217 def recoverSession(self, session, check, isCancelled):
3218 self.addSession(session, check, session.getId())
3219 session.recover(isCancelled)
3221 def getSessionById( self, sessionId ):
3222 """Returns the session from the conference list corresponding to the
3223 unique session id specified
3225 return self.sessions.get(sessionId,None)
3227 def getRoomList(self):
3228 roomList =[]
3229 for session in self.sessions.values():
3230 if session.getRoom()!=None:
3231 roomname = session.getRoom().getName()
3232 if roomname not in roomList:
3233 roomList.append(roomname)
3234 return roomList
3236 def getSessionList( self ):
3237 """Retruns a list of the conference session objects
3239 return self.sessions.values()
3241 def getSessionListSorted( self ):
3242 """Retruns a sorted list of the conference sessions
3244 res=[]
3245 for entry in self.getSchedule().getEntries():
3246 if isinstance(entry,LinkedTimeSchEntry) and \
3247 isinstance(entry.getOwner(),SessionSlot):
3248 session=entry.getOwner().getSession()
3249 if session not in res:
3250 res.append(session)
3251 return res
3253 def getSessionSlotList(self):
3254 return [slot for session in self.sessions.values() for slot in session.getSlotList()]
3256 def getNumberOfSessions(self):
3257 return len(self.sessions)
3259 def _generateNewContributionId(self):
3260 """Returns a new unique identifier for the current conference
3261 contributions
3263 return str(self.__contribGenerator.newCount())
3265 def genNewAbstractId(self):
3266 return str(self.__contribGenerator.newCount())
3268 def syncContribCounter(self):
3269 self.__contribGenerator.sync(self.getAbstractMgr()._getOldAbstractCounter())
3270 return self.__contribGenerator._getCount()
3272 def addContribution(self, newContrib, contrib_id=None):
3273 """Adds a new contribution object to the conference taking care of
3274 assigning a new unique id to it
3276 if self.hasContribution(newContrib):
3277 return
3278 if isinstance(newContrib.getCurrentStatus(),ContribStatusWithdrawn):
3279 raise MaKaCError( _("Cannot add a contribution which has been withdrawn"), _("Event"))
3280 if contrib_id is None or contrib_id == '':
3281 contribId=self._generateNewContributionId()
3282 while self.contributions.has_key(contribId):
3283 contribId=self._generateNewContributionId()
3284 else:
3285 contribId = str(contrib_id)
3286 self.__contribGenerator.sync(contribId)
3287 if self.contributions.has_key(contribId):
3288 raise MaKaCError( _("Cannot add this contribution id:(%s) as it has already been used")%contribId, _("Event"))
3289 newContrib.includeInConference(self,contribId)
3290 self.contributions[contribId]=newContrib
3291 for auth in newContrib.getAuthorList():
3292 self.indexAuthor(auth)
3293 for spk in newContrib.getSpeakerList():
3294 self.indexSpeaker(spk)
3295 for sub in newContrib.getSubmitterList():
3296 self.addContribSubmitter(newContrib,sub)
3298 signals.event.contribution_created.send(newContrib, parent=self)
3299 self.notifyModification()
3301 def hasContribution(self,contrib):
3302 return contrib.getConference()==self and \
3303 self.contributions.has_key(contrib.getId())
3305 def removeContribution( self, contrib, callDelete=True ):
3306 if not self.contributions.has_key( contrib.getId() ):
3307 return
3308 for sub in contrib.getSubmitterList()[:]:
3309 self.removeContribSubmitter(contrib,sub)
3310 for auth in contrib.getPrimaryAuthorList()[:]:
3311 contrib.removePrimaryAuthor(auth)
3312 for auth in contrib.getCoAuthorList()[:]:
3313 contrib.removeCoAuthor(auth)
3314 for spk in contrib.getSpeakerList()[:]:
3315 contrib.removeSpeaker(spk)
3316 del self.contributions[ contrib.getId() ]
3317 if callDelete:
3318 contrib.delete()
3319 #else:
3320 # contrib.unindex()
3321 self.notifyModification()
3323 def recoverContribution(self, contrib):
3324 self.addContribution(contrib, contrib.getId())
3325 contrib.recover()
3327 # Note: this kind of factories should never be used as they only allow to
3328 # create a single type of contributions
3329 def newContribution( self, id=None ):
3330 """Creates and returns a new contribution object already added to the
3331 conference list (all its data is set to the default)
3333 c = Contribution()
3334 self.addContribution( c, id )
3335 return c
3337 def getOwnContributionById( self, id ):
3338 """Returns the contribution from the conference list corresponding to
3339 the unique contribution id specified
3341 if self.contributions.has_key( id ):
3342 return self.contributions[ id ]
3343 return None
3345 def getContributionById( self, id ):
3346 """Returns the contribution corresponding to the id specified
3348 return self.contributions.get(str(id).strip(),None)
3350 def getContributionList(self):
3351 """Returns a list of the conference contribution objects
3353 return self.contributions.values()
3355 def iterContributions(self):
3356 return self.contributions.itervalues()
3358 def getContributionListWithoutSessions(self):
3359 """Returns a list of the conference contribution objects which do not have a session
3361 return [c for c in self.contributions.values() if not c.getSession()]
3364 def getContributionListSorted(self, includeWithdrawn=True, key="id"):
3365 """Returns a list of the conference contribution objects, sorted by key provided
3367 contributions = self.contributions.values()
3368 if not includeWithdrawn:
3369 contributions = filter(lambda c: not isinstance(c.getCurrentStatus(), ContribStatusWithdrawn), contributions)
3370 contributions.sort(key = lambda c: getattr(c, key))
3371 return contributions
3373 def getNumberOfContributions(self, only_scheduled=False):
3374 if only_scheduled:
3375 return len(filter(lambda c: c.isScheduled(), self.contributions.itervalues()))
3376 else:
3377 return len(self.contributions)
3379 def hasSomethingOnWeekend(self, day):
3380 """Checks if the event has a session or contribution on the weekend indicated by `day`.
3382 `day` must be either a saturday or a sunday"""
3383 if day.weekday() == 5:
3384 weekend = (day, day + timedelta(days=1))
3385 elif day.weekday() == 6:
3386 weekend = (day, day - timedelta(days=1))
3387 else:
3388 raise ValueError('day must be on a weekend')
3389 return (any(c.startDate.date() in weekend and not isinstance(c.getCurrentStatus(), ContribStatusWithdrawn)
3390 for c in self.contributions.itervalues() if c.startDate is not None) or
3391 any(s.startDate.date() in weekend for s in self.sessions.itervalues() if s.startDate is not None))
3393 def getProgramDescription(self):
3394 try:
3395 return self.programDescription
3396 except:
3397 self.programDescription = ""
3398 return self.programDescription
3400 def setProgramDescription(self, txt):
3401 self.programDescription = txt
3403 def _generateNewTrackId( self ):
3406 return str(self.__programGenerator.newCount())
3408 def addTrack( self, newTrack ):
3411 #XXX: The conference program shoul be isolated in a separated object
3412 if newTrack in self.program:
3413 return
3415 trackId = newTrack.getId()
3416 if trackId == "not assigned":
3417 trackId = self._generateNewTrackId()
3418 self.program.append( newTrack )
3419 newTrack.setConference( self )
3420 newTrack.setId( trackId )
3421 self.notifyModification()
3423 def removeTrack( self, track ):
3424 if track in self.program:
3425 track.delete()
3426 if track in self.program:
3427 self.program.remove( track )
3428 self.notifyModification()
3430 def recoverTrack(self, track):
3431 self.addTrack(track)
3432 track.recover()
3434 def newTrack( self ):
3437 t = Track()
3438 self.addTrack( t )
3439 return t
3441 def getTrackById( self, id ):
3444 for track in self.program:
3445 if track.getId() == id.strip():
3446 return track
3447 return None
3449 def getTrackList( self ):
3452 return self.program
3454 def isLastTrack(self,track):
3457 return self.getTrackPos(track)==(len(self.program)-1)
3459 def isFirstTrack(self,track):
3462 return self.getTrackPos(track)==0
3464 def getTrackPos(self,track):
3467 return self.program.index(track)
3469 def moveTrack(self,track,newPos):
3472 self.program.remove(track)
3473 self.program.insert(newPos,track)
3474 self.notifyModification()
3476 def moveUpTrack(self,track):
3479 if self.isFirstTrack(track):
3480 return
3481 newPos=self.getTrackPos(track)-1
3482 self.moveTrack(track,newPos)
3484 def moveDownTrack(self,track):
3487 if self.isLastTrack(track):
3488 return
3489 newPos = self.getTrackPos(track) + 1
3490 self.moveTrack(track, newPos)
3492 def _cmpTracks(self, t1, t2):
3493 o1 = self.program.index(t1)
3494 o2 = self.program.index(t2)
3495 return cmp(o1, o2)
3497 def sortTrackList(self, l):
3498 """Sorts out a list of tracks according to the current programme order.
3500 if len(l) == 0:
3501 return []
3502 elif len(l) == 1:
3503 return [l[0]]
3504 else:
3505 res = []
3506 for i in l:
3507 res.append(i)
3508 res.sort(self._cmpTracks)
3509 return res
3511 def requireDomain(self, dom):
3512 self.__ac.requireDomain(dom)
3513 signals.event.domain_access_granted.send(self, domain=dom)
3515 def freeDomain(self, dom):
3516 self.__ac.freeDomain(dom)
3517 signals.event.domain_access_revoked.send(self, domain=dom)
3519 def getDomainList(self):
3520 return self.__ac.getRequiredDomainList()
3522 def isProtected(self):
3523 """Tells whether a conference is protected for accessing or not
3525 return self.__ac.isProtected()
3527 def getAccessProtectionLevel( self ):
3528 return self.__ac.getAccessProtectionLevel()
3530 def isItselfProtected( self ):
3531 return self.__ac.isItselfProtected()
3533 def hasAnyProtection( self ):
3534 """Tells whether a conference has any kind of protection over it:
3535 access or domain protection.
3537 if self.isProtected():
3538 return True
3539 if self.getDomainList():
3540 return True
3542 if self.getAccessProtectionLevel() == -1:
3543 return False
3545 for owner in self.getOwnerList():
3546 if owner.hasAnyProtection():
3547 return True
3549 return False
3551 def hasProtectedOwner( self ):
3552 return self.__ac._getFatherProtection()
3554 def setProtection( self, private ):
3556 Allows to change the conference access protection
3559 oldValue = 1 if self.isProtected() else -1
3561 self.getAccessController().setProtection( private )
3563 if oldValue != private:
3564 # notify listeners
3565 signals.event.protection_changed.send(self, old=oldValue, new=private)
3567 def grantAccess( self, prin ):
3568 self.__ac.grantAccess( prin )
3569 if isinstance(prin, AvatarUserWrapper):
3570 prin.linkTo(self, "access")
3572 def revokeAccess( self, prin ):
3573 self.__ac.revokeAccess( prin )
3574 if isinstance(prin, AvatarUserWrapper):
3575 prin.unlinkTo(self, "access")
3577 def canView( self, aw ):
3578 """tells whether the specified access wrappers has access to the current
3579 object or any of its parts"""
3580 if self.canAccess( aw ):
3581 return True
3582 for session in self.getSessionList():
3583 if session.canView( aw ):
3584 return True
3585 for contrib in self.getContributionList():
3586 if contrib.canView( aw ):
3587 return True
3588 return False
3590 def isAllowedToAccess( self, av):
3591 """tells if a user has privileges to access the current conference
3592 (independently that it is protected or not)
3594 if not av:
3595 return False
3596 if (av in self.getChairList()) or (self.__ac.canUserAccess( av )) or (self.canUserModify( av )):
3597 return True
3599 # if the conference is not protected by itself
3600 if not self.isItselfProtected():
3601 # then inherit behavior from parent category
3602 for owner in self.getOwnerList():
3603 if owner.isAllowedToAccess( av ):
3604 return True
3606 # track coordinators are also allowed to access the conference
3607 for track in self.getTrackList():
3608 if track.isCoordinator( av ):
3609 return True
3611 # paper reviewing team should be also allowed to access
3612 if self.getConfPaperReview().isInReviewingTeam(av):
3613 return True
3615 return False
3617 def canAccess( self, aw ):
3618 """Tells whether an access wrapper is allowed to access the current
3619 conference: when the conference is protected, only if the user is a
3620 chair or is granted to access the conference, when the client ip is
3621 not restricted.
3624 # Allow harvesters (Invenio, offline cache) to access
3625 # protected pages
3626 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
3627 return True
3629 # Managers have always access
3630 if self.canModify(aw):
3631 return True
3633 if self.isProtected():
3634 if self.isAllowedToAccess( aw.getUser() ):
3635 return True
3636 else:
3637 return self.canKeyAccess(aw)
3638 else:
3639 # Domain control is triggered just for PUBLIC events
3640 return self.canIPAccess(request.remote_addr)
3642 def canKeyAccess(self, aw, key=None):
3643 accessKey = self.getAccessKey()
3644 if not accessKey:
3645 return False
3646 return key == accessKey or session.get('accessKeys', {}).get(self.getUniqueId()) == accessKey
3648 def canKeyModify(self):
3649 modifKey = self.getModifKey()
3650 if not modifKey:
3651 return False
3652 return session.get('modifKeys', {}).get(self.id) == modifKey
3654 def grantModification( self, prin, sendEmail=True ):
3655 email = None
3656 if isinstance(prin, ConferenceChair):
3657 email = prin.getEmail()
3658 elif isinstance(prin, str):
3659 email = prin
3660 if email != None:
3661 if email == "":
3662 return
3663 ah = AvatarHolder()
3664 results=ah.match({"email":email}, exact=1)
3665 #No registered user in Indico with that email
3666 if len(results) == 0:
3667 self.__ac.grantModificationEmail(email)
3668 self.getConference().getPendingQueuesMgr().addPendingConfManager(prin, False)
3669 if sendEmail and isinstance(prin, ConferenceChair):
3670 notif = pendingQueues._PendingConfManagerNotification( [prin] )
3671 mail.GenericMailer.sendAndLog(notif, self.getConference(), 'Event')
3672 #The user is registered in Indico and is activated as well
3673 elif len(results) == 1 and results[0] is not None and results[0].isActivated():
3674 self.__ac.grantModification(results[0])
3675 results[0].linkTo(self, "manager")
3676 else:
3677 self.__ac.grantModification( prin )
3678 if isinstance(prin, AvatarUserWrapper):
3679 prin.linkTo(self, "manager")
3681 def revokeModification( self, prin ):
3682 self.__ac.revokeModification( prin )
3683 if isinstance(prin, AvatarUserWrapper):
3684 prin.unlinkTo(self, "manager")
3686 @unify_user_args(legacy=True)
3687 def canUserModify( self, av ):
3688 if av == None:
3689 return False
3690 if ( av == self.getCreator()) or self.getAccessController().canModify( av ):
3691 return True
3692 for owner in self.getOwnerList():
3693 if owner.canUserModify( av ):
3694 return True
3695 return False
3697 @unify_user_args(legacy=True)
3698 def canModify(self, aw_or_user):
3699 """Tells whether an access wrapper is allowed to modify the current
3700 conference: only if the user is granted to modify the conference and
3701 he is accessing from an IP address which is not restricted.
3703 if hasattr(aw_or_user, 'getUser'):
3704 aw_or_user = aw_or_user.getUser()
3705 return self.canUserModify(aw_or_user) or self.canKeyModify()
3707 def getManagerList( self ):
3708 return self.__ac.getModifierList()
3710 def addToRegistrars(self, av):
3711 self.getRegistrarList().append(av)
3712 self.notifyModification()
3713 if isinstance(av, AvatarUserWrapper):
3714 av.linkTo(self, "registrar")
3716 def removeFromRegistrars(self, av):
3717 self.getRegistrarList().remove(av)
3718 self.notifyModification()
3719 if isinstance(av, AvatarUserWrapper):
3720 av.unlinkTo(self, "registrar")
3722 def isRegistrar(self, av):
3723 if av is None:
3724 return False
3725 try:
3726 return any(principal.containsUser(av) for principal in self.getRegistrarList())
3727 except AttributeError:
3728 return False
3730 def getRegistrarList(self):
3731 try:
3732 return self.__registrars
3733 except AttributeError:
3734 self.__registrars = []
3735 return self.__registrars
3737 def canManageRegistration(self, av):
3738 return self.isRegistrar(av) or self.canUserModify(av)
3740 def getAllowedToAccessList( self ):
3741 return self.__ac.getAccessList()
3743 def addMaterial( self, newMat ):
3744 newMat.setId( str(self.__materialGenerator.newCount()) )
3745 newMat.setOwner( self )
3746 self.materials[ newMat.getId() ] = newMat
3747 self.notifyModification()
3749 def removeMaterial( self, mat ):
3750 if mat.getId() in self.materials.keys():
3751 mat.delete()
3752 self.materials[mat.getId()].setOwner(None)
3753 del self.materials[ mat.getId() ]
3754 self.notifyModification()
3755 elif mat.getId().lower() == 'paper':
3756 self.removePaper()
3757 self.notifyModification()
3758 elif mat.getId().lower() == 'slides':
3759 self.removeSlides()
3760 self.notifyModification()
3761 elif mat.getId().lower() == 'video':
3762 self.removeVideo()
3763 self.notifyModification()
3764 elif mat.getId().lower() == 'poster':
3765 self.removePoster()
3766 self.notifyModification()
3768 def recoverMaterial(self, recMat):
3769 # Id must already be set in recMat.
3770 recMat.setOwner(self)
3771 self.materials[recMat.getId()] = recMat
3772 recMat.recover()
3773 self.notifyModification()
3775 def getMaterialRegistry(self):
3777 Return the correct material registry for this type
3779 from MaKaC.webinterface.materialFactories import ConfMFRegistry
3780 return ConfMFRegistry
3782 def getMaterialById( self, matId ):
3783 if matId.lower() == 'paper':
3784 return self.getPaper()
3785 elif matId.lower() == 'slides':
3786 return self.getSlides()
3787 elif matId.lower() == 'video':
3788 return self.getVideo()
3789 elif matId.lower() == 'poster':
3790 return self.getPoster()
3791 elif self.materials.has_key(matId):
3792 return self.materials[ matId ]
3793 return None
3795 def getMaterialList( self ):
3796 return self.materials.values()
3798 def getAllMaterialList(self, sort=True):
3799 l = self.getMaterialList()
3800 if self.getPaper():
3801 l.append( self.getPaper() )
3802 if self.getSlides():
3803 l.append( self.getSlides() )
3804 if self.getVideo():
3805 l.append( self.getVideo() )
3806 if self.getPoster():
3807 l.append( self.getPoster() )
3808 if sort:
3809 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
3810 return l
3812 def _getMaterialFiles(self, material):
3814 Adaption of _getMaterialFiles in WPTPLConferenceDisplay for desired format, objects
3815 seemed mutually exclusive hence use of similar logic here specific to Conference.
3817 files = []
3818 processed = []
3820 for res in material.getResourceList():
3821 try:
3822 ftype = res.getFileType()
3823 fname = res.getFileName()
3824 furl = urlHandlers.UHFileAccess.getURL(res)
3826 if fname in processed:
3827 fname = "%s - %s" % (fname, processed.count(fname))
3829 processed.append(res.getFileName())
3830 except:
3831 # If we are here then the resource is a Link object.
3832 fname, ftype, furl = str(res.getURL()), "link", str(res.getURL())
3833 fdesc = res.getDescription()
3834 files.append({'title': fname,
3835 'description': fdesc,
3836 'type': ftype,
3837 'url': furl})
3838 return files
3840 def getAllMaterialDict(self, child=None):
3842 This method iterates through the children of the conference, creating
3843 a dictionary which maps type to material link URLs.
3846 child = self if child is None else child
3848 node = {}
3849 node['title'] = child.getTitle()
3851 try:
3852 node['type'] = child.getType()
3853 except:
3854 # If we land here, it's a session which doesn't have 'getType'
3855 node['type'] = 'session'
3857 node['children'] = []
3858 node['material'] = []
3860 if node['type'] in ['conference', 'meeting']:
3861 for session in child.getSessionList():
3862 node['children'].append(self.getAllMaterialDict(session))
3864 for contrib in child.getContributionList():
3865 node['children'].append(self.getAllMaterialDict(contrib))
3867 for material in child.getAllMaterialList():
3868 files = self._getMaterialFiles(material)
3870 for f in files:
3871 materialNode = {}
3872 materialNode['type'] = 'material'
3873 materialNode['title'] = material.getTitle()
3874 materialNode['materialType'] = f['type']
3875 materialNode['url'] = str(f['url'])
3877 node['material'].append(materialNode)
3879 return node
3881 def setPaper( self, newPaper ):
3882 if self.getPaper() != None:
3883 raise MaKaCError( _("The paper for this conference has already been set"), _("Conference"))
3884 self.paper=newPaper
3885 self.paper.setOwner( self )
3886 self.notifyModification()
3888 def removePaper( self ):
3889 if self.paper is None:
3890 return
3891 self.paper.delete()
3892 self.paper.setOwner(None)
3893 self.paper = None
3894 self.notifyModification()
3896 def recoverPaper(self, p):
3897 self.setPaper(p)
3898 p.recover()
3900 def getPaper( self ):
3901 try:
3902 if self.paper:
3903 pass
3904 except AttributeError:
3905 self.paper = None
3906 return self.paper
3908 def setSlides( self, newSlides ):
3909 if self.getSlides() != None:
3910 raise MaKaCError( _("The slides for this conference have already been set"), _("Conference"))
3911 self.slides=newSlides
3912 self.slides.setOwner( self )
3913 self.notifyModification()
3915 def removeSlides( self ):
3916 if self.slides is None:
3917 return
3918 self.slides.delete()
3919 self.slides.setOwner( None )
3920 self.slides= None
3921 self.notifyModification()
3923 def recoverSlides(self, s):
3924 self.setSlides(s)
3925 s.recover()
3927 def getSlides( self ):
3928 try:
3929 if self.slides:
3930 pass
3931 except AttributeError:
3932 self.slides = None
3933 return self.slides
3935 def setVideo( self, newVideo ):
3936 if self.getVideo() != None:
3937 raise MaKaCError( _("The video for this conference has already been set"), _("Conference"))
3938 self.video=newVideo
3939 self.video.setOwner( self )
3940 self.notifyModification()
3942 def removeVideo( self ):
3943 if self.getVideo() is None:
3944 return
3945 self.video.delete()
3946 self.video.setOwner(None)
3947 self.video = None
3948 self.notifyModification()
3950 def recoverVideo(self, v):
3951 self.setVideo(v)
3952 v.recover()
3954 def getVideo( self ):
3955 try:
3956 if self.video:
3957 pass
3958 except AttributeError:
3959 self.video = None
3960 return self.video
3962 def setPoster( self, newPoster ):
3963 if self.getPoster() != None:
3964 raise MaKaCError( _("the poster for this conference has already been set"), _("Conference"))
3965 self.poster=newPoster
3966 self.poster.setOwner( self )
3967 self.notifyModification()
3969 def removePoster( self ):
3970 if self.getPoster() is None:
3971 return
3972 self.poster.delete()
3973 self.poster.setOwner(None)
3974 self.poster = None
3975 self.notifyModification()
3977 def recoverPoster(self, p):
3978 self.setPoster(p)
3979 p.recover()
3981 def getPoster( self ):
3982 try:
3983 if self.poster:
3984 pass
3985 except AttributeError:
3986 self.poster = None
3987 return self.poster
3989 def _setSchedule( self, sch=None ):
3990 self.__schedule=ConferenceSchedule(self)
3991 for session in self.getSessionList():
3992 for slot in session.getSlotList():
3993 self.__schedule.addEntry(slot.getConfSchEntry())
3995 def getSchedule( self ):
3996 try:
3997 if not self.__schedule:
3998 self._setSchedule()
3999 except AttributeError, e:
4000 self._setSchedule()
4001 return self.__schedule
4003 def fit( self ):
4004 sch = self.getSchedule()
4006 sDate = sch.calculateStartDate()
4007 eDate = sch.calculateEndDate()
4008 self.setStartDate(sDate)
4009 self.setEndDate(eDate)
4011 def fitSlotsOnDay( self, day ):
4012 for entry in self.getSchedule().getEntriesOnDay(day) :
4013 if isinstance(entry.getOwner(), SessionSlot) :
4014 entry.getOwner().fit()
4016 def getDisplayMgr(self):
4018 Return the display manager for the conference
4020 from MaKaC.webinterface import displayMgr
4021 return displayMgr.ConfDisplayMgrRegistery().getDisplayMgr(self)
4023 def getDefaultStyle( self ):
4024 return self.getDisplayMgr().getDefaultStyle()
4026 def clone( self, startDate, options, eventManager=None, userPerformingClone = None ):
4027 # startDate must be in the timezone of the event (to avoid problems with daylight-saving times)
4028 cat = self.getOwnerList()[0]
4029 managing = options.get("managing",None)
4030 if managing is not None:
4031 creator = managing
4032 else:
4033 creator = self.getCreator()
4034 conf = cat.newConference(creator)
4035 if managing is not None :
4036 conf.grantModification(managing)
4037 conf.setTitle(self.getTitle())
4038 conf.setDescription(self.getDescription())
4039 conf.setTimezone(self.getTimezone())
4040 for loc in self.getLocationList():
4041 if loc is not None:
4042 conf.addLocation(loc.clone())
4043 if self.getRoom() is not None:
4044 conf.setRoom(self.getRoom().clone())
4045 startDate = timezone(self.getTimezone()).localize(startDate).astimezone(timezone('UTC'))
4046 timeDelta = startDate - self.getStartDate()
4047 endDate = self.getEndDate() + timeDelta
4048 conf.setDates( startDate, endDate, moveEntries=1 )
4049 conf.setContactInfo(self.getContactInfo())
4050 conf.setChairmanText(self.getChairmanText())
4051 conf.setVisibility(self.getVisibility())
4052 conf.setSupportInfo(self.getSupportInfo().clone(self))
4053 conf.setReportNumberHolder(self.getReportNumberHolder().clone(self))
4054 for ch in self.getChairList():
4055 conf.addChair(ch.clone())
4056 ContextManager.setdefault("clone.unique_id_map", {})[self.getUniqueId()] = conf.getUniqueId()
4057 # Display Manager
4058 from MaKaC.webinterface import displayMgr
4059 selfDispMgr=displayMgr.ConfDisplayMgrRegistery().getDisplayMgr(self)
4060 selfDispMgr.clone(conf)
4061 # Contribution Types' List (main detailes of the conference)
4062 for t in self.getContribTypeList() :
4063 conf.addContribType(t.clone(conf))
4064 if options.get("sessions", False):
4065 for entry in self.getSchedule().getEntries():
4066 if isinstance(entry,BreakTimeSchEntry):
4067 conf.getSchedule().addEntry(entry.clone(conf))
4068 db_root = DBMgr.getInstance().getDBConnection().root()
4069 if db_root.has_key( "webfactoryregistry" ):
4070 confRegistry = db_root["webfactoryregistry"]
4071 else:
4072 confRegistry = OOBTree.OOBTree()
4073 db_root["webfactoryregistry"] = confRegistry
4074 meeting=False
4075 # if the event is a meeting or a lecture
4076 if confRegistry.get(str(self.getId()), None) is not None :
4077 meeting=True
4078 confRegistry[str(conf.getId())] = confRegistry[str(self.getId())]
4079 # if it's a conference, no web factory is needed
4080 # Tracks in a conference
4081 if options.get("tracks",False) :
4082 for tr in self.getTrackList() :
4083 conf.addTrack(tr.clone(conf))
4084 # Meetings' and conferences' sessions cloning
4085 if options.get("sessions",False) :
4086 for s in self.getSessionList() :
4087 newSes = s.clone(timeDelta, conf, options, session_id=s.getId())
4088 ContextManager.setdefault("clone.unique_id_map", {})[s.getUniqueId()] = newSes.getUniqueId()
4089 conf.addSession(newSes)
4090 # Materials' cloning
4091 if options.get("materials",False) :
4092 for m in self.getMaterialList() :
4093 conf.addMaterial(m.clone(conf))
4094 if self.getPaper() is not None:
4095 conf.setPaper(self.getPaper().clone(conf))
4096 if self.getSlides() is not None:
4097 conf.setSlides(self.getSlides().clone(conf))
4098 if self.getVideo() is not None:
4099 conf.setVideo(self.getVideo().clone(conf))
4100 if self.getPoster() is not None:
4101 conf.setPoster(self.getPoster().clone(conf))
4102 # access and modification keys
4103 if options.get("keys", False) :
4104 conf.setAccessKey(self.getAccessKey())
4105 conf.setModifKey(self.getModifKey())
4106 # Access Control cloning
4107 if options.get("access",False) :
4108 conf.setProtection(self.getAccessController()._getAccessProtection())
4109 for mgr in self.getManagerList() :
4110 conf.grantModification(mgr)
4111 for user in self.getAllowedToAccessList() :
4112 conf.grantAccess(user)
4113 for right in self.getSessionCoordinatorRights():
4114 conf.addSessionCoordinatorRight(right)
4115 for domain in self.getDomainList():
4116 conf.requireDomain(domain)
4117 # conference's registration form
4118 if options.get("registration",False) :
4119 conf.setRegistrationForm(self.getRegistrationForm().clone(conf))
4121 # conference's evaluation
4122 if options.get("evaluation",False) :
4123 #Modify this, if you have now many evaluations.
4124 #You will have to clone every evaluations of this conference.
4125 conf.setEvaluations([self.getEvaluation().clone(conf)])
4127 #conference's abstracts
4128 if options.get("abstracts",False) :
4129 conf.abstractMgr = self.abstractMgr.clone(conf)
4130 # Meetings' and conferences' contributions cloning
4131 if options.get("contributions",False) :
4132 sch = conf.getSchedule()
4133 for cont in self.getContributionList():
4134 if cont.getSession() is None :
4135 if not meeting:
4136 nc = cont.clone(conf, options, timeDelta)
4137 conf.addContribution(nc)
4138 if cont.isScheduled() :
4139 sch.addEntry(nc.getSchEntry())
4140 ContextManager.setdefault("clone.unique_id_map", {})[cont.getUniqueId()] = nc.getUniqueId()
4141 elif cont.isScheduled():
4142 # meetings...only scheduled
4143 nc = cont.clone(conf, options, timeDelta)
4144 conf.addContribution(nc)
4145 sch.addEntry(nc.getSchEntry())
4146 ContextManager.setdefault("clone.unique_id_map", {})[cont.getUniqueId()] = nc.getUniqueId()
4147 # Participants' module settings and list cloning
4148 if options.get("participants",False) :
4149 self.getParticipation().clone(conf, options, eventManager)
4150 conf.notifyModification()
4152 #we inform the plugins in case they want to add anything to the new conference
4153 EventCloner.clone_event(self, conf)
4154 return conf
4156 def getCoordinatedTracks( self, av ):
4157 """Returns a list with the tracks for which a user is coordinator.
4159 try:
4160 if self._trackCoordinators:
4161 pass
4162 except AttributeError:
4163 self._trackCoordinators = TCIndex()
4164 self.notifyModification()
4165 return self._trackCoordinators.getTracks( av )
4167 def addTrackCoordinator( self, track, av ):
4168 """Makes a user become coordinator for a track.
4170 try:
4171 if self._trackCoordinators:
4172 pass
4173 except AttributeError:
4174 self._trackCoordinators = TCIndex()
4175 self.notifyModification()
4176 if track in self.program:
4177 track.addCoordinator( av )
4178 self._trackCoordinators.indexCoordinator( av, track )
4179 self.notifyModification()
4181 def removeTrackCoordinator( self, track, av ):
4182 """Removes a user as coordinator for a track.
4184 try:
4185 if self._trackCoordinators:
4186 pass
4187 except AttributeError:
4188 self._trackCoordinators = TCIndex()
4189 self.notifyModification()
4190 if track in self.program:
4191 track.removeCoordinator( av )
4192 self._trackCoordinators.unindexCoordinator( av, track )
4193 self.notifyModification()
4195 def _rebuildAuthorIndex(self):
4196 self._authorIdx=AuthorIndex()
4197 for contrib in self.getContributionList():
4198 if not isinstance(contrib.getCurrentStatus(),ContribStatusWithdrawn):
4199 for auth in contrib.getAuthorList():
4200 self._authorIdx.index(auth)
4202 def getAuthorIndex(self):
4203 try:
4204 if self._authorIdx:
4205 pass
4206 except AttributeError:
4207 self._rebuildAuthorIndex()
4208 return self._authorIdx
4210 def indexAuthor(self,auth):
4211 c=auth.getContribution()
4212 if c.isAuthor(auth):
4213 if not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4214 self.getAuthorIndex().index(auth)
4215 if c.isPrimaryAuthor(auth):
4216 self._getPrimAuthIndex().index(auth)
4218 def unindexAuthor(self,auth):
4219 c=auth.getContribution()
4220 if c.isAuthor(auth):
4221 self.getAuthorIndex().unindex(auth)
4222 if c.isPrimaryAuthor(auth):
4223 self._getPrimAuthIndex().unindex(auth)
4225 def _rebuildSpeakerIndex(self):
4226 self._speakerIdx=AuthorIndex()
4227 for contrib in self.getContributionList():
4228 if not isinstance(contrib.getCurrentStatus(),ContribStatusWithdrawn):
4229 for auth in contrib.getSpeakerList():
4230 self._speakerIdx.index(auth)
4231 for subcontrib in contrib.getSubContributionList():
4232 for auth in subcontrib.getSpeakerList():
4233 self._speakerIdx.index(auth)
4235 def getSpeakerIndex(self):
4236 try:
4237 if self._speakerIdx:
4238 pass
4239 except AttributeError:
4240 self._rebuildSpeakerIndex()
4241 return self._speakerIdx
4243 def indexSpeaker(self,auth):
4244 c=auth.getContribution()
4245 if not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4246 self.getSpeakerIndex().index(auth)
4248 def unindexSpeaker(self,auth):
4249 c=auth.getContribution()
4250 if c and not isinstance(c.getCurrentStatus(),ContribStatusWithdrawn):
4251 self.getSpeakerIndex().unindex(auth)
4253 def getRegistrationForm(self):
4254 try:
4255 if self._registrationForm is None:
4256 self._registrationForm = registration.RegistrationForm(self)
4257 except AttributeError,e:
4258 self._registrationForm = registration.RegistrationForm(self)
4259 return self._registrationForm
4261 def setRegistrationForm(self,rf):
4262 self._registrationForm = rf
4263 rf.setConference(self)
4265 def removeRegistrationForm(self):
4266 try:
4267 self._registrationForm.delete()
4268 self._registrationForm.setConference(None)
4269 self._registrationForm = None
4270 except AttributeError:
4271 self._registrationForm = None
4273 def recoverRegistrationForm(self, rf):
4274 self.setRegistrationForm(rf)
4275 rf.recover()
4277 def getEvaluation(self, id=0):
4278 ############################################################################
4279 #For the moment only one evaluation per conference is used. #
4280 #In the future if there are more than one evaluation, modify this function.#
4281 ############################################################################
4282 """ Return the evaluation given by its ID or None if nothing found.
4283 Params:
4284 id -- id of the wanted evaluation
4286 for evaluation in self.getEvaluations():
4287 if str(evaluation.getId()) == str(id) :
4288 return evaluation
4289 if Config.getInstance().getDebug():
4290 raise Exception(_("Error with id: expected '%s', found '%s'.")%(id, self.getEvaluations()[0].getId()))
4291 else:
4292 return self.getEvaluations()[0]
4294 def getEvaluations(self):
4295 if not hasattr(self, "_evaluations"):
4296 self._evaluations = [Evaluation(self)]
4297 return self._evaluations
4299 def setEvaluations(self, evaluationsList):
4300 self._evaluations = evaluationsList
4301 for evaluation in self._evaluations:
4302 evaluation.setConference(self)
4304 def removeEvaluation(self, evaluation):
4305 """remove the given evaluation from its evaluations."""
4306 evaluations = self.getEvaluations()
4307 if evaluations.count(evaluation)>0:
4308 evaluations.remove(evaluation)
4309 evaluation.removeReferences()
4310 self.notifyModification()
4312 def removeAllEvaluations(self):
4313 for evaluation in self.getEvaluations():
4314 evaluation.removeReferences()
4315 self._evaluations = []
4316 self.notifyModification()
4318 def _getEvaluationCounter(self):
4319 if not hasattr(self, "_evaluationCounter"):
4320 self._evaluationCounter = Counter()
4321 return self._evaluationCounter
4323 ## Videoconference bookings related
4324 def getBookings(self):
4325 try:
4326 if self._bookings:
4327 pass
4328 except AttributeError, e:
4329 self._bookings = {}
4330 self.notifyModification()
4331 return self._bookings
4333 def getBookingsList(self, sort = False):
4334 bl = self.getBookings().values()
4335 if sort:
4336 bl.sort()
4337 return bl
4339 def _getBookingGenerator(self):
4340 try:
4341 return self._bookingGenerator
4342 except AttributeError, e:
4343 self._bookingGenerator = Counter()
4344 return self._bookingGenerator
4346 def getNewBookingId(self):
4347 return str(self._getBookingGenerator().newCount())
4349 def addBooking(self, bp):
4350 if (bp.getId() == ""):
4351 bp.setId(self.getNewBookingId())
4352 self.getBookings()[bp.getId()] = bp
4353 self.notifyModification()
4355 def hasBooking(self,booking):
4356 return booking.getConference()==self and \
4357 self.getBookings().has_key(booking.getId())
4359 def removeBooking(self, booking):
4360 if self.hasBooking(booking):
4361 deletion= booking.deleteBooking()
4362 if deletion[0] != 1:
4363 del self.getBookings()[booking.getId()]
4364 self.notifyModification()
4365 return deletion
4367 def getBookingByType(self, type):
4368 if self.getBookings().has_key(type):
4369 return self.getBookings()[type]
4370 return None
4372 def getBookingById(self, id):
4373 if self.getBookings().has_key(id):
4374 return self.getBookings()[id]
4375 return None
4377 ## End of Videoconference bookings related
4379 def getRegistrants(self):
4380 try:
4381 if self._registrants:
4382 pass
4383 except AttributeError, e:
4384 self._registrants = {}
4385 self.notifyModification()
4386 return self._registrants
4388 def getRegistrantsByEmail(self, email=None):
4390 Returns the index of registrants by email OR a specific registrant if an email address
4391 is passed as argument.
4393 try:
4394 if self._registrantsByEmail:
4395 pass
4396 except AttributeError, e:
4397 self._registrantsByEmail = self._createRegistrantsByEmail()
4398 self.notifyModification()
4399 if email:
4400 return self._registrantsByEmail.get(email)
4401 return self._registrantsByEmail
4403 def _createRegistrantsByEmail(self):
4404 dicByEmail = {}
4405 for r in self.getRegistrantsList():
4406 dicByEmail[r.getEmail()] = r
4407 return dicByEmail
4409 def getRegistrantsList(self, sort = False):
4410 rl = self.getRegistrants().values()
4411 if sort:
4412 rl.sort(registration.Registrant._cmpFamilyName)
4413 return rl
4415 def _getRegistrantGenerator(self):
4416 try:
4417 return self._registrantGenerator
4418 except AttributeError, e:
4419 self._registrantGenerator = Counter()
4420 return self._registrantGenerator
4422 def addRegistrant(self, rp, user):
4423 rp.setId( str(self._getRegistrantGenerator().newCount()) )
4424 rp.setOwner( self )
4425 self.getRegistrants()[rp.getId()] = rp
4426 signals.event.registrant_changed.send(self, user=user, registrant=rp, action='added')
4427 self.notifyModification()
4429 def updateRegistrantIndexByEmail(self, rp, newEmail):
4430 oldEmail = rp.getEmail()
4431 if oldEmail != newEmail:
4432 if self.getRegistrantsByEmail().has_key(oldEmail):
4433 del self.getRegistrantsByEmail()[oldEmail]
4434 self.getRegistrantsByEmail()[newEmail] = rp
4435 self.notifyModification()
4437 def hasRegistrant(self,rp):
4438 return rp.getConference()==self and \
4439 self.getRegistrants().has_key(rp.getId())
4441 def hasRegistrantByEmail(self, email):
4442 # Return true if there is someone with the email of the param "email"
4443 return self.getRegistrantsByEmail().has_key(email)
4445 def removeRegistrant(self, id):
4446 part = self.getRegistrants()[id]
4447 self._registrationForm.notifyRegistrantRemoval(self.getRegistrants()[id])
4448 del self.getRegistrantsByEmail()[self.getRegistrantById(id).getEmail()]
4449 del self.getRegistrants()[id]
4450 signals.event.registrant_changed.send(self, user=part.getAvatar(), registrant=part, action='removed')
4451 TrashCanManager().add(part)
4452 self.notifyModification()
4454 def getRegistrantById(self, id):
4455 if self.getRegistrants().has_key(id):
4456 return self.getRegistrants()[id]
4457 return None
4459 def _getPrimAuthIndex(self):
4460 try:
4461 if self._primAuthIdx:
4462 pass
4463 except AttributeError:
4464 self._primAuthIdx=_PrimAuthIdx(self)
4465 return self._primAuthIdx
4467 def getContribsMatchingAuth(self,query,onlyPrimary=True):
4468 if str(query).strip()=="":
4469 return self.getContributionList()
4470 res=self._getPrimAuthIndex().match(query)
4471 return [self.getContributionById(id) for id in res]
4473 def getCoordinatedSessions( self, av ):
4474 """Returns a list with the sessions for which a user is coordinator.
4476 try:
4477 if self._sessionCoordinators:
4478 pass
4479 except AttributeError:
4480 self._sessionCoordinators = SCIndex()
4481 sessions = self._sessionCoordinators.getSessions( av )
4482 for session in self.getSessionList():
4483 if session not in sessions and av != None:
4484 for email in av.getEmails():
4485 if email in session.getCoordinatorEmailList():
4486 sessions.append(session)
4487 break
4488 return sessions
4490 def getManagedSession( self, av ):
4491 ls = []
4492 for session in self.getSessionList():
4493 pending = False
4494 if av != None:
4495 for email in av.getEmails():
4496 if email in session.getAccessController().getModificationEmail():
4497 pending = True
4498 break
4499 if av in session.getManagerList() or pending:
4500 ls.append(session)
4501 return ls
4503 def addSessionCoordinator(self,session,av):
4504 """Makes a user become coordinator for a session.
4506 try:
4507 if self._sessionCoordinators:
4508 pass
4509 except AttributeError:
4510 self._sessionCoordinators = SCIndex()
4511 if self.sessions.has_key(session.getId()):
4512 session.addCoordinator(av)
4513 self._sessionCoordinators.index(av,session)
4514 session._addCoordinatorEmail(av.getEmail())
4516 def removeSessionCoordinator( self, session, av ):
4517 """Removes a user as coordinator for a session.
4519 try:
4520 if self._sessionCoordinators:
4521 pass
4522 except AttributeError:
4523 self._sessionCoordinators = SCIndex()
4524 if self.sessions.has_key(session.getId()):
4525 session.removeCoordinator( av )
4526 self._sessionCoordinators.unindex(av,session)
4527 session.removeCoordinatorEmail(av.getEmail())
4529 def _getSubmitterIdx(self):
4530 try:
4531 return self._submitterIdx
4532 except AttributeError:
4533 self._submitterIdx=SubmitterIndex()
4534 return self._submitterIdx
4536 def addContribSubmitter(self,contrib,av):
4537 self._getSubmitterIdx().index(av,contrib)
4539 def removeContribSubmitter(self,contrib,av):
4540 self._getSubmitterIdx().unindex(av,contrib)
4542 def getContribsForSubmitter(self,av):
4543 return self._getSubmitterIdx().getContributions(av)
4545 def getBOAConfig(self):
4546 try:
4547 if self._boa:
4548 pass
4549 except AttributeError:
4550 self._boa=BOAConfig(self)
4551 return self._boa
4553 def getSessionCoordinatorRights(self):
4554 try:
4555 if self._sessionCoordinatorRights:
4556 pass
4557 except AttributeError, e:
4558 self._sessionCoordinatorRights = []
4559 self.notifyModification()
4560 return self._sessionCoordinatorRights
4562 def hasSessionCoordinatorRight(self, right):
4563 return right in self.getSessionCoordinatorRights()
4565 def addSessionCoordinatorRight(self, right):
4566 if SessionCoordinatorRights().hasRight(right) and not self.hasSessionCoordinatorRight(right):
4567 self._sessionCoordinatorRights.append(right)
4568 self.notifyModification()
4570 def removeSessionCoordinatorRight(self, right):
4571 if SessionCoordinatorRights().hasRight(right) and self.hasSessionCoordinatorRight(right):
4572 self._sessionCoordinatorRights.remove(right)
4573 self.notifyModification()
4575 def hasEnabledSection(self, section):
4576 # This hack is there since there is no more enable/disable boxes
4577 # in the conference managment area corresponding to those features.
4578 # Until the managment area is improved to get a more user-friendly
4579 # way of enabling/disabling those features, we always make them
4580 # available for the time being, but we keep the previous code for
4581 # further improvements
4582 return True
4584 def getPendingQueuesMgr(self):
4585 try:
4586 if self._pendingQueuesMgr:
4587 pass
4588 except AttributeError, e:
4589 self._pendingQueuesMgr=pendingQueues.ConfPendingQueuesMgr(self)
4590 return self._pendingQueuesMgr
4592 def getAccessController(self):
4593 return self.__ac
4595 def _cmpTitle( c1, c2 ):
4596 o1 = c1.getTitle().lower().strip()
4597 o2 = c2.getTitle().lower().strip()
4598 return cmp( o1, o2 )
4599 _cmpTitle=staticmethod(_cmpTitle)
4601 def getReportNumberHolder(self):
4602 try:
4603 if self._reportNumberHolder:
4604 pass
4605 except AttributeError, e:
4606 self._reportNumberHolder=ReportNumberHolder(self)
4607 return self._reportNumberHolder
4609 def setReportNumberHolder(self, rnh):
4610 self._reportNumberHolder=rnh
4612 def getBadgeTemplateManager(self):
4613 try:
4614 if self.__badgeTemplateManager:
4615 pass
4616 except AttributeError:
4617 self.__badgeTemplateManager = BadgeTemplateManager(self)
4618 return self.__badgeTemplateManager
4620 def setBadgeTemplateManager(self, badgeTemplateManager):
4621 self.__badgeTemplateManager = badgeTemplateManager
4623 def getPosterTemplateManager(self):
4624 try:
4625 if self.__posterTemplateManager:
4626 pass
4627 except AttributeError:
4628 self.__posterTemplateManager = PosterTemplateManager(self)
4630 return self.__posterTemplateManager
4632 def setPosterTemplateManager(self, posterTemplateManager):
4633 self.__posterTemplateManager = posterTemplateManager
4635 class DefaultConference(Conference):
4636 """ 'default' conference, which stores the
4637 default templates for posters and badges
4640 def indexConf(self):
4641 pass
4643 def __init__(self):
4644 admin = User.find_first(is_admin=True)
4645 if admin is None:
4646 raise MaKaCError(_("""There are no admin users. The "default" conference that stores the template cannot be created.
4647 Please add at least 1 user to the admin list."""))
4648 Conference.__init__(self, admin.as_avatar, "default")
4651 class ConferenceHolder( ObjectHolder ):
4652 """Specialised ObjectHolder dealing with conference objects. It gives a
4653 common entry point and provides simple methods to access and
4654 maintain the collection of stored conferences (DB).
4656 idxName = "conferences"
4657 counterName = "CONFERENCE"
4659 def _newId( self ):
4660 id = ObjectHolder._newId( self )
4661 return "%s"%id
4663 def getById(self, id, quiet=False):
4664 if id == 'default':
4665 return CategoryManager().getDefaultConference()
4667 id = str(id)
4668 if is_legacy_id(id):
4669 mapping = LegacyEventMapping.find_first(legacy_event_id=id)
4670 id = str(mapping.event_id) if mapping is not None else None
4671 event = self._getIdx().get(id) if id is not None else None
4672 if event is None and not quiet:
4673 raise NotFoundError(_("The event with id '{}' does not exist or has been deleted").format(id),
4674 title=_("Event not found"))
4675 return event
4678 class Observer(object):
4679 """ Base class for Observer objects.
4680 Inheriting classes should overload the following boolean class attributes:
4681 _shouldBeTitleNotified
4682 _shouldBeDateChangeNotified
4683 _shouldBeLocationChangeNotified
4684 _shouldBeDeletionNotified
4685 And set them to True if they want to be notified of the corresponding event.
4686 In that case, they also have to implement the corresponding methods:
4687 _notifyTitleChange (for title notification)
4688 _notifyEventDateChanges and _notifyTimezoneChange (for date / timezone notification)
4689 _shouldBeLocationChangeNotified (for location notification)
4690 _notifyDeletion (for deletion notification).
4691 The interface for those methods is also specified in this class. If the corresponding
4692 class attribute is set to False but the method is not implemented, an exception will be thrown.
4694 _shouldBeTitleNotified = False
4695 _shouldBeDateChangeNotified = False
4696 _shouldBeLocationChangeNotified = False
4697 _shouldBeDeletionNotified = False
4699 def getObserverName(self):
4700 name = "'Observer of class" + self.__class__.__name__
4701 try:
4702 conf = self.getOwner()
4703 name = name + " of event " + conf.getId() + "'"
4704 except AttributeError:
4705 pass
4706 return name
4708 def notifyTitleChange(self, oldTitle, newTitle):
4709 if self._shouldBeTitleNotified:
4710 self._notifyTitleChange(oldTitle, newTitle)
4712 def notifyEventDateChanges(self, oldStartDate = None, newStartDate = None, oldEndDate = None, newEndDate = None):
4713 if self._shouldBeDateChangeNotified:
4714 self._notifyEventDateChanges(oldStartDate, newStartDate, oldEndDate, newEndDate)
4716 def notifyTimezoneChange(self, oldTimezone, newTimezone):
4717 if self._shouldBeDateChangeNotified:
4718 self._notifyTimezoneChange(oldTimezone, newTimezone)
4720 def notifyLocationChange(self, newLocation):
4721 if self._shouldBeLocationChangeNotified:
4722 self._notifyLocationChange(newLocation)
4724 def notifyDeletion(self):
4725 if self._shouldBeDeletionNotified:
4726 self._notifyDeletion()
4728 def _notifyTitleChange(self, oldTitle, newTitle):
4729 """ To be implemented by inheriting classes
4730 Notifies the observer that the Conference object's title has changed
4732 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method _notifyTitleChange")
4734 def _notifyEventDateChanges(self, oldStartDate, newStartDate, oldEndDate, newEndDate):
4735 """ To be implemented by inheriting classes
4736 Notifies the observer that the start and / or end dates of the object it is attached to has changed.
4737 If the observer finds any problems during whatever he needs to do as a consequence of
4738 the event dates changing, he should write strings describing the problems
4739 in the 'dateChangeNotificationProblems' context variable (which is a list of strings).
4741 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyStartDateChange")
4743 def _notifyTimezoneChange(self, oldTimezone, newTimezone):
4744 """ To be implemented by inheriting classes.
4745 Notifies the observer that the end date of the object it is attached to has changed.
4746 This method has to return a list of strings describing problems encountered during
4747 whatever the DateChangeObserver object does as a consequence of the notification.
4748 If there are no problems, the DateChangeObserver should return an empty list.
4750 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyTimezoneChange")
4752 def _notifyLocationChange(self):
4753 """ To be implemented by inheriting classes
4754 Notifies the observer that the location of the object it is attached to has changed.
4756 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyLocationChange")
4758 def _notifyDeletion(self):
4759 """ To be implemented by inheriting classes
4760 Notifies the observer that the Conference object it is attached to has been deleted
4762 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyDeletion")
4764 class TitleChangeObserver(Observer):
4765 """ Base class for objects who want to be notified of a Conference object being deleted.
4766 Inheriting classes have to implement the notifyTitleChange method, and probably the __init__ method too.
4769 def notifyTitleChange(self, oldTitle, newTitle):
4770 """ To be implemented by inheriting classes
4771 Notifies the observer that the Conference object's title has changed
4773 raise MaKaCError("Class " + str(self.__class__.__name__) + " did not implement method notifyTitleChange")
4776 class SessionChair(ConferenceParticipation):
4778 def __init__(self):
4779 self._session=None
4780 self._id=""
4781 ConferenceParticipation.__init__(self)
4783 def _notifyModification( self ):
4784 if self._session != None:
4785 self._session.notifyModification()
4787 def clone(self):
4788 chair = SessionChair()
4789 chair.setValues(self.getValues())
4790 return chair
4792 def getSession(self):
4793 return self._session
4795 def getConference(self):
4796 s=self.getSession()
4797 if s is None:
4798 return None
4799 return s.getConference()
4801 def includeInSession(self,session,id):
4802 if self.getSession()==session and self.getId()==id.strip():
4803 return
4804 self._session=session
4805 self._id=id
4807 def delete( self ):
4808 self._session=None
4809 ConferenceParticipation.delete(self)
4811 def getLocator(self):
4812 if self.getSession() is None:
4813 return None
4814 loc=self.getSession().getLocator()
4815 loc["convId"]=self.getId()
4816 return loc
4818 def isSessionManager(self):
4819 # pendings managers
4820 if self.getEmail() in self._session.getAccessController().getModificationEmail():
4821 return True
4822 # managers list
4823 for manager in self._session.getManagerList():
4824 if self.getEmail() == manager.getEmail():
4825 return True
4826 return False
4828 def isSessionCoordinator(self):
4829 # pendings coordinators
4830 if self.getEmail() in self._session.getConference().getPendingQueuesMgr().getPendingCoordinatorsKeys():
4831 return True
4832 # coordinator list
4833 for coord in self._session.getCoordinatorList():
4834 if self.getEmail() == coord.getEmail():
4835 return True
4836 return False
4839 class SlotChair(ConferenceParticipation):
4841 def __init__(self):
4842 self._slot=None
4843 self._id=""
4844 ConferenceParticipation.__init__(self)
4846 def _notifyModification( self ):
4847 if self._slot != None:
4848 self._slot.notifyModification()
4850 def clone(self):
4851 chair = SlotChair()
4852 chair.setValues(self.getValues())
4853 return chair
4855 def getSlot(self):
4856 return self._slot
4858 def getSession(self):
4859 s=self.getSlot()
4860 if s is None:
4861 return None
4862 return s.getSession()
4864 def getConference(self):
4865 s=self.getSlot()
4866 if s is None:
4867 return None
4868 return s.getConference()
4870 def includeInSlot(self,slot,id):
4871 if self.getSlot()==slot and self.getId()==id.strip():
4872 return
4873 self._slot=slot
4874 self._id=id
4876 def delete( self ):
4877 self._slot=None
4878 ConferenceParticipation.delete(self)
4880 def getLocator(self):
4881 if self.getSlot() is None:
4882 return None
4883 loc=self.getSlot().getLocator()
4884 loc["convId"]=self.getId()
4885 return loc
4887 class SessionCoordinatorRights:
4889 def __init__(self):
4890 self._rights = {"modifContribs": "Modify the contributions",
4891 "unrestrictedSessionTT": "Unrestricted session timetable management"
4894 def hasRight(self, r):
4895 return self._rights.has_key(r)
4897 def getRights(self):
4898 return self._rights
4900 def getRightList(self, sort=False):
4901 l=self._rights.values()
4902 if sort:
4903 l.sort()
4904 return l
4906 def getRightKeys(self):
4907 return self._rights.keys()
4909 def getRight(self, id):
4910 if self._rights.has_key(id):
4911 return self._rights[id]
4912 return None
4914 class SCIndex(Persistent):
4915 """Index for conference session coordinators.
4917 This class allows to index conference session coordinators so the owner
4918 can answer optimally to the query if a user is coordinating
4919 any conference session.
4920 It is implemented by simply using a BTree where the Avatar id is used
4921 as key (because it is unique and non variable) and a list of
4922 coordinated sessions is kept as keys. It is the responsability of the
4923 index owner (conference) to keep it up-to-date i.e. notify session
4924 coordinator additions and removals.
4927 def __init__( self ):
4928 self._idx=OOBTree()
4931 def getSessions(self,av):
4932 """Gives a list with the sessions a user is coordinating.
4934 if av == None:
4935 return []
4936 return self._idx.get(av.getId(),[])
4938 def index(self,av,session):
4939 """Registers in the index a coordinator of a session.
4941 if av == None or session == None:
4942 return
4943 if not self._idx.has_key(av.getId()):
4944 l=[]
4945 self._idx[av.getId()]=l
4946 else:
4947 l=self._idx[av.getId()]
4948 if session not in l:
4949 l.append(session)
4950 self.notifyModification()
4952 def unindex(self,av,session):
4953 if av==None or session==None:
4954 return
4955 l=self._idx.get(av.getId(),[])
4956 if session in l:
4957 l.remove(session)
4958 self.notifyModification()
4960 def notifyModification(self):
4961 self._idx._p_changed=1
4964 class Session(CommonObjectBase, Locatable):
4965 """This class implements a conference session, being the different parts
4966 in which the conference can be divided and the contributions can be
4967 organised in. The class contains necessary attributes to store session
4968 basic data and provides the operations related to sessions. In
4969 principle, a session has no sense to exist without being related to a
4970 conference but it is allowed for flexibility.
4973 fossilizes(ISessionFossil)
4976 def __init__(self, **sessionData):
4977 """Class constructor. Initialise the class attributes to the default
4978 values.
4979 Params:
4980 sessionData -- (Dict) Contains the data the session object has to
4981 be initialised to.
4983 self.conference=None
4984 self.id="not assigned"
4985 self.title=""
4986 self.description=""
4987 #################################
4988 # Fermi timezone awareness #
4989 #################################
4990 self.startDate = nowutc()
4991 #################################
4992 # Fermi timezone awareness(end) #
4993 #################################
4995 self.duration=timedelta(minutes=1)
4996 self.places=[]
4997 self.rooms=[]
4998 self.conveners=[] # This attribute must not be used and should disappear someday
4999 self._conveners=[]
5000 self._convenerGen=Counter()
5001 self.convenerText=""
5002 self.contributions={}
5003 self._contributionDuration=timedelta(minutes=20)
5004 self.__ac=AccessController(self)
5005 self.materials={}
5006 self.__materialGenerator=Counter()
5007 self._comments = ""
5008 self.slots={}
5009 self.__slotGenerator=Counter()
5010 self._setSchedule()
5011 self._coordinators=OOBTree()
5012 self._coordinatorsEmail = []
5013 self._code=""
5014 self._color="#e3f2d3"
5015 self._textColor="#202020"
5016 self._textColorToLinks=False
5017 self._ttType=SlotSchTypeFactory.getDefaultId()
5018 self._closed = False
5019 self._registrationSession = None
5020 self._creationDS = nowutc()
5021 self._modificationDS = nowutc()
5022 self._keywords = ""
5024 @return_ascii
5025 def __repr__(self):
5026 event_id = self.conference.getId() if self.conference else None
5027 return '<Session({}, {}, {})>'.format(self.getId(), self.getTitle(), event_id)
5029 def __cmp__(self, other):
5030 if type(self) is not type(other):
5031 # This is actually dangerous and the ZODB manual says not to do this
5032 # because it relies on memory order. However, this branch should never
5033 # be taken anyway since we do not store different types in the same set
5034 # or use them as keys.
5035 return cmp(hash(self), hash(other))
5036 if self.getConference() == other.getConference():
5037 return cmp(self.getId(), other.getId())
5038 return cmp(self.getConference(), other.getConference())
5040 @property
5041 @memoize_request
5042 def note(self):
5043 from indico.modules.events.notes.models.notes import EventNote
5044 return EventNote.get_for_linked_object(self)
5046 def getVerboseType(self):
5047 return 'Session'
5049 def getTimezone( self ):
5050 return self.getConference().getTimezone()
5052 def updateNonInheritingChildren(self, elem, delete=False, propagate=True):
5053 self.getAccessController().updateNonInheritingChildren(elem, delete)
5054 if propagate == True:
5055 self.notify_protection_to_owner(elem, delete)
5057 def notify_protection_to_owner(self, elem, delete=False):
5058 """ This methods notifies the owner that the protection has been changed,
5059 so it can update its list of non inheriting children """
5060 self.getOwner().updateNonInheritingChildren(elem, delete)
5062 def getKeywords(self):
5063 try:
5064 return self._keywords
5065 except:
5066 self._keywords = ""
5067 return ""
5069 def setKeywords(self, keywords):
5070 self._keywords = keywords
5072 def notifyModification( self, raiseEvent = True, date = None, cleanCache = True ):
5073 """Method called to notify the current session has been modified.
5075 self.setModificationDate(date)
5077 parent = self.getConference()
5078 if parent:
5079 parent.setModificationDate(date)
5080 if cleanCache:
5081 for slot in self.getSlotList():
5082 slot.cleanCache()
5083 self._p_changed=1
5085 def getModificationDate( self ):
5086 """Returns the date in which the session was last modified"""
5087 try:
5088 return self._modificationDS
5089 except:
5090 self._modificationDS = nowutc()
5091 return self._modificationDS
5093 def getCreationDate( self ):
5094 """Returns the date in which the session was created"""
5095 try:
5096 return self._creationDS
5097 except:
5098 self._creationDS = nowutc()
5099 return self._creationDS
5101 def getLogInfo(self):
5102 data = {}
5103 data["subject"] = self.title
5104 data["session id"] = self.id
5105 data["session code"] = self._code
5106 data["title"] = self.title
5107 data["description"] = self.description
5108 data["start date"] = format_datetime(self.startDate, locale='en_GB', timezone=self.getConference().timezone)
5109 data["duration"] = format_human_timedelta(self.duration)
5110 for p in self.places :
5111 data["place"] = p.getName()
5112 for r in self.rooms :
5113 data["room"] = r.getName()
5114 for sc in self.getConvenerList() :
5115 data["convener %s"%sc.getId()] = sc.getFullName()
5116 for co in self.getCoordinatorList() :
5117 data["coordinators %s"%co.getId()] = co.getFullName()
5119 return data
5121 def getEnableSessionSlots(self):
5122 try:
5123 return self.getConference().getEnableSessionSlots()
5124 except:
5125 return True
5127 def cmpSessionByTitle(session1, session2):
5128 return cmp(session1.getTitle(), session2.getTitle())
5129 cmpSessionByTitle = staticmethod(cmpSessionByTitle)
5131 def hasRegistrationSession(self):
5132 return self.getRegistrationSession() is not None
5134 def getRegistrationSession(self):
5135 try:
5136 if self._registrationSession:
5137 pass
5138 except AttributeError, e:
5139 self._registrationSession = None
5140 return self._registrationSession
5142 def setRegistrationSession(self, rs):
5143 self._registrationSession = rs
5145 def isClosed( self ):
5146 if self.getConference().isClosed():
5147 return True
5148 try:
5149 return self._closed
5150 except:
5151 self._closed = False
5152 return False
5154 def setClosed( self, closed=True ):
5155 self._closed = closed
5156 self.notifyModification(cleanCache = False)
5158 def includeInConference(self,conf,newId):
5159 self.conference=conf
5160 self.id=newId
5161 for slot in self.getSlotList():
5162 conf.getSchedule().addEntry(slot.getConfSchEntry(),2)
5163 self.getConference().addSession(self)
5164 self.notifyModification()
5166 def delete(self):
5167 while len(self.getConvenerList()) > 0:
5168 self.removeConvener(self.getConvenerList()[0])
5169 while len(self.getMaterialList()) > 0:
5170 self.removeMaterial(self.getMaterialList()[0])
5171 for c in self.getCoordinatorList()[:]:
5172 self.removeCoordinator(c)
5173 while len(self.contributions.values())>0:
5174 self.removeContribution(self.contributions.values()[0])
5175 while len(self.slots.values())>0:
5176 self._removeSlot(self.slots.values()[0])
5177 if self.getConference() is not None:
5178 self.getConference().removeSession(self)
5179 if self.hasRegistrationSession():
5180 self.getConference().getRegistrationForm().getSessionsForm().removeSession(self.getId())
5181 self.getRegistrationSession().setRegistrationForm(None)
5182 TrashCanManager().add(self.getRegistrationSession())
5183 self.notify_protection_to_owner(self, delete=True)
5184 self.conference=None
5185 TrashCanManager().add(self)
5187 def recover(self, isCancelled):
5188 if self.hasRegistrationSession():
5189 if not isCancelled:
5190 self.getRegistrationSession().setRegistrationForm(self.getConference().getRegistrationForm())
5191 self.getConference().getRegistrationForm().getSessionsForm().addSession(self.getRegistrationSession())
5192 TrashCanManager().remove(self.getRegistrationSession())
5193 TrashCanManager().remove(self)
5195 def getLocator( self ):
5196 """Gives back a globaly unique identification encapsulated in a Locator
5197 object for the session instance
5199 if self.conference == None:
5200 return Locator()
5201 lconf = self.conference.getLocator()
5202 lconf["sessionId"] = self.getId()
5203 return lconf
5205 def getConference( self ):
5206 return self.conference
5208 def getSession( self ):
5209 return self
5211 def getOwner( self ):
5212 return self.getConference()
5214 def getId( self ):
5215 return self.id
5217 def getUniqueId( self ):
5218 """returns (string) the unique identiffier of the item"""
5219 """used mainly in the web session access key table"""
5220 return "%ss%s" % (self.getConference().getUniqueId(),self.id)
5222 def getModifKey( self ):
5223 return self.getConference().getModifKey()
5225 def getAccessKey( self ):
5226 return self.getConference().getAccessKey()
5228 def getContribDuration(self):
5229 try:
5230 return self._contributionDuration
5231 except:
5232 self._contributionDuration = timedelta(minutes=20)
5233 return self._contributionDuration
5235 def setContribDuration(self, hour=0, min=20, dur=None):
5236 if dur is not None:
5237 self._contributionDuration=dur
5238 else:
5239 self._contributionDuration = timedelta(hours=hour,minutes=min)
5241 def fit(self):
5242 #if not self.getConference().getEnableSessionSlots():
5243 # self.getSlotList()[0].fit()
5244 self.setStartDate(self.getMinSlotStartDate(),0,0)
5245 self.setEndDate(self.getMaxSlotEndDate(),0)
5247 def addSlot(self,newSlot):
5248 id = newSlot.getId()
5249 if id == "not assigned":
5250 newSlot.setId(str(self.__slotGenerator.newCount()))
5251 self.slots[newSlot.getId()]=newSlot
5252 self.fit()
5253 self.getSchedule().addEntry(newSlot.getSessionSchEntry(),2)
5254 if self.getConference() is not None:
5255 self.getConference().getSchedule().addEntry(newSlot.getConfSchEntry(),2)
5256 self.notifyModification()
5258 def _removeSlot(self,slot):
5259 del self.slots[slot.getId()]
5260 self.getSchedule().removeEntry(slot.getSessionSchEntry())
5261 if self.getConference() is not None:
5262 self.getConference().getSchedule().removeEntry(slot.getConfSchEntry())
5263 slot.delete()
5265 def removeSlot(self, slot, force=False):
5266 if self.slots.has_key(slot.getId()):
5267 if len(self.slots)==1 and not force:
5268 raise MaKaCError( _("A session must have at least one slot"), _("Session"))
5269 msg = u'Deleted session block: {}'.format(to_unicode(slot.getTitle() or slot.getSession().getTitle()))
5270 self.getConference().log(EventLogRealm.management, EventLogKind.negative, u'Timetable',
5271 msg, session.user, data=slot.getLogInfo())
5272 self._removeSlot(slot)
5273 self.fit()
5274 self.notifyModification()
5276 def recoverSlot(self, slot):
5277 self.addSlot(slot)
5278 slot.recover()
5280 def getSlotById(self,slotId):
5281 return self.slots.get(slotId,None)
5283 def getSlotList(self):
5284 return self.slots.values()
5286 def getSortedSlotList(self):
5287 sl = self.getSlotList()
5288 sl.sort(utils.sortSlotByDate)
5289 return sl
5291 def getMinSlotStartTime(self):
5292 min = (25,61)
5293 for slot in self.getSlotList():
5294 if slot.isMoreThanDay():
5295 return (0,0)
5296 shour = slot.getStartDate().hour
5297 smin = slot.getStartDate().minute
5298 if (shour, smin) < min:
5299 min = (shour, smin)
5300 return min
5302 def getMaxSlotEndTime(self):
5303 max = (-1,-1)
5304 for slot in self.getSlotList():
5305 if slot.isMoreThanDay():
5306 return (23, 59)
5307 endDate = slot.getEndDate()
5308 if (endDate.hour, endDate.minute) > max:
5309 newEndDate = endDate - timedelta(0, 0, 0)
5310 max = (newEndDate.hour, newEndDate.minute)
5311 return max
5313 def getMinSlotStartDate(self):
5314 slotList = self.getSlotList()
5315 if len(slotList)==0:
5316 return self.getStartDate()
5317 else:
5318 sDate = self.getEndDate()
5319 for slot in slotList:
5320 if slot.getStartDate() < sDate:
5321 sDate = slot.getStartDate()
5322 return sDate
5324 def getMaxSlotEndDate(self):
5325 slotList = self.getSlotList()
5326 if len(slotList)==0:
5327 return self.getEndDate()
5328 else:
5329 eDate = self.getStartDate()
5330 for slot in slotList:
5331 if slot.getEndDate() > eDate:
5332 eDate = slot.getEndDate()
5333 return eDate
5335 def _getCorrectColor(self, color):
5336 if not color.startswith("#"):
5337 color = "#%s"%color
5338 m = re.match("^#[0-9A-Fa-f]{6}$", color)
5339 if m:
5340 return color
5341 return None
5343 def _getCorrectBgColor(self, color):
5344 color=self._getCorrectColor(color)
5345 if color is None:
5346 return self._color
5347 return color
5349 def _getCorrectTextColor(self, color):
5350 color=self._getCorrectColor(color)
5351 if color is None:
5352 return self._textColor
5353 return color
5355 def setValues( self, sessionData,check=2,moveEntries=0 ):
5356 """Sets all the values of the current session object from a dictionary
5357 containing the following key-value pairs:
5358 title-(str)
5359 description-(str)
5360 locationName-(str) => name of the location, if not specified
5361 it will be set to the conference location name.
5362 locationAddress-(str)
5363 roomName-(str) => name of the room, if not specified it will
5364 be set to the conference room name.
5365 sDate - (datetime) => starting date of the session, if not
5366 specified it will be set to now.
5367 eDate - (datetime) => ending date of the session, if not
5368 specified the end date will be set to the start one
5369 durHour - (int) => hours of duration for each entry in the session
5370 by default.
5371 durMin - (int) => hours of duration for each entry in the session
5372 by default.
5373 _conveners - (str)
5374 check parameter:
5375 0: no check at all
5376 1: check and raise error in case of problem
5377 2: check and adapt the owner dates
5378 Please, note that this method sets ALL values which means that if
5379 the given dictionary doesn't contain any of the keys the value
5380 will set to a default value.
5383 self.setTitle( sessionData.get("title", "NO TITLE ASSIGNED") )
5384 self.setDescription( sessionData.get("description", "") )
5385 code = sessionData.get("code", "")
5386 if code.strip() == "":
5387 if self.getId()=="not assigned":
5388 self.setCode("no code")
5389 else:
5390 self.setCode(self.getId())
5391 else:
5392 self.setCode(code)
5393 bgcolor = sessionData.get("backgroundColor", "")
5394 if bgcolor.strip() != "":
5395 self.setColor(self._getCorrectBgColor(bgcolor))
5396 textcolor = sessionData.get("textColor", "")
5397 if textcolor.strip() != "":
5398 if sessionData.has_key("autotextcolor"):
5399 self.setTextColor(utils.getTextColorFromBackgroundColor(self.getColor()))
5400 else:
5401 self.setTextColor(self._getCorrectTextColor(textcolor))
5402 self.setTextColorToLinks(sessionData.has_key("textcolortolinks"))
5404 if "locationName" in sessionData:
5405 loc = self.getOwnLocation()
5406 if not loc:
5407 loc = CustomLocation()
5408 self.setLocation( loc )
5409 loc.setName( sessionData["locationName"] )
5410 loc.setAddress( sessionData.get("locationAddress", "") )
5411 else:
5412 self.setLocation(None)
5414 #same as for the location
5415 if "roomName" in sessionData:
5416 room = self.getOwnRoom()
5417 if not room:
5418 room = CustomRoom()
5419 self.setRoom( room )
5420 room.setName( sessionData["roomName"] )
5421 else:
5422 self.setRoom(None)
5424 if sessionData.get("sDate",None) is not None:
5425 self.setStartDate(sessionData["sDate"],check,moveEntries=moveEntries)
5426 if sessionData.get("eDate",None) is not None:
5427 self.setEndDate(sessionData["eDate"],check)
5428 self._checkInnerSchedule()
5429 if sessionData.get("contribDuration","")!="":
5430 self._contributionDuration = sessionData.get("contribDuration")
5431 else:
5432 self._contributionDuration = timedelta(hours=int(sessionData.get("durHour",0)), minutes=int(sessionData.get("durMin",20)))
5433 self.notifyModification()
5435 def move(self, sDate):
5437 Move a session from the old start date to a new start date, and
5438 it moves all the entries of the session as well, without date validations.
5440 if sDate is not None:
5441 oldStartDate=self.startDate
5442 self.startDate=copy.copy(sDate)
5443 diff=self.startDate-oldStartDate
5444 # Check date to not be prior conference start date and to not surpass conference end date
5445 # The schedule is returning the datetime object as timezone aware relative to the conference
5446 # timezone. Must adjust the startdate accordingly for comparison. JMF
5447 conftz = self.getConference().getTimezone()
5448 if self.getStartDate() < self.getConference().getSchedule().getStartDate() or \
5449 self.getEndDate() > self.getConference().getSchedule().getEndDate():
5450 raise MaKaCError( _("Impossible to move the session because it would be out of the conference dates"))
5451 for entry in self.getSchedule().getEntries():
5452 if isinstance(entry,LinkedTimeSchEntry) and \
5453 isinstance(entry.getOwner(), SessionSlot):
5454 e = entry.getOwner()
5455 e.move(e.getStartDate() + diff)
5456 self.getSchedule().reSchedule()
5457 self.getConference().getSchedule().reSchedule()
5458 self.notifyModification()
5460 def clone(self, deltaTime, conf, options, session_id=None):
5461 ses = Session()
5462 conf.addSession(ses, check=0, session_id=session_id)
5463 ses.setTitle(self.getTitle())
5464 ses.setDescription(self.getDescription())
5465 startDate = self.getStartDate() + deltaTime
5466 ses.setStartDate(startDate, check=1)
5467 ses.setDuration(dur=self.getDuration())
5469 if self.getOwnLocation() is not None:
5470 ses.addLocation(self.getOwnLocation().clone())
5471 if self.getOwnRoom() is not None:
5472 ses.setRoom(self.getOwnRoom().clone())
5473 ses.setColor(self.getColor())
5474 ses.setTextColor(self.getTextColor())
5475 ses.setTextColorToLinks(self.isTextColorToLinks())
5476 ses.setCode(self.getCode())
5477 ses.setContribDuration(dur=self.getContribDuration())
5478 ses.setScheduleType(self.getScheduleType())
5479 ses.setComments(self.getComments())
5481 # Access Control cloning
5482 if options.get("access", False) :
5483 ses.setProtection(self.getAccessController()._getAccessProtection())
5484 for mgr in self.getManagerList() :
5485 ses.grantModification(mgr)
5486 for user in self.getAllowedToAccessList() :
5487 ses.grantAccess(user)
5488 for domain in self.getDomainList():
5489 ses.requireDomain(domain)
5490 for coord in self.getCoordinatorList():
5491 ses.addCoordinator(coord)
5493 #slots in timeschedule
5494 for slot in self.getSlotList() :
5495 newslot = slot.clone(ses, options)
5496 ses.addSlot(newslot)
5497 ContextManager.setdefault("clone.unique_id_map", {})[slot.getUniqueId()] = newslot.getUniqueId()
5499 ses.notifyModification()
5501 return ses
5504 def setTitle( self, newTitle ):
5505 self.title = newTitle
5506 self.notifyModification()
5508 def getTitle( self ):
5509 return self.title
5511 def setDescription(self, newDescription ):
5512 self.description = newDescription
5513 self.notifyModification()
5515 def getDescription(self):
5516 return self.description
5518 def getCode(self):
5519 try:
5520 if self._code:
5521 pass
5522 except AttributeError:
5523 self._code=self.id
5524 return self._code
5526 def setCode(self,newCode):
5527 self._code=str(newCode).strip()
5529 def getColor(self):
5530 try:
5531 if self._color:
5532 pass
5533 except AttributeError:
5534 self._color="#e3f2d3"
5535 return self._color
5536 getBgColor=getColor
5538 def setColor(self,newColor):
5539 self._color=str(newColor).strip()
5540 self.notifyModification()
5541 setBgColor=setColor
5543 def getTextColor(self):
5544 try:
5545 if self._textColor:
5546 pass
5547 except AttributeError:
5548 self._textColor="#202020"
5549 return self._textColor
5551 def setTextColor(self,newColor):
5552 self._textColor=str(newColor).strip()
5553 self.notifyModification()
5555 def isTextColorToLinks(self):
5556 try:
5557 if self._textColorToLink:
5558 pass
5559 except AttributeError:
5560 self._textColorToLink=False
5561 return self._textColorToLink
5563 def setTextColorToLinks(self, v):
5564 self._textColorToLink=v
5565 self.notifyModification()
5567 def getStartDate(self):
5568 return self.startDate
5570 def getAdjustedStartDate(self,tz=None):
5571 if not tz:
5572 tz = self.getConference().getTimezone()
5573 if tz not in all_timezones:
5574 tz = 'UTC'
5575 return self.startDate.astimezone(timezone(tz))
5577 def verifyStartDate(self, sdate, check=2):
5578 """check parameter:
5579 0: no check at all
5580 1: check and raise error in case of problem (default)
5581 2: check and adapt the owner dates
5584 conf=self.getConference()
5586 if conf is not None and sdate < conf.getSchedule().getStartDate():
5587 if check==1:
5588 raise ParentTimingError( _("The session starting date cannot be prior to the event starting date"), _("Session"))
5589 elif check==2:
5590 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
5591 conf, sdate.astimezone(timezone(conf.getTimezone()))))
5592 conf.setStartDate(sdate,check=0,moveEntries=0)
5594 def setStartDate(self,newDate,check=2,moveEntries=0):
5596 moveEntries parameter:
5597 0: do not move inner slots
5598 1: move
5599 2: do not move but check that session is not out of the conference dates
5602 if not newDate.tzname():
5603 raise MaKaCError("date should be timezone aware")
5604 if check != 0:
5605 self.verifyStartDate(newDate,check)
5606 oldSdate = self.getStartDate()
5607 try:
5608 tz = str(self.getStartDate().tzinfo)
5609 except:
5610 tz = 'UTC'
5611 diff = newDate - oldSdate
5612 self.startDate=copy.copy(newDate)
5613 if moveEntries == 1 and diff is not None and diff != timedelta(0):
5614 # If the start date changed, we move entries inside the timetable
5615 newDateTz = newDate.astimezone(timezone(tz))
5616 if oldSdate.astimezone(timezone(tz)).date() != newDateTz.date():
5617 entries = self.getSchedule().getEntries()[:]
5618 else:
5619 entries = self.getSchedule().getEntriesOnDay(newDateTz)[:]
5620 self.getSchedule().moveEntriesBelow(diff, entries)
5622 if moveEntries != 0 and self.getConference() and \
5623 not self.getConference().getEnableSessionSlots() and \
5624 self.getSlotList() != [] and \
5625 self.getSlotList()[0].getStartDate() != newDate:
5626 self.getSlotList()[0].startDate = newDate
5628 if check == 1:
5629 self._checkInnerSchedule()
5630 self.notifyModification()
5632 def _checkInnerSchedule( self ):
5633 self.getSchedule().checkSanity()
5635 def getEndDate(self):
5636 return self.startDate+self.duration
5638 ####################################
5639 # Fermi timezone awareness #
5640 ####################################
5642 def getAdjustedEndDate(self,tz=None):
5643 return self.getAdjustedStartDate(tz) + self.duration
5645 ####################################
5646 # Fermi timezone awareness(end) #
5647 ####################################
5649 def verifyEndDate(self, edate,check=1):
5650 """check parameter:
5651 0: no check at all
5652 1: check and raise error in case of problem
5653 2: check and adapt the owner dates
5655 try:
5656 tz = timezone(self.getConference().getTimezone())
5657 except:
5658 tz = timezone('UTC')
5659 # compare end date with start date
5660 if edate<=self.getStartDate():
5661 if check == 1:
5662 raise MaKaCError( _("End date cannot be prior to the Start date"), _("Session"))
5663 if check == 2:
5664 self.setStartDate(edate)
5665 # check conference dates
5666 if (self.getConference()):
5667 conf=self.getConference()
5668 confStartDate = conf.getSchedule().getStartDate()
5669 confEndDate = conf.getSchedule().getEndDate()
5670 if conf is not None and (edate>confEndDate or edate<=confStartDate):
5671 if check==1:
5672 raise ParentTimingError( _("The end date has to be between the event dates (%s - %s)")%\
5673 (confStartDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
5674 confEndDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
5675 _("Session"))
5676 if check==2:
5677 if edate>confEndDate:
5678 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
5679 self.getConference(),
5680 edate.astimezone(tz)))
5681 self.getConference().setEndDate(edate)
5682 if edate<=confStartDate:
5683 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
5684 self.getConference(),
5685 edate.astimezone(tz)))
5686 self.getConference().setStartDate(edate)
5687 # check inner schedule
5688 if len(self.getSlotList()) != 0 and self.getSlotList()[-1].getSchedule().hasEntriesAfter(edate):
5689 raise TimingError( _("Cannot change end date: some entries in the session schedule end after the new date"), _("Session"))
5691 def setEndDate(self,newDate,check=2):
5692 if not newDate.tzname():
5693 raise MaKaCError("date should be timezone aware")
5694 if check != 0:
5695 self.verifyEndDate(newDate,check)
5696 self.duration=newDate-self.getStartDate()
5697 # A session is not always linked to a conference (for eg. at creation time)
5698 #if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSlotList()[0].getEndDate() != newDate:
5699 # self.getSlotList()[0].duration = self.duration
5700 self.notifyModification()
5702 def setDates(self, sDate, eDate, check=1, moveEntries=0):
5703 if eDate <= sDate:
5704 tz = timezone(self.getConference().getTimezone())
5705 raise FormValuesError(_("The end date ({}) cannot be prior to the start date ({})").format(
5706 eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),
5707 _("Session"))
5708 self.setStartDate(sDate, check, moveEntries)
5709 self.setEndDate(eDate, check)
5710 self._checkInnerSchedule()
5712 def getDuration(self):
5713 return self.duration
5715 def setDuration(self, hours=0, minutes=15, dur=0):
5716 if dur == 0:
5717 dur = timedelta(hours=int(hours), minutes=int(minutes))
5718 if dur.seconds <= 0:
5719 raise FormValuesError(_("The duration cannot be less than zero"), _("Session"))
5720 self.duration = dur
5721 self.verifyEndDate(self.getEndDate())
5722 self.notifyModification()
5724 def getStartOnDay(self, day, tz=None):
5725 if not tz:
5726 tz = self.getConference().getTimezone()
5727 if type(day) is datetime:
5728 day = day.astimezone(timezone(tz))
5729 if day.date() < self.getStartDate().astimezone(timezone(tz)).date() or day.date() > self.getEndDate().astimezone(timezone(tz)).date() :
5730 return None
5731 minTime = self.getEndDate()
5732 for e in self.getSchedule().getEntriesOnDay(day) :
5733 if e.getStartDate() < minTime :
5734 minTime = e.getStartDate()
5735 if minTime == self.getEndDate() :
5736 minTime = day.replace(hour=8, minute=0)#datetime.combine(day,time(hour=8, minute=0))
5737 if minTime < self.getStartDate() :
5738 return self.getStartDate()
5739 return minTime
5741 def getEndOnDay(self, day, tz=None):
5742 if not tz:
5743 tz = self.getConference().getTimezone()
5744 if type(day) is datetime:
5745 day = day.astimezone(timezone(tz))
5746 if day.date() < self.getStartDate().astimezone(timezone(tz)).date() or day.date() > self.getEndDate().astimezone(timezone(tz)).date() :
5747 return None
5748 maxTime = self.getStartDate();
5749 for e in self.getSchedule().getEntriesOnDay(day) :
5750 if e.getEndDate() > maxTime :
5751 maxTime = e.getEndDate()
5752 if maxTime == self.getStartDate() :
5753 maxTime = day.replace(hour=19, minute=0)#datetime.combine(day,time(19,0))
5754 if maxTime > self.getEndDate() :
5755 return self.getEndDate()
5756 return maxTime
5758 def getLocationParent( self ):
5760 Returns the object from which the room/location
5761 information should be inherited
5763 return self.getConference()
5765 def getLocationList(self):
5766 """Method returning a list of "location" objects which contain the
5767 information about the different places the conference is gonna
5768 happen
5770 return self.places
5772 def addLocation(self, newPlace):
5773 self.places.append( newPlace )
5774 self.notifyModification()
5776 def _resetConveners(self):
5777 try:
5778 if self._conveners:
5779 return
5780 except AttributeError:
5781 self._conveners=[]
5782 for oc in self.conveners:
5783 newConv=SessionChair()
5784 newConv.setDataFromAvatar(oc)
5785 self._addConvener(newConv)
5787 def getConvenerList(self):
5788 self._resetConveners()
5789 return self._conveners
5791 def getAllConvenerList(self):
5792 convenerList = set()
5793 for slot in self.getSlotList():
5794 for convener in slot.getConvenerList():
5795 convenerList.add(convener)
5796 return convenerList
5798 def _addConvener(self,newConv):
5799 if newConv in self._conveners:
5800 return
5801 try:
5802 if self._convenerGen:
5803 pass
5804 except AttributeError:
5805 self._convenerGen=Counter()
5806 id = newConv.getId()
5807 if id == "":
5808 id=int(self._convenerGen.newCount())
5809 newConv.includeInSession(self,id)
5810 self._conveners.append(newConv)
5811 self.notifyModification()
5813 def addConvener(self,newConv):
5814 self._resetConveners()
5815 self._addConvener(newConv)
5816 if isinstance(newConv, AvatarUserWrapper):
5817 conv.unlinkTo(self, "convener")
5819 def removeConvener(self,conv):
5820 self._resetConveners()
5821 if conv not in self._conveners:
5822 return
5823 #--Pending queue: remove pending Convener waiting to became manager if anything
5824 self.getConference().getPendingQueuesMgr().removePendingManager(conv)
5826 #--Pending queue: remove pending Convener waiting to became coordinator if anything
5827 self.getConference().getPendingQueuesMgr().removePendingCoordinator(conv)
5829 self._conveners.remove(conv)
5830 if isinstance(conv, AvatarUserWrapper):
5831 conv.linkTo(self, "convener")
5832 conv.delete()
5833 self.notifyModification()
5835 def recoverConvener(self, con):
5836 self.addConvener(con)
5837 con.recover()
5839 def getConvenerById(self,id):
5840 id=int(id)
5841 for conv in self._conveners:
5842 if conv.getId()==id:
5843 return conv
5844 return None
5846 def getConvenerText( self ):
5847 #to be removed
5848 try:
5849 if self.convenerText:
5850 pass
5851 except AttributeError, e:
5852 self.convenerText = ""
5853 return self.convenerText
5855 def setConvenerText( self, newText ):
5856 self.convenerText = newText.strip()
5858 def appendConvenerText( self, newText ):
5859 self.setConvenerText( "%s, %s"%(self.getConvenerText(), newText.strip()) )
5861 def addContribution(self, newContrib, contrib_id=None):
5862 """Registers the contribution passed as parameter within the session
5863 assigning it a unique id.
5865 if self.hasContribution(newContrib):
5866 return
5867 self.getConference().addContribution(newContrib, contrib_id=contrib_id)
5868 self.contributions[newContrib.getId()]=newContrib
5869 newContrib.setSession(self)
5871 self.updateNonInheritingChildren(newContrib)
5872 for child in newContrib.getAccessController().getNonInheritingChildren():
5873 self.updateNonInheritingChildren(child)
5875 self.notifyModification()
5877 def hasContribution(self,contrib):
5878 return contrib.getSession()==self and \
5879 self.contributions.has_key(contrib.getId())
5881 def removeContribution(self,contrib):
5882 """Removes the indicated contribution from the session
5884 if not self.hasContribution(contrib):
5885 return
5886 if contrib.isScheduled():
5887 # unschedule the contribution
5888 sch=contrib.getSchEntry().getSchedule()
5889 sch.removeEntry(contrib.getSchEntry())
5890 del self.contributions[contrib.getId()]
5892 self.updateNonInheritingChildren(contrib, delete=True, propagate=False)
5893 for child in contrib.getAccessController().getNonInheritingChildren():
5894 self.updateNonInheritingChildren(child, delete=True, propagate=False)
5896 contrib.setSession(None)
5898 self.notifyModification()
5900 def newContribution( self, params = None, id=None ):
5901 c = Contribution()
5902 if params:
5903 c.setValues(params)
5904 self.addContribution( c, id )
5905 return c
5907 def getContributionById(self,id):
5908 id=str(id).strip()
5909 if self.contributions.has_key( id ):
5910 return self.contributions[ id ]
5911 return None
5913 def getContributionList( self ):
5914 return self.contributions.values()
5916 def getNumberOfContributions(self, only_scheduled=False):
5917 if only_scheduled:
5918 return len(filter(lambda c: c.isScheduled(), self.contributions.itervalues()))
5919 else:
5920 return len(self.contributions)
5922 def isProtected(self):
5923 # tells if a session is protected or not
5924 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
5926 def getAccessProtectionLevel( self ):
5927 return self.__ac.getAccessProtectionLevel()
5929 def isItselfProtected( self ):
5930 return self.__ac.isItselfProtected()
5932 def hasAnyProtection( self ):
5933 """Tells whether a session has any kind of protection over it:
5934 access or domain protection.
5936 if self.__ac.isProtected():
5937 return True
5938 if self.getDomainList():
5939 return True
5940 if self.getAccessProtectionLevel() == -1:
5941 return False
5943 return self.getOwner().hasAnyProtection()
5945 def hasProtectedOwner( self ):
5946 if self.getOwner() != None:
5947 return self.getOwner().isProtected()
5948 return False
5950 def setProtection( self, private ):
5951 self.__ac.setProtection( private )
5952 self.notify_protection_to_owner(self)
5954 def grantAccess( self, prin ):
5955 self.__ac.grantAccess( prin )
5956 if isinstance(prin, AvatarUserWrapper):
5957 prin.linkTo(self, "access")
5959 def revokeAccess( self, prin ):
5960 self.__ac.revokeAccess( prin )
5961 if isinstance(prin, AvatarUserWrapper):
5962 prin.unlinkTo(self, "access")
5964 def canView( self, aw ):
5965 """tells whether the specified user has access to the current object
5966 or any of its sub-objects
5968 if self.canAccess( aw ):
5969 return True
5971 for contrib in self.getContributionList():
5972 if contrib.canView( aw ):
5973 return True
5974 return False
5976 def isAllowedToAccess( self, user ):
5977 if not user:
5978 return False
5979 if user in self.getCoordinatorList() or self.__ac.canUserAccess( user ) \
5980 or self.canUserModify( user ) or (not self.isItselfProtected() and self.getOwner().isAllowedToAccess(user)):
5981 return True
5982 return False
5984 def canAccess( self, aw ):
5985 # Allow harvesters (Invenio, offline cache) to access
5986 # protected pages
5987 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
5988 return True
5989 #####################################################
5991 # Managers have always access
5992 if self.canModify(aw):
5993 return True
5995 flag_allowed_to_access = self.isAllowedToAccess(aw.getUser())
5996 if not self.canIPAccess(request.remote_addr) and not self.canUserModify(aw.getUser()) and \
5997 not flag_allowed_to_access:
5998 return False
5999 if not self.isProtected():
6000 return True
6001 return flag_allowed_to_access or self.conference.canKeyAccess(aw)
6003 def grantModification(self, sb, sendEmail=True):
6004 if isinstance(sb, SessionChair) or isinstance(sb, SlotChair):
6005 ah = AvatarHolder()
6006 results = ah.match({"email": sb.getEmail()}, exact=1)
6007 r = None
6008 for i in results:
6009 if sb.getEmail().lower().strip() in [j.lower().strip() for j in i.getEmails()]:
6010 r = i
6011 break
6012 if r is not None and r.isActivated():
6013 self.__ac.grantModification(r)
6014 r.linkTo(self, "manager")
6015 elif sb.getEmail() != "":
6016 modificationEmailGranted = self.__ac.grantModificationEmail(sb.getEmail())
6017 if modificationEmailGranted and sendEmail:
6018 notif = pendingQueues._PendingManagerNotification( [sb] )
6019 mail.GenericMailer.sendAndLog(notif, self.getConference(), 'Session')
6020 else:
6021 self.__ac.grantModification( sb )
6022 if isinstance(sb, AvatarUserWrapper):
6023 sb.linkTo(self, "manager")
6025 def revokeModification( self, prin ):
6026 self.__ac.revokeModification( prin )
6027 if isinstance(prin, AvatarUserWrapper):
6028 prin.unlinkTo(self, "manager")
6030 def canModify(self, aw_or_user):
6031 if hasattr(aw_or_user, 'getUser'):
6032 aw_or_user = aw_or_user.getUser()
6033 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
6035 def canUserModify( self, av ):
6036 """Tells whether a user is allowed to modify the current session:
6037 only if the user is granted to modify the session or the user
6038 can modify the corresponding conference.
6040 return self.getConference().canUserModify( av ) or self.__ac.canModify( av )
6042 def getManagerList( self ):
6043 return self.__ac.getModifierList()
6045 def getAllowedToAccessList( self ):
6046 return self.__ac.getAccessList()
6048 def addMaterial( self, newMat ):
6049 newMat.setId( str(self.__materialGenerator.newCount()) )
6050 newMat.setOwner( self )
6051 self.materials[ newMat.getId() ] = newMat
6052 self.notifyModification()
6054 def removeMaterial( self, mat ):
6055 if mat.getId() in self.materials.keys():
6056 mat.delete()
6057 self.materials[mat.getId()].setOwner(None)
6058 del self.materials[ mat.getId() ]
6059 self.notifyModification()
6060 return "done: %s"%mat.getId()
6061 return "not done: %s"%mat.getId()
6063 def recoverMaterial(self, recMat):
6064 # Id must already be set in recMat.
6065 recMat.setOwner( self )
6066 self.materials[ recMat.getId() ] = recMat
6067 recMat.recover()
6068 self.notifyModification()
6070 def getMaterialRegistry(self):
6072 Return the correct material registry for this type
6074 from MaKaC.webinterface.materialFactories import SessionMFRegistry
6075 return SessionMFRegistry
6077 def getMaterialById( self, matId ):
6078 if self.materials.has_key(matId):
6079 return self.materials[ matId ]
6080 return None
6082 def getMaterialList( self ):
6083 return self.materials.values()
6085 def getAllMaterialList(self, sort=True):
6086 l = self.getMaterialList()
6087 if sort:
6088 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
6089 return l
6091 def _setSchedule(self):
6092 self.__schedule=SessionSchedule(self)
6093 sl=self.getSlotList()
6094 for slot in self.getSlotList():
6095 self.__schedule.addEntry(slot.getSchEntry())
6097 def getSchedule( self ):
6098 try:
6099 if self.__schedule is None or not isinstance(self.__schedule,SessionSchedule):
6100 self._setSchedule()
6101 except AttributeError, e:
6102 self._setSchedule()
6103 return self.__schedule
6105 def getMasterSchedule( self ):
6106 return self.getOwner().getSchedule()
6108 def requireDomain( self, dom ):
6109 self.__ac.requireDomain( dom )
6111 def freeDomain( self, dom ):
6112 self.__ac.freeDomain( dom )
6114 def getDomainList( self ):
6115 return self.__ac.getRequiredDomainList()
6117 def setComments(self,comm):
6118 self._comments = comm.strip()
6120 def getComments(self):
6121 try:
6122 if self._comments:
6123 pass
6124 except AttributeError,e:
6125 self._comments=""
6126 return self._comments
6128 def _addCoordinator(self, av):
6129 if av is None or self._coordinators.has_key(av.getId()):
6130 return
6131 self._coordinators[av.getId()]=av
6132 if self.getConference() is not None:
6133 self.getConference().addSessionCoordinator(self,av)
6135 def getCoordinatorEmailList(self):
6136 try:
6137 return self._coordinatorsEmail
6138 except:
6139 self._coordinatorsEmail = []
6140 return self._coordinatorsEmail
6142 def _addCoordinatorEmail(self, email):
6143 if email not in self.getCoordinatorEmailList():
6144 self.getCoordinatorEmailList().append(email)
6146 def removeCoordinatorEmail(self, email):
6147 if email in self.getCoordinatorEmailList():
6148 self.getCoordinatorEmailList().remove(email)
6149 self._p_changed = 1
6151 def addCoordinator( self, sb, sendEmail=True ):
6152 """Grants coordination privileges to user.
6154 Arguments:
6155 sb -- It can be either:
6156 (AvatarUserWrapper) the user to which
6157 coordination privileges must be granted.
6159 (MaKaC.conference.SessionChair) a non-existing which
6160 has to become indico user before to be granted with privileges.
6162 try:
6163 if self._coordinators:
6164 pass
6165 except AttributeError, e:
6166 self._coordinators=OOBTree()
6168 if isinstance(sb, SessionChair):
6169 ah = AvatarHolder()
6170 results=ah.match({"email":sb.getEmail()}, exact=1)
6171 r=None
6173 for i in results:
6174 if sb.getEmail().lower().strip() in [j.lower().strip() for j in i.getEmails()]:
6176 break
6178 if r is not None and r.isActivated():
6180 self._addCoordinator(r)
6181 r.linkTo(self, "coordinator")
6182 else:
6183 self.getConference().getPendingQueuesMgr().addPendingCoordinator(sb)
6184 else:
6185 self._addCoordinator(sb)
6186 if isinstance(sb, AvatarUserWrapper):
6187 sb.linkTo(self, "coordinator")
6189 def removeCoordinator( self, av ):
6190 """Revokes coordination privileges to user.
6192 Arguments:
6193 av -- (AvatarUserWrapper) user for which coordination privileges
6194 must be revoked
6196 try:
6197 if self._coordinators:
6198 pass
6199 except AttributeError, e:
6200 self._coordinators=OOBTree()
6202 if av is None or not self._coordinators.has_key(av.getId()):
6203 return
6204 del self._coordinators[av.getId()]
6205 if isinstance(av, AvatarUserWrapper):
6206 av.unlinkTo(self, "coordinator")
6207 if self.getConference() is not None:
6208 self.getConference().removeSessionCoordinator(self,av)
6210 def isCoordinator( self, av ):
6211 """Tells whether the specified user is a coordinator of the session.
6213 Arguments:
6214 av -- (AvatarUserWrapper) user to be checked
6216 Return value: (boolean)
6218 try:
6219 if self._coordinators:
6220 pass
6221 except AttributeError, e:
6222 self._coordinators=OOBTree()
6223 if (av is not None) and self._coordinators.has_key(av.getId()):
6224 return True
6225 ret = False
6226 if isinstance(av, AvatarUserWrapper):
6227 for email in av.getEmails():
6228 if email in self.getCoordinatorEmailList():
6229 self.addCoordinator(av)
6230 self.removeCoordinatorEmail(email)
6231 ret = True
6232 return ret
6234 def hasConvenerByEmail(self, email):
6235 for convener in self.getConvenerList():
6236 if email == convener.getEmail():
6237 return True
6238 return False
6241 def getCoordinatorList( self ):
6242 """Return all users which have privileges to coordinate the session.
6244 Return value: (list)
6246 try:
6247 if self._coordinators:
6248 pass
6249 except AttributeError, e:
6250 self._coordinators=OOBTree()
6252 return self._coordinators.values()
6254 def canCoordinate(self, aw_or_user, right=""):
6255 """Tells if a user has coordination privileges.
6257 Only session coordinators have coordination privileges over a
6258 session.
6260 Params:
6261 aw -- (MaKaC.accessControl.AccessWrapper) User access
6262 information for which the coordination privileges must be
6263 checked.
6265 Return value: (boolean)
6267 if hasattr(aw_or_user, 'getUser'):
6268 aw_or_user = aw_or_user.getUser()
6269 if right != "":
6270 return self.isCoordinator(aw_or_user) and self.getConference().hasSessionCoordinatorRight(right)
6271 return self.isCoordinator(aw_or_user)
6274 def getScheduleType(self):
6275 try:
6276 if self._ttType:
6277 pass
6278 except AttributeError:
6279 self._ttType=SlotSchTypeFactory.getDefaultId()
6280 return self._ttType
6282 def setScheduleType(self,t):
6283 try:
6284 if self._ttType:
6285 pass
6286 except AttributeError:
6287 self._ttType=SlotSchTypeFactory.getDefaultId()
6288 t=str(t).strip().lower()
6289 if t not in SlotSchTypeFactory.getIdList() or t==self._ttType:
6290 return
6291 self._ttType=t
6292 for slot in self.getSlotList():
6293 slot.setScheduleType(t)
6295 def getAccessController(self):
6296 return self.__ac
6299 def _cmpTitle( s1, s2 ):
6300 s1=s1.getTitle().lower().strip()
6301 s2=s2.getTitle().lower().strip()
6302 return cmp( s1, s2 )
6303 _cmpTitle=staticmethod(_cmpTitle)
6306 class SessionSlot(Persistent, Fossilizable, Locatable):
6308 fossilizes(ISessionSlotFossil)
6310 def __init__(self,session,**sessionSlotData):
6311 self.session = session
6312 self.id = "not assigned"
6313 self.title = ""
6314 self.startDate=None
6315 self.duration = timedelta(minutes=1)
6316 self.places = []
6317 self.rooms = []
6318 self._conveners = []
6319 self._convenerGen=Counter()
6320 self._schedule=SlotSchTypeFactory.getDefaultKlass()(self)
6321 self._sessionSchEntry=LinkedTimeSchEntry(self)
6322 self._confSchEntry=LinkedTimeSchEntry(self)
6323 self._contributionDuration = None
6325 @property
6326 @memoize_request
6327 def note(self):
6328 from indico.modules.events.notes.models.notes import EventNote
6329 return EventNote.get_for_linked_object(self.session)
6331 def getTimezone( self ):
6332 return self.getConference().getTimezone()
6334 def getLogInfo(self):
6335 data = {}
6336 data["id"] = self.id
6337 data["title"] = self.title
6338 data["session"] = self.session.getTitle()
6339 data["start date"] = format_datetime(self.startDate, locale='en_GB', timezone=self.getConference().timezone)
6340 data["duration"] = format_human_timedelta(self.duration)
6341 i = 0
6342 for p in self.places :
6343 data["place %s"%i] = p.getName()
6344 i+=1
6345 i = 0
6346 for r in self.rooms :
6347 data["room %s"%i] = r.getName()
6348 i+=1
6349 for c in self._conveners :
6350 data["convener %s"%c.getId()] = c.getFullName()
6351 return data
6353 def clone(self,session, options):
6355 slot = SessionSlot(session)
6356 slot.session = session
6357 slot.setTitle(self.getTitle())
6358 timeDifference = session.getConference().getStartDate() - self.getSession().getConference().getStartDate()
6359 slot.setStartDate(self.getStartDate() + timeDifference)
6360 slot.setDuration(dur=self.getDuration(), check=2)
6362 #places
6363 if self.getOwnLocation() is not None:
6364 slot.setLocation(self.getOwnLocation().clone())
6365 #rooms
6366 if self.getOwnRoom() is not None:
6367 slot.setRoom(self.getOwnRoom().clone())
6369 #chairs = conveners
6370 for ch in self.getOwnConvenerList() :
6371 slot.addConvener(ch.clone())
6373 #populate the timetable
6374 if options.get("contributions", False) :
6375 for entry in self.getEntries() :
6376 if isinstance(entry, BreakTimeSchEntry) :
6377 newentry = entry.clone(slot)
6378 slot.getSchedule().addEntry(newentry,0)
6379 elif isinstance(entry, ContribSchEntry) :
6380 contrib = entry.getOwner()
6381 newcontrib = contrib.clone(session, options, timeDifference)
6382 slot.getSchedule().addEntry(newcontrib.getSchEntry(),0)
6383 ContextManager.setdefault("clone.unique_id_map", {})[contrib.getUniqueId()] = newcontrib.getUniqueId()
6385 slot.setContribDuration(0, 0, self.getContribDuration())
6386 slot.notifyModification(cleanCache = False)
6388 return slot
6390 def fit( self ):
6392 sets the start date of the slot to the start date of the first son
6393 and the end date to the end date of the last son
6395 sch = self.getSchedule()
6396 entries = sch.getEntries()
6397 if len(entries) > 0:
6398 self.setStartDate(entries[0].getStartDate(),0,0)
6399 self.setEndDate(sch.calculateEndDate(), check=0)
6401 def recalculateTimes( self, type, diff ):
6403 recalculate and reschedule the contributions of the session slot with a time "diff" of separation.
6405 if type=="duration":
6406 entries = self.getSchedule().getEntries()[:]
6408 while i<len(entries):
6409 entry=entries[i]
6410 if i+1 == len(entries):
6411 dur=self.getEndDate()-entry.getStartDate()
6412 else:
6413 nextentry=entries[i+1]
6414 dur=nextentry.getStartDate()-entry.getStartDate()-diff
6415 if dur<timedelta(0):
6416 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())
6417 entry.setDuration(dur=dur)
6418 i+=1
6419 if len(entries) != 0 and self.getEndDate() < entry.getEndDate():
6420 self.setEndDate(entry.getEndDate(),2)
6421 elif type=="startingTime":
6422 st = self.getStartDate()
6423 entries = self.getSchedule().getEntries()[:]
6424 for entry in entries:
6425 entry.setStartDate(st,0,0)
6426 # add diff to last item end date if and only if the item is
6427 # not a break
6428 #if not isinstance(entry, BreakTimeSchEntry):
6429 # st=entry.getEndDate()+diff
6430 #else:
6431 # st=entry.getEndDate()
6432 st=entry.getEndDate()+diff
6433 if len(entries) != 0 and self.getEndDate() < st:
6434 self.setEndDate(st,2)
6436 def setValues(self,data,check=2, moveEntriesBelow=0):
6437 """check parameter:
6438 0: no check at all
6439 1: check and raise error in case of problem
6440 2: check and adapt the owner dates
6443 # In order to move the entries below, it is needed to know the diff (we have to move them)
6444 # and the list of entries to move. It's is needed to take those datas in advance because they
6445 # are going to be modified before the moving.
6446 if moveEntriesBelow == 1:
6447 oldStartDate=copy.copy(self.getStartDate())
6448 oldDuration=copy.copy(self.getDuration())
6449 i=self.getConfSchEntry().getSchedule().getEntries().index(self.getConfSchEntry())+1
6450 entriesList = self.getConfSchEntry().getSchedule().getEntries()[i:]
6451 self.title=data.get("title", "NO TITLE ASSIGNED")
6452 # Do we move all entries in the slot
6453 move = int(data.get("move",0))
6455 if "locationName" in data:
6456 loc = self.getOwnLocation()
6457 if not loc:
6458 loc = CustomLocation()
6459 self.setLocation( loc )
6460 loc.setName( data["locationName"] )
6461 loc.setAddress( data.get("locationAddress", "") )
6462 else:
6463 self.setLocation( None )
6465 if "roomName" in data:
6466 room = self.getOwnRoom()
6467 if not room:
6468 room = CustomRoom()
6469 self.setRoom( room )
6470 room.setName( data["roomName"] )
6471 else:
6472 self.setRoom( None )
6473 sDate = eDate = None
6474 confTZ = self.getOwner().getConference().getTimezone()
6475 if data.get("sDate",None) is not None:
6476 sd = data.get("sDate")
6477 sDate = timezone(confTZ).localize(datetime(sd.year,sd.month,sd.day,sd.hour,sd.minute))
6478 elif data.get("sYear","")!="" and data.get("sMonth","")!="" and \
6479 data.get("sDay","")!="" and data.get("sHour","")!="" and \
6480 data.get("sMinute","")!="":
6481 sDate = timezone(confTZ).localize(datetime(int(data["sYear"]),int(data["sMonth"]),
6482 int(data["sDay"]),int(data["sHour"]),
6483 int(data["sMinute"])))
6484 if data.get("eDate",None) is not None:
6485 ed = data.get("eDate")
6486 eDate = timezone(confTZ).localize(datetime(ed.year,ed.month,ed.day,ed.hour,ed.minute))
6487 elif data.get("eYear","")!="" and data.get("eMonth","")!="" and \
6488 data.get("eDay","")!="" and data.get("eHour","")!="" and \
6489 data.get("eMinute","")!="":
6490 eDate = timezone(confTZ).localize(datetime(int(data["eYear"]),int(data["eMonth"]),
6491 int(data["eDay"]),int(data["eHour"]),
6492 int(data["eMinute"])))
6493 if sDate != None and eDate != None:
6494 sDateUTC = sDate.astimezone(timezone('UTC'))
6495 eDateUTC = eDate.astimezone(timezone('UTC'))
6496 self.setDates(sDateUTC,eDateUTC,check,moveEntries=move)
6497 elif sDate != None:
6498 sDateUTC = sDate.astimezone(timezone('UTC'))
6499 self.setStartDate(sDateUTC,check,moveEntries=move)
6500 if data.get("durHours","")!="" and data.get("durMins","")!="":
6501 self.setDuration(hours=data["durHours"],minutes=data["durMins"],check=check)
6502 if data.get("contribDurHours","")!="" and data.get("contribDurMins","")!="":
6503 self.setContribDuration(int(data["contribDurHours"]),int(data["contribDurMins"]))
6504 elif data.get("contribDuration","")!="":
6505 self.setContribDuration(dur=data.get("contribDuration"))
6506 else:
6507 self.setContribDuration(None,None)
6508 conveners = data.get("conveners",None)
6509 if conveners is not None:
6510 self.clearConvenerList()
6511 for conv in conveners:
6512 sc = SlotChair()
6513 sc.setTitle(conv.getTitle())
6514 sc.setFirstName(conv.getFirstName())
6515 sc.setFamilyName(conv.getFamilyName())
6516 sc.setAffiliation(conv.getAffiliation())
6517 sc.setEmail(conv.getEmail())
6518 self.addConvener(sc)
6519 if moveEntriesBelow == 1:
6520 diff = (self.getStartDate() - oldStartDate) + (self.getDuration() - oldDuration)
6521 self.getSchedule().moveEntriesBelow(diff, entriesList)
6522 self._checkInnerSchedule()
6523 self.notifyModification()
6525 def _checkInnerSchedule( self ):
6526 self.getSchedule().checkSanity()
6528 def setContribDuration(self, hour=0, min=0, dur=None):
6529 self._contributionDuration = None
6530 if dur is not None:
6531 self._contributionDuration=dur
6532 elif hour != None and min != None:
6533 self._contributionDuration = timedelta(hours=hour,minutes=min)
6535 def getContribDuration(self):
6537 Duration by default for contributions within the slots.
6539 try:
6540 if self._contributionDuration:
6541 pass
6542 except AttributeError, e:
6543 self._contributionDuration = None
6544 return self._contributionDuration
6546 def notifyModification( self, cleanCache = True, cleanCacheEntries = False):
6547 self.getSession().notifyModification(cleanCache = False)
6548 if cleanCache:
6549 self.cleanCache(cleanCacheEntries)
6550 self._p_changed = 1
6552 def cleanCache(self, cleanCacheEntries = False):
6553 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
6554 ScheduleToJson.cleanCache(self)
6555 ContextManager.set('clean%s'%self.getUniqueId(), True)
6556 if cleanCacheEntries:
6557 for entry in self.getSchedule().getEntries():
6558 entry.getOwner().cleanCache(cleanConference = False)
6560 def getLocator( self ):
6561 l=self.getSession().getLocator()
6562 l["slotId"]=self.getId()
6563 return l
6565 def getConference( self ):
6566 return self.getSession().getConference()
6568 def getSession(self):
6569 return self.session
6571 def getOwner(self):
6572 return self.session
6574 def getContributionList(self):
6575 return [e.getOwner() for e in ifilter(lambda e: isinstance(e, ContribSchEntry),
6576 self.getSchedule().getEntries())]
6578 def _setSchedule(self, klass):
6579 old_sch = self.getSchedule()
6580 self._schedule = klass(self)
6581 #after removing old entries, one could try to fit them into the new
6582 # schedule, but there are several things to consider which are left
6583 # for later implementation (breaks, entries not fitting in the
6584 # slots,...)
6585 while len(old_sch.getEntries()) > 0:
6586 entry = old_sch.getEntries()[0]
6587 old_sch.removeEntry(entry)
6588 self.notifyModification()
6590 def getSchedule(self):
6591 return self._schedule
6593 def getMasterSchedule( self ):
6594 return self.getOwner().getSchedule()
6596 def getConfSchEntry( self ):
6597 try:
6598 if self._confSchEntry:
6599 pass
6600 except AttributeError:
6601 self._confSchEntry=LinkedTimeSchEntry(self)
6602 return self._confSchEntry
6604 def getSessionSchEntry( self ):
6605 try:
6606 if self._sessionSchEntry:
6607 pass
6608 except AttributeError:
6609 self._sessionSchEntry=self._schEntry
6610 return self._sessionSchEntry
6612 def setId( self, newId ):
6613 self.id=str(newId)
6614 self.notifyModification()
6616 def getId( self ):
6617 return self.id
6619 def getUniqueId( self ):
6620 """Returns (string) the unique identiffier of the item.
6621 Used mainly in the web session access key table"""
6622 return "%sl%s" % (self.getSession().getUniqueId(),self.id)
6624 def setTitle( self, newTitle ):
6625 self.title=newTitle
6626 self.notifyModification()
6628 def getTitle( self ):
6629 try:
6630 if self.title:
6631 pass
6632 except AttributeError,e:
6633 self.title=""
6634 return self.title
6636 def getFullTitle( self ):
6637 return self.getSession().getTitle() + (": " + self.getTitle() if self.getTitle() else "")
6639 def getName(self):
6640 return "slot %s"%self.getId()
6642 def getDescription(self):
6643 return self.getSession().getDescription()
6645 def setDates(self, sDate, eDate, check=2, moveEntries=0):
6646 """check parameter:
6647 0: no check at all
6648 1: check and raise error in case of problem
6649 2: check and adapt the owner dates"""
6651 if sDate > eDate:
6652 raise FormValuesError(_("End date cannot be prior to Start date"), _("Slot"))
6654 self.setStartDate(sDate, check, moveEntries, checkDuration=False)
6655 self.setDuration(0, 0, 0, eDate-sDate, check)
6656 self.notifyModification()
6658 def getEntries(self):
6659 entriesList = self.getSchedule().getEntries()
6660 return entriesList
6662 def move(self, sDate):
6663 diff=sDate-self.startDate
6664 self.startDate = sDate
6665 for slotEntry in self.getSchedule().getEntries():
6666 if isinstance(slotEntry, BreakTimeSchEntry):
6667 slotEntry.startDate = slotEntry.getStartDate() + diff
6668 else:
6669 se = slotEntry.getOwner()
6670 se.startDate = se.getStartDate() + diff
6671 self.getSchedule().reSchedule()
6673 def verifyStartDate(self, sDate,check=2):
6674 """check parameter:
6675 0: no check at all
6676 1: check and raise error in case of problem
6677 2: check and adapt the owner dates"""
6679 tz = timezone(self.getConference().getTimezone())
6681 if sDate < self.getSession().getStartDate():
6682 if check == 1:
6683 raise ParentTimingError(_("The slot \"%s\" cannot start (%s) before its parent session starts (%s)")%\
6684 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
6685 self.getSession().getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
6686 _("Slot"))
6687 elif check == 2:
6688 self.getSession().setStartDate(sDate, check, 0)
6690 def setStartDate(self,sDate,check=2,moveEntries=0,checkDuration=True):
6691 """check parameter:
6692 0: no check at all
6693 1: check and raise error in case of problem
6694 2: check and adapt the owner dates"""
6695 if sDate is None:
6696 return
6697 if not sDate.tzname():
6698 raise MaKaCError("date should be timezone aware")
6699 if check != 0:
6700 #If not using .fit() at the end of this method, comment it out
6701 #if self.getSession().getStartDate() > sDate:
6702 # self.getSession().duration += self.getSession().getStartDate() - sDate
6703 self.verifyStartDate(sDate,check)
6705 # calculate the difference betwwen old and new date
6706 difference = None
6707 if self.startDate is not None:
6708 difference = sDate - self.getStartDate()
6710 self.startDate=copy.copy(sDate)
6712 if difference != None and difference != timedelta(0) and moveEntries:
6713 ContextManager.get('autoOps').append((self, "ENTRIES_MOVED",
6714 self, sDate.astimezone(timezone(self.getTimezone()))))
6715 self.getSchedule().moveEntriesBelow(difference,self.getSchedule().getEntries()[:])
6717 if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSession().getStartDate() != sDate:
6718 self.getSession().setStartDate(sDate, check, 0)
6719 if check != 0 and self.getSession() and checkDuration:
6720 self.verifyDuration(self.getDuration(), check=check)
6722 # synchronize with other timetables
6723 self.getSessionSchEntry().synchro()
6724 self.getConfSchEntry().synchro()
6725 self.getSession().fit()
6726 self.notifyModification()
6728 def setEndDate(self,eDate,check=2):
6729 if not eDate.tzname():
6730 raise MaKaCError("date should be timezone aware")
6731 if check != 0:
6732 self.verifyDuration(eDate-self.startDate, check)
6733 self.setDuration(dur=eDate-self.startDate,check=check)
6734 if self.getConference() and not self.getConference().getEnableSessionSlots() and self.getSession().getEndDate() != eDate:
6735 self.getSession().setEndDate(eDate, check)
6736 self.getSession().fit()
6737 self.notifyModification()
6739 def getStartDate( self ):
6740 return self.startDate
6742 def getAdjustedStartDate(self,tz=None):
6743 if not tz:
6744 tz = self.getConference().getTimezone()
6745 if tz not in all_timezones:
6746 tz = 'UTC'
6747 return self.startDate.astimezone(timezone(tz))
6749 def getEndDate( self ):
6750 if self.startDate is None:
6751 return None
6752 return self.startDate+self.duration
6754 def getAdjustedEndDate( self, tz=None ):
6755 if not tz:
6756 tz = self.getConference().getTimezone()
6757 if tz not in all_timezones:
6758 tz = 'UTC'
6759 if self.getEndDate():
6760 return self.getEndDate().astimezone(timezone(tz))
6761 return None
6763 def getDuration( self ):
6764 return self.duration
6766 def isMoreThanDay(self):
6767 if self.getDuration() >= timedelta(days=1):
6768 return True
6769 return False
6771 def verifyDuration(self, dur, check=1):
6772 """check parameter:
6773 0: no check at all
6774 1: check and raise error in case of problem
6775 2: check and adapt the owner dates"""
6777 tz = timezone(self.getConference().getTimezone())
6778 if dur <= timedelta(0):
6779 raise FormValuesError(_("The duration cannot be less than zero"), _("Slot"))
6780 if dur.days > 1:
6781 raise FormValuesError(_("The duration cannot be more than one day"), _("Slot"))
6782 if self.startDate is not None:
6783 sessionStartDate = self.getSession().getStartDate()
6784 sessionEndDate = self.getSession().getEndDate()
6785 # end date has to be between the session dates
6786 eDate = self.startDate + dur
6787 if eDate > sessionEndDate:
6788 if check==1:
6789 raise EntryTimingError(_("The session slot cannot end (%s) after its parent session (%s)") \
6790 % (eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
6791 sessionEndDate.astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
6792 _("Slot"))
6793 elif check==2:
6794 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
6795 self.getSession(), eDate.astimezone(tz)))
6796 self.getSession().setEndDate(eDate,check)
6797 if eDate.astimezone(tz).date() > self.startDate.astimezone(tz).date():
6798 raise TimingError( _("The time slot must end on the same day it has started"), _("Slot"))
6799 # do not modify if slot entries will be affected
6800 sch = self.getSchedule()
6801 entries = sch.getEntries()
6802 if entries != []:
6803 if eDate < sch.calculateEndDate():
6804 raise TimingError(_("The session slot cannot end at (%s) because there is a contribution (%s) ending after that time. ")%\
6805 (eDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
6806 sch.calculateEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
6807 _("Slot"))
6809 def setDuration(self, days=0,hours=0,minutes=0,dur=0,check=1):
6810 """check parameter:
6811 0: no check at all
6812 1: check and raise error in case of problem
6813 2: check and adapt the owner dates"""
6815 if dur==0:
6816 dur = timedelta(days=int(days),hours=int(hours),minutes=int(minutes))
6817 if dur==0 and check==2:
6818 ContextManager.get('autoOps').append((self, "DURATION_SET",
6819 self, 1))
6820 dur = timedelta(minutes=1)
6821 if dur > timedelta(days=1) and check==2:
6822 pass#dur = timedelta(days=1)
6823 if check != 0:
6824 self.verifyDuration(dur, check)
6825 self.duration = dur
6826 self.getSessionSchEntry().synchro()
6827 self.getConfSchEntry().synchro()
6828 self.getSession().fit()
6829 self.notifyModification()
6831 def getLocationParent( self ):
6833 Returns the object from which the room/location
6834 information should be inherited
6836 return self.session.conference
6838 def delete(self):
6839 signals.event.session_slot_deleted.send(self)
6840 self.getSchedule().clear()
6841 if self.getSession() is not None:
6842 self.getSession().removeSlot(self)
6843 self.session=None
6844 TrashCanManager().add(self)
6846 def recover(self):
6847 TrashCanManager().remove(self)
6849 def getAccessController( self ):
6850 return self.getSession().getAccessController()
6852 def canAccess(self,aw):
6853 return self.getSession().canAccess(aw)
6855 def canView(self,aw):
6856 return self.getSession().canView(aw)
6858 def isProtected(self):
6859 return self.getSession().isProtected()
6861 def getAccessKey( self ):
6862 return self.getSession().getAccessKey()
6864 def setScheduleType(self,id):
6865 id=str(id).strip().lower()
6866 currentId=SlotSchTypeFactory.getId(self.getSchedule())
6867 if id not in SlotSchTypeFactory.getIdList() or id==currentId:
6868 return
6869 self._setSchedule(SlotSchTypeFactory.getScheduleKlass(id))
6871 def getConvenerList(self):
6872 try:
6873 if self._conveners:
6874 pass
6875 except AttributeError:
6876 self._conveners = []
6877 if self._conveners == []:
6878 return self.getSession().getConvenerList()
6879 return self._conveners
6881 def addConvener(self,newConv):
6882 if newConv in self._conveners:
6883 return
6884 try:
6885 if self._convenerGen:
6886 pass
6887 except AttributeError:
6888 self._convenerGen=Counter()
6889 id = newConv.getId()
6890 if id == "":
6891 id=int(self._convenerGen.newCount())
6892 newConv.includeInSlot(self,id)
6893 self._conveners.append(newConv)
6894 self.notifyModification()
6896 def removeConvener(self,conv):
6897 if conv not in self._conveners:
6898 return
6899 self._conveners.remove(conv)
6900 conv.delete()
6901 self.notifyModification()
6903 def recoverConvener(self, con):
6904 self.addConvener(con)
6905 con.recover()
6907 def getConvenerById(self,id):
6908 id=int(id)
6909 for conv in self._conveners:
6910 if conv.getId()==id:
6911 return conv
6912 return None
6914 def getOwnConvenerList(self):
6915 try:
6916 if self._conveners:
6917 pass
6918 except AttributeError:
6919 self._conveners = []
6920 return self._conveners
6922 def clearConvenerList(self):
6923 while len(self.getOwnConvenerList()) > 0:
6924 self._conveners.pop()
6925 self.notifyModification()
6927 def getColor(self):
6928 res=""
6929 if self.getSession() is not None:
6930 res=self.getSession().getColor()
6931 return res
6933 def getTextColor(self):
6934 res=""
6935 if self.getSession() is not None:
6936 res=self.getSession().getTextColor()
6937 return res
6939 def getAllMaterialList(self, sort=True):
6940 return self.getSession().getAllMaterialList(sort=sort)
6942 def getRecursiveAllowedToAccessList(self):
6943 return self.getSession().getRecursiveAllowedToAccessList()
6945 def canModify(self, aw_or_user):
6946 return self.getSession().canModify(aw_or_user)
6949 class ContributionParticipation(Persistent, Fossilizable):
6951 fossilizes(IContributionParticipationFossil, IContributionParticipationMinimalFossil,\
6952 IContributionParticipationTTDisplayFossil,\
6953 IContributionParticipationTTMgmtFossil)
6955 def __init__( self ):
6956 self._contrib = None
6957 self._id = ""
6958 self._firstName = ""
6959 self._surName = ""
6960 self._email = ""
6961 self._affiliation = ""
6962 self._address = ""
6963 self._phone = ""
6964 self._title = ""
6965 self._fax = ""
6967 def _notifyModification( self ):
6968 if self._contrib != None:
6969 self._contrib.notifyModification()
6971 def setValues(self, data):
6972 self.setFirstName(data.get("firstName", ""))
6973 self.setFamilyName(data.get("familyName",""))
6974 self.setAffiliation(data.get("affilation",""))
6975 self.setAddress(data.get("address",""))
6976 self.setEmail(data.get("email",""))
6977 self.setFax(data.get("fax",""))
6978 self.setTitle(data.get("title",""))
6979 self.setPhone(data.get("phone",""))
6980 self._notifyModification()
6982 def getValues(self):
6983 data={}
6984 data["firstName"]=self.getFirstName()
6985 data["familyName"]=self.getFamilyName()
6986 data["affilation"]=self.getAffiliation()
6987 data["address"]=self.getAddress()
6988 data["email"]=self.getEmail()
6989 data["fax"]=self.getFax()
6990 data["title"]=self.getTitle()
6991 data["phone"]=self.getPhone()
6992 return data
6994 def clone(self):
6995 part = ContributionParticipation()
6996 part.setValues(self.getValues())
6997 return part
6999 def setDataFromAvatar(self,av):
7000 # av is an Avatar object.
7001 if av is None:
7002 return
7003 self.setFirstName(av.getName())
7004 self.setFamilyName(av.getSurName())
7005 self.setEmail(av.getEmail())
7006 self.setAffiliation(av.getOrganisation())
7007 self.setAddress(av.getAddress())
7008 self.setPhone(av.getTelephone())
7009 self.setTitle(av.getTitle())
7010 self.setFax(av.getFax())
7011 self._notifyModification()
7013 def setDataFromOtherCP(self,cp):
7014 # cp is a ContributionParticipation object.
7015 if cp is None:
7016 return
7017 self.setFirstName(cp.getFirstName())
7018 self.setFamilyName(cp.getFamilyName())
7019 self.setEmail(cp.getEmail())
7020 self.setAffiliation(cp.getAffiliation())
7021 self.setAddress(cp.getAddress())
7022 self.setPhone(cp.getPhone())
7023 self.setTitle(cp.getTitle())
7024 self.setFax(cp.getFax())
7025 self._notifyModification()
7027 def includeInContribution( self, contrib, id ):
7028 if self.getContribution() == contrib and self.getId()==id.strip():
7029 return
7030 self._contrib = contrib
7031 self._id = id
7033 def delete( self ):
7034 self._contrib = None
7035 TrashCanManager().add(self)
7037 def recover(self):
7038 TrashCanManager().remove(self)
7040 def setId(self, newId):
7041 self._id = newId
7043 def getId( self ):
7044 return self._id
7046 def getContribution( self ):
7047 return self._contrib
7049 def getConference(self):
7050 return self._contrib.getConference()
7052 def getLocator(self):
7053 if self.getContribution() is None:
7054 return None
7055 loc=self.getContribution().getLocator()
7056 loc["authorId"]=self.getId()
7057 return loc
7059 def _unindex(self):
7060 contrib=self.getContribution()
7061 if contrib is not None:
7062 conf=contrib.getConference()
7063 if conf is not None:
7064 conf.unindexAuthor(self)
7065 conf.unindexSpeaker(self)
7067 def _index(self):
7068 contrib=self.getContribution()
7069 if contrib is not None:
7070 conf=contrib.getConference()
7071 if conf is not None:
7072 conf.indexAuthor(self)
7073 conf.indexSpeaker(self)
7075 @Updates ('MaKaC.conference.ContributionParticipation', 'firstName')
7076 def setFirstName( self, newName ):
7077 tmp=newName.strip()
7078 if tmp==self._firstName:
7079 return
7080 self._unindex()
7081 self._firstName=tmp
7082 self._index()
7083 self._notifyModification()
7085 def getFirstName( self ):
7086 return self._firstName
7088 def getName( self ):
7089 return self._firstName
7092 @Updates ('MaKaC.conference.ContributionParticipation', 'familyName')
7093 def setFamilyName( self, newName ):
7094 tmp=newName.strip()
7095 if tmp==self._surName:
7096 return
7097 self._unindex()
7098 self._surName=tmp
7099 self._index()
7100 self._notifyModification()
7102 def getFamilyName( self ):
7103 return self._surName
7105 def getSurName( self ):
7106 return self._surName
7109 @Updates ('MaKaC.conference.ContributionParticipation', 'email')
7110 def setEmail( self, newMail ):
7111 tmp=newMail.strip()
7112 if tmp==self._email:
7113 return
7114 self._unindex()
7115 self._email=newMail.strip()
7116 self._index()
7117 self._notifyModification()
7119 def getEmail( self ):
7120 return self._email
7122 @Updates ('MaKaC.conference.ContributionParticipation', 'affiliation')
7123 def setAffiliation( self, newAffil ):
7124 self._affiliation = newAffil.strip()
7125 self._notifyModification()
7127 def getAffiliation( self ):
7128 if self._affiliation.lower() == "unknown":
7129 return ""
7130 return self._affiliation
7132 @Updates ('MaKaC.conference.ContributionParticipation', 'address')
7133 def setAddress( self, newAddr ):
7134 self._address = newAddr.strip()
7135 self._notifyModification()
7137 def getAddress( self ):
7138 return self._address
7140 @Updates('MaKaC.conference.ContributionParticipation', 'phone')
7141 def setPhone( self, newPhone ):
7142 self._phone = newPhone.strip()
7143 self._notifyModification()
7145 def getPhone( self ):
7146 return self._phone
7148 @Updates ('MaKaC.conference.ContributionParticipation', 'title')
7149 def setTitle( self, newTitle ):
7150 self._title = newTitle.strip()
7151 self._notifyModification()
7153 def getTitle( self ):
7154 return self._title
7156 @Updates ('MaKaC.conference.ContributionParticipation', 'fax')
7157 def setFax( self, newFax ):
7158 self._fax = newFax.strip()
7159 self._notifyModification()
7161 def getFax( self ):
7162 try:
7163 if self._fax:
7164 pass
7165 except AttributeError:
7166 self._fax=""
7167 return self._fax
7169 def getDirectFullName( self ):
7170 res = self.getDirectFullNameNoTitle()
7171 if self.getTitle() != "":
7172 res = "%s %s"%( self.getTitle(), res )
7173 return res
7175 def getDirectFullNameNoTitle(self, upper=True):
7176 familyName = safe_upper(self.getFamilyName()) if upper else self.getFamilyName()
7177 return "{0} {1}".format(self.getFirstName(), familyName).strip()
7179 def getFullName(self):
7180 res = self.getFullNameNoTitle()
7181 if self.getTitle():
7182 res = "%s %s" % (self.getTitle(), res)
7183 return res
7185 def getFullNameNoTitle(self):
7186 res = safe_upper(self.getFamilyName())
7187 if self.getFirstName():
7188 if res.strip():
7189 res = "%s, %s" % (res, self.getFirstName())
7190 else:
7191 res = self.getFirstName()
7192 return res
7194 def getAbrName(self):
7195 res = self.getFamilyName()
7196 if self.getFirstName():
7197 if res:
7198 res = "%s, " % res
7199 res = "%s%s." % (res, safe_upper(self.getFirstName()[0]))
7200 return res
7202 def isSubmitter(self):
7203 if self.getContribution() is None:
7204 return False
7205 return self.getContribution().canUserSubmit(self)
7207 def isPendingSubmitter(self):
7208 if self.getContribution() is None:
7209 return False
7210 if self.getContribution().getConference() is None:
7211 return False
7212 return self.getContribution().getConference().getPendingQueuesMgr().isPendingSubmitter(self)
7214 def isInAuthorList(self):
7215 # Sometimes authors are not in the author index for an unknown reason.
7216 # In this case we don't want to link to the author page since opening it would fail
7217 return self.getConference().getAuthorIndex().getByAuthorObj(self) is not None
7219 @staticmethod
7220 def _cmpFamilyName(cp1, cp2):
7221 o1 = "%s %s"%(cp1.getFamilyName(), cp1.getFirstName())
7222 o2 = "%s %s"%(cp2.getFamilyName(), cp2.getFirstName())
7223 o1=o1.lower().strip()
7224 o2=o2.lower().strip()
7225 return cmp( o1, o2 )
7228 class AuthorIndex(Persistent):
7230 def __init__(self):
7231 self._idx=OOBTree()
7233 def _getKey(self,author):
7234 k = "%s %s %s"%(author.getFamilyName().lower(),author.getFirstName().lower(),author.getEmail().lower())
7235 return k.strip()
7237 def index(self,author):
7238 key=self._getKey(author)
7239 if not self._idx.has_key(key):
7240 self._idx[key]=[]
7241 l = self._idx[key]
7242 l.append(author)
7243 self._idx[key] = l
7244 self.notifyModification()
7246 def unindex(self,author):
7247 key=self._getKey(author)
7248 if self._idx.has_key(key):
7249 if author in self._idx[key]:
7250 l = self._idx[key]
7251 l.remove(author)
7252 self._idx[key] = l
7253 if len(self._idx[key])<=0:
7254 del self._idx[key]
7255 self.notifyModification()
7257 def getParticipations(self):
7258 return self._idx.values()
7260 def getById(self, id):
7261 return self._idx.get(id,None)
7263 def getByAuthorObj(self, auth):
7264 return self.getById(self._getKey(auth))
7266 def getParticipationKeys(self):
7267 return self._idx.keys()
7269 def notifyModification(self):
7270 self._idx._p_changed = 1
7271 self._p_changed = 1
7273 def iteritems(self):
7274 return self._idx.iteritems()
7276 def match(self, criteria, exact=0):
7277 self._options = ['organisation', 'surName', 'name', 'email']
7278 l = []
7279 for item in self.getParticipations():
7280 if len(item)>0:
7281 ok = []
7282 for f,v in criteria.items():
7283 if f == 'organisation' and v != '':
7284 if (exact == 0 and item[0].getAffiliation().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getAffiliation().lower() != v.lower()):
7285 ok.append(False)
7286 else:
7287 ok.append(True)
7288 if f == 'surName' and v!= '':
7289 if (exact == 0 and item[0].getSurName().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getSurName().lower() != v.lower()):
7290 ok.append(False)
7291 else:
7292 ok.append(True)
7293 if f == 'name' and v!= '':
7294 if (exact == 0 and item[0].getName().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getName().lower() != v.lower()):
7295 ok.append(False)
7296 else:
7297 ok.append(True)
7298 if f == 'email' and v!= '':
7299 if (exact == 0 and item[0].getEmail().lower().find(v.lower()) == -1) or (exact == 1 and item[0].getEmail().lower() != v.lower()):
7300 ok.append(False)
7301 else:
7302 ok.append(True)
7303 if len(ok) > 0 and not False in ok:
7304 l.append(item[0])
7305 return l
7307 class _AuthIdx(Persistent):
7309 def __init__(self,conf):
7310 self._conf=conf
7311 self._idx=OOBTree()
7313 def _getKey(self,auth):
7314 return "%s %s"%(auth.getFamilyName().lower(),auth.getFirstName().lower())
7316 def index(self,auth):
7317 if auth.getContribution() is None:
7318 raise MaKaCError( _("Cannot index an author of a contribution which has not been included in a Conference"), _("Author Index"))
7319 if auth.getContribution().getConference()!=self._conf:
7320 raise MaKaCError( _("cannot index an author of a contribution which does not belong to this Conference"), _("Author Index"))
7321 key=self._getKey(auth)
7322 contribId=str(auth.getContribution().getId())
7323 if not self._idx.has_key(key):
7324 self._idx[key]=OIBTree()
7325 if not self._idx[key].has_key(contribId):
7326 self._idx[key][contribId]=0
7327 self._idx[key][contribId]+=1
7329 def unindex(self,auth):
7330 if auth.getContribution() is None:
7331 raise MaKaCError( _("Cannot unindex an author of a contribution which is not included in a conference"), _("Author Index"))
7332 if auth.getContribution().getConference()!=self._conf:
7333 raise MaKaCError( _("Cannot unindex an author of a contribution which does not belong to this conference"), _("Author Index"))
7334 key=self._getKey(auth)
7335 if not self._idx.has_key(key):
7336 return
7337 contribId=str(auth.getContribution().getId())
7338 self._idx[key][contribId]-=1
7339 if self._idx[key][contribId]<=0:
7340 del self._idx[key][contribId]
7341 if len(self._idx[key])<=0:
7342 del self._idx[key]
7344 def match(self,query):
7345 query=query.lower().strip()
7346 res=OISet()
7347 for k in self._idx.keys():
7348 if k.find(query)!=-1:
7349 res=union(res,self._idx[k])
7350 return res
7353 class _PrimAuthIdx(_AuthIdx):
7355 def __init__(self,conf):
7356 _AuthIdx.__init__(self,conf)
7357 for contrib in self._conf.getContributionList():
7358 for auth in contrib.getPrimaryAuthorList():
7359 self.index(auth)
7362 class Contribution(CommonObjectBase, Locatable):
7363 """This class implements a conference contribution, being the concrete
7364 contributes of the conference participants. The class contains
7365 necessary attributes to store contribution basic meta data and provides
7366 the useful operations to access and manage them. A contribution can be
7367 attached either to a session or to a conference.
7370 fossilizes(IContributionFossil, IContributionWithSpeakersFossil, IContributionWithSubContribsFossil)
7372 def __init__(self, **contribData):
7373 self.parent = None
7374 self._session = None
7375 self.id = ""
7376 self.title = ""
7377 self._fields = {}
7378 self.description = ""
7379 self.startDate = None
7380 self.duration = timedelta(0)
7381 self.speakers = []
7382 self.speakerText = ""
7383 self.place = None
7384 self.room = None
7385 self._boardNumber = ""
7386 self._resetSchEntry()
7387 self.__ac = AccessController(self)
7388 self.materials = {}
7389 self.__materialGenerator = Counter()
7390 self._subConts = []
7391 self.__subContGenerator = Counter()
7392 self.paper = None
7393 self.slides = None
7394 self.video = None
7395 self.poster = None
7396 self.reviewing = None
7397 self._authorGen = Counter()
7398 self._authors = OOBTree()
7399 self._primaryAuthors = []
7400 self._coAuthors = []
7401 self._speakers = []
7402 self._track = None
7403 self._type = None
7404 self._status = ContribStatusNotSch(self)
7405 #List of allowed users to submit material
7406 self._submitters = []
7407 self._submittersEmail = []
7408 self._modificationDS = nowutc()
7409 self._keywords = ""
7410 self._reviewManager = ReviewManager(self)
7412 def __cmp__(self, other):
7413 if type(self) is not type(other):
7414 # This is actually dangerous and the ZODB manual says not to do this
7415 # because it relies on memory order. However, this branch should never
7416 # be taken anyway since we do not store different types in the same set
7417 # or use them as keys.
7418 return cmp(hash(self), hash(other))
7419 if self.getConference() == other.getConference():
7420 return cmp(self.getId(), other.getId())
7421 return cmp(self.getConference(), other.getConference())
7423 @return_ascii
7424 def __repr__(self):
7425 parent_id = self.parent.getId() if self.parent else None
7426 return u'<Contribution({}, {}, {})>'.format(self.getId(), to_unicode(self.getTitle()), parent_id)
7428 @property
7429 @memoize_request
7430 def note(self):
7431 from indico.modules.events.notes.models.notes import EventNote
7432 return EventNote.get_for_linked_object(self)
7434 def getVerboseType(self):
7435 return 'Contribution'
7437 def getTimezone(self):
7438 return self.getConference().getTimezone()
7440 def getReviewManager(self):
7441 if not hasattr(self, "_reviewManager"):
7442 self._reviewManager = ReviewManager(self)
7443 return self._reviewManager
7445 def updateNonInheritingChildren(self, elem, delete=False):
7446 self.getAccessController().updateNonInheritingChildren(elem, delete)
7447 self.notify_protection_to_owner(elem, delete)
7449 def notify_protection_to_owner(self, elem, delete=False):
7450 self.getOwner().updateNonInheritingChildren(elem, delete)
7452 def getKeywords(self):
7453 try:
7454 return self._keywords
7455 except:
7456 self._keywords = ""
7457 return ""
7459 def setKeywords(self, keywords):
7460 if type(keywords) is list:
7461 self._keywords = keywords[0]
7462 else:
7463 self._keywords = keywords
7464 self.notifyModification(cleanCache=False)
7466 def getFields(self, valueonly=False):
7467 try:
7468 if self._fields:
7469 pass
7470 except AttributeError:
7471 self._fields = {}
7472 if not valueonly:
7473 return self._fields
7474 else:
7475 return dict((k, v.value if isinstance(v, AbstractFieldContent) else v) for k, v in self._fields.iteritems())
7477 def removeField(self, field):
7478 if field in self.getFields():
7479 del self.getFields()[field]
7480 self.notifyModification()
7482 def setField(self, fid, v):
7483 if isinstance(v, AbstractFieldContent):
7484 v = v.value
7485 try:
7486 self.getFields()[fid].value = v
7488 # `AttritbuteError` may happen if the field is not yet an AbstractFieldContent
7489 # (lazy migration)
7490 # `KeyError` means that the attribute doesn't exist in the contrib, in which
7491 # case it should be created anyway
7492 except (AttributeError, KeyError):
7493 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7494 for f in afm.getFields():
7495 if f.getId() == fid:
7496 self.getFields()[fid] = AbstractFieldContent(f, v)
7497 break
7498 self.notifyModification()
7500 def getField(self, field):
7501 if field in self.getFields():
7502 value = self.getFields()[field]
7503 if type(value) is list:
7504 return "".join(value)
7505 elif value is None:
7506 return ""
7507 else:
7508 return value
7509 else:
7510 return ""
7512 def getLogInfo(self):
7513 data = {}
7514 data["subject"] = self.getTitle()
7515 data["id"] = self.id
7516 data["title"] = self.title
7517 data["parent title"] = self.parent.getTitle()
7518 if self._session is not None:
7519 data["session title"] = self._session.getTitle()
7520 data["description"] = self.description
7521 if self.getConference():
7522 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7523 for f in afm.getFields():
7524 id = f.getId()
7525 field = self.getField(id)
7526 if field.value:
7527 data['Abstract field {}'.format(field.field._caption)] = field.value
7528 data["start date"] = format_datetime(self.startDate, locale='en_GB', timezone=self.getConference().timezone)
7529 data["duration"] = format_human_timedelta(self.duration)
7530 if self._track is not None:
7531 data["track"] = self._track.getTitle()
7532 if self._type is not None:
7533 data["type"] = self._type
7534 data["speaker text"] = self.speakerText
7535 if self.place is not None:
7536 data["place"] = self.place.getName()
7537 if self.room is not None:
7538 data["room"] = self.room.getName()
7539 data["board number"] = self._boardNumber
7540 for sc in self.getSubContributionList():
7541 data["subcontribution %s" % sc.getId()] = sc.getTitle()
7542 for pa in self._primaryAuthors:
7543 data["primary author %s" % pa.getId()] = pa.getFullName()
7544 for ca in self._coAuthors:
7545 data["co-author %s" % ca.getId()] = ca.getFullName()
7546 for sp in self._speakers:
7547 data["speaker %s" % sp.getId()] = sp.getFullName()
7548 for s in self.getSubmitterList():
7549 if isinstance(s, AvatarUserWrapper):
7550 data["submitter"] = s.getFullName()
7551 else:
7552 data["submitter"] = s.getName()
7553 return data
7555 def setValues(self, data, check=2, moveEntriesBelow=0):
7556 """Sets all the values of the current contribution object from a
7557 dictionary containing the following key-value pairs:
7558 title-(str)
7559 description-(str)
7560 locationName-(str) => name of the location, if not specified
7561 it will be set to the parent location name.
7562 locationAddress-(str)
7563 roomName-(str) => name of the room, if not specified it will
7564 be set to the parent room name.
7565 year, month, day, sHour, sMinute - (str) => components of the
7566 starting date of the session, if not specified it will
7567 be set to now.
7568 durationHours, durationMinutes - (str)
7569 speakers - (str)
7570 check parameter:
7571 0: no check at all
7572 1: check and raise error in case of problem
7573 2: check and adapt the owner dates
7574 moveEntries:
7575 0: no move
7576 1: moveEntries below the contribution
7577 Please, note that this method sets ALL values which means that if
7578 the given dictionary doesn't contain any of the keys the value
7579 will set to a default value.
7582 # In order to move the entries below, it is needed to know the diff (we have to move them)
7583 # and the list of entries to move. It's is needed to take those datas in advance because they
7584 # are going to be modified before the moving.
7585 if moveEntriesBelow == 1:
7586 oldStartDate = copy.copy(self.getStartDate())
7587 oldDuration = copy.copy(self.getDuration())
7588 i = self.getSchEntry().getSchedule().getEntries().index(self.getSchEntry())+1
7589 entriesList = self.getSchEntry().getSchedule().getEntries()[i:]
7590 if data.has_key("title"):
7591 self.setTitle(data["title"])
7592 if data.has_key("keywords"):
7593 self.setKeywords(data["keywords"])
7594 if data.has_key("description"):
7595 self.setDescription(data["description"])
7596 if data.has_key("type") and self.getConference():
7597 self.setType(self.getConference().getContribTypeById(data["type"]))
7598 if self.getConference():
7599 afm = self.getConference().getAbstractMgr().getAbstractFieldsMgr()
7600 for f in afm.getFields():
7601 id = f.getId()
7602 if data.has_key("f_%s" % id):
7603 self.setField(id, data["f_%s" % id])
7605 if "locationName" in data:
7606 loc = self.getOwnLocation()
7607 if not loc:
7608 loc = CustomLocation()
7609 self.setLocation(loc)
7610 loc.setName(data["locationName"])
7611 loc.setAddress(data.get("locationAddress", ""))
7612 else:
7613 self.setLocation(None)
7615 #same as for the location
7616 if "roomName" in data:
7617 room = self.getOwnRoom()
7618 if not room:
7619 room = CustomRoom()
7620 self.setRoom(room)
7621 room.setName(data["roomName"])
7622 room.retrieveFullName(data.get("locationName", ""))
7623 else:
7624 self.setRoom(None)
7626 tz = 'UTC'
7627 if self.getConference():
7628 tz = self.getConference().getTimezone()
7629 if data.get("targetDay", "") != "" and data.get("sHour", "") != "" and data.get("sMinute", "") != "" and check == 2:
7630 ############################################
7631 # Fermi timezone awareness #
7632 ############################################
7633 me = timezone(tz).localize(datetime(int(data["targetDay"][0:4]),
7634 int(data["targetDay"][5:7]), int(data["targetDay"][8:])))
7635 sdate = timezone(tz).localize(datetime(me.year, me.month,
7636 me.day, int(data["sHour"]), int(data["sMinute"])))
7637 self.setStartDate(sdate.astimezone(timezone('UTC')), check=2)
7638 if data.get("sYear", "") != "" and data.get("sMonth", "") != "" and \
7639 data.get("sDay", "") != "" and data.get("sHour", "") != "" and \
7640 data.get("sMinute", "") != "":
7641 self.setStartDate(timezone(tz).localize(datetime(int(data["sYear"]),
7642 int(data["sMonth"]), int(data["sDay"]),
7643 int(data["sHour"]), int(data["sMinute"]))).astimezone(timezone('UTC')),
7644 check=2)
7645 ############################################
7646 # Fermi timezone awareness(end) #
7647 ############################################
7648 if data.get("durTimedelta", "") != "":
7649 self.setDuration(check=check, dur=data["durTimedelta"])
7650 elif data.get("durHours", "") != "" and data.get("durMins", "") != "":
7651 self.setDuration(data["durHours"], data["durMins"], check)
7652 else:
7653 h = data.get("durHours", "").strip()
7654 m = data.get("durMins", "").strip()
7655 if h != "" or m != "":
7656 h = h or "0"
7657 m = m or "0"
7658 if h != "0" or m != "0":
7659 self.setDuration(int(h), int(m), check)
7660 if data.has_key("boardNumber"):
7661 self.setBoardNumber(data.get("boardNumber", ""))
7662 if moveEntriesBelow == 1:
7663 diff = (self.getStartDate() - oldStartDate) + (self.getDuration() - oldDuration)
7664 self.getConference().getSchedule().moveEntriesBelow(diff, entriesList)
7665 self.notifyModification()
7667 def clone(self, parent, options, deltaTime = 0):
7668 cont = Contribution()
7669 parent.addContribution(cont, contrib_id=self.getId())
7670 cont.setTitle( self.getTitle() )
7671 cont.setDescription( self.getDescription() )
7672 for k, v in self.getFields().items():
7673 cont.setField(k, v)
7674 cont.setKeywords( self.getKeywords() )
7675 if deltaTime == 0 :
7676 deltaTime = parent.getStartDate() - self.getOwner().getStartDate()
7678 startDate = None
7679 if self.startDate is not None :
7680 startDate = self.getStartDate() + deltaTime
7681 cont.setStartDate( startDate )
7683 cont.setDuration( dur=self.getDuration() )
7685 if self.getOwnLocation() is not None:
7686 cont.setLocation(self.getOwnLocation().clone())
7687 if self.getOwnRoom() is not None:
7688 cont.setRoom(self.getOwnRoom().clone())
7689 cont.setBoardNumber(self.getBoardNumber())
7690 cont.setReportNumberHolder(self.getReportNumberHolder().clone(self))
7692 cont.setStatus(self.getCurrentStatus())
7694 if self.getType() is not None :
7695 for ct in cont.getConference().getContribTypeList() :
7696 if ct.getName() == self.getType().getName() :
7697 cont.setType(ct)
7698 break
7700 if options.get("tracks", False) :
7701 if self.getTrack() is not None :
7702 for tr in cont.getConference().getTrackList() :
7703 if tr.getTitle() == self.getTrack().getTitle() :
7704 cont.setTrack(tr)
7705 break
7706 else :
7707 cont.setTrack(None)
7709 if options.get("access", False) :
7710 cont.setProtection(self.getAccessController()._getAccessProtection())
7711 for u in self.getAllowedToAccessList() :
7712 cont.grantAccess(u)
7713 for mgr in self.getManagerList() :
7714 cont.grantModification(mgr)
7715 for sub in self.getSubmitterList() :
7716 cont.grantSubmission(sub)
7717 for domain in self.getDomainList():
7718 cont.requireDomain(domain)
7720 if options.get("authors", False) :
7721 for a in self.getPrimaryAuthorList() :
7722 cont.addPrimaryAuthor(a.clone())
7723 for ca in self.getCoAuthorList() :
7724 cont.addCoAuthor(ca.clone())
7725 for sp in self.getSpeakerList():
7726 cont.newSpeaker(sp.clone())
7727 cont.setSpeakerText(self.getSpeakerText())
7729 if options.get("materials", False) :
7730 for m in self.getMaterialList() :
7731 cont.addMaterial(m.clone(cont))
7732 if self.getPaper() is not None:
7733 cont.setPaper(self.getPaper().clone(cont))
7734 if self.getSlides() is not None:
7735 cont.setSlides(self.getSlides().clone(cont))
7736 if self.getVideo() is not None:
7737 cont.setVideo(self.getVideo().clone(cont))
7738 if self.getPoster() is not None:
7739 cont.setPoster(self.getPoster().clone(cont))
7740 if self.getReviewing() is not None:
7741 cont.setReviewing(self.getReviewing().clone(cont))
7743 if options.get("subcontribs", False) :
7744 for sc in self.getSubContributionList() :
7745 cont.addSubContribution(sc.clone(cont, self, options), subcontrib_id=sc.getId())
7746 return cont
7748 def notifyModification( self, date = None, raiseEvent = True, cleanCache = True):
7749 self.setModificationDate(date)
7751 if raiseEvent:
7752 signals.event.contribution_data_changed.send(self)
7754 if cleanCache:
7755 self.cleanCache()
7757 parent = self.getParent()
7758 if parent:
7759 parent.setModificationDate()
7760 self._p_changed = 1
7762 def cleanCache(self, cleanConference = True):
7763 # Do not clean cache if already cleaned
7764 if not ContextManager.get('clean%s'%self.getUniqueId(), False):
7765 ScheduleToJson.cleanCache(self)
7766 ContextManager.set('clean%s'%self.getUniqueId(), cleanConference)
7768 def getCategoriesPath(self):
7769 return self.getConference().getCategoriesPath()
7771 def getModifKey( self ):
7772 return self.getConference().getModifKey()
7774 def getAccessKey( self ):
7775 return self.getConference().getAccessKey()
7777 def getLocator( self ):
7778 """Gives back a globaly unique identification encapsulated in a Locator
7779 object for the contribution instance
7781 if self.getConference() == None:
7782 return Locator()
7783 lconf = self.getConference().getLocator()
7784 if self.getSession() is not None:
7785 lconf["sessionId"] = self.getSession().getId()
7786 lconf["contribId"] = self.getId()
7787 return lconf
7789 def _setConference( self, conf ):
7790 self.parent = conf
7792 def _setId( self, id ):
7793 self.id = id
7795 def includeInConference( self, conf, id ):
7796 """sets the conference of a contribution
7798 if self.getConference() is not None:
7799 #raise MaKaCError("the contribution is already included in a conference")
7800 pass
7801 else:
7802 self._setConference( conf )
7803 self._setId( id )
7805 def delete( self ):
7806 """deletes a contribution and all of its subitems
7809 oldParent = self.getConference()
7811 if oldParent != None:
7812 signals.event.contribution_deleted.send(self, parent=oldParent)
7814 self.setTrack(None)
7815 for mat in self.getMaterialList():
7816 self.removeMaterial(mat)
7817 self.removePaper()
7818 self.removeSlides()
7819 self.removeVideo()
7820 self.removePoster()
7821 self.removeReviewing()
7823 self.notify_protection_to_owner(self, delete=True)
7825 self.setSession(None)
7827 while len(self.getSubContributionList()) > 0:
7829 sc = self.getSubContributionList()[0]
7831 self.removeSubContribution(sc)
7834 # delete it from parent session (if it exists)
7835 if self.getOwner() != self.getConference():
7837 self.getOwner().removeContribution( self )
7839 # (always) delete it from the parent conference
7840 self.getConference().removeContribution( self, callDelete=False )
7842 self._setConference( None )
7844 self.setStatus(ContribStatusNone(self))
7846 TrashCanManager().add(self)
7848 def recover(self):
7849 TrashCanManager().remove(self)
7851 def setId( self, newId ):
7852 self._setId(newId)
7854 def getId( self ):
7855 return self.id
7857 def getUniqueId( self ):
7858 """returns (string) the unique identifier of the item"""
7859 """used mainly in the web session access key table"""
7860 return "%st%s" % (self.getConference().getUniqueId(),self.id)
7862 def setTitle( self, newTitle, notify = True ):
7863 oldTitle = self.title
7864 self.title = newTitle.strip()
7866 if notify:
7867 if oldTitle != newTitle:
7868 signals.event.contribution_title_changed.send(self, old=oldTitle, new=newTitle)
7869 self.notifyModification()
7871 def getTitle( self ):
7872 if self.title.strip() == "":
7873 return "(no title)"
7874 return self.title
7876 def getDescription(self):
7877 return str(self.getField("content"))
7879 def setDescription(self, desc):
7880 self.setField("content", desc)
7882 def setParent(self,parent):
7883 self.parent=parent
7884 self.notifyModification(cleanCache = False)
7885 if self.parent==None:
7886 return
7888 def getParent( self ):
7889 if self.getSession() is not None:
7890 return self.getSession()
7891 return self.getConference()
7893 def getOwner( self ):
7894 return self.getParent()
7896 def setOwner(self, owner):
7897 self.setParent(owner)
7899 def getConference( self ):
7900 return self.parent
7902 def getSession( self ):
7903 try:
7904 if self._session:
7905 pass
7906 except AttributeError:
7907 self._session=None
7908 return self._session
7910 def setSession(self,session):
7911 if self.getSession()==session:
7912 return
7913 if self.isScheduled():
7914 schEntry=self.getSchEntry()
7915 schEntry.getSchedule().removeEntry(schEntry)
7916 oldSession=self.getSession()
7917 if oldSession is not None:
7918 oldSession.removeContribution(self)
7919 self._session=session
7920 if session is not None:
7921 session.addContribution(self)
7923 def getContribution(self):
7924 return self
7926 def _resetSchEntry(self):
7927 self.__schEntry=ContribSchEntry(self)
7929 def getSchEntry(self):
7930 if self.__schEntry is None or \
7931 not isinstance(self.__schEntry,ContribSchEntry):
7932 self._resetSchEntry()
7933 return self.__schEntry
7935 def isScheduled(self):
7936 #For the moment we do it like this
7937 return self.getSchEntry().getSchedule() is not None
7939 def isWithdrawn(self):
7940 return isinstance(self.getCurrentStatus(), ContribStatusWithdrawn)
7942 def getLocationParent(self):
7944 Returns the object from which the room/location
7945 information should be inherited
7947 if not self.getConference().getEnableSessionSlots() and self.getSession():
7948 return self.getSession()
7949 if self.isScheduled():
7950 return self.getSchEntry().getSchedule().getOwner()
7951 return self.getOwner()
7953 def getOwnLocation(self):
7954 return self.place
7956 def setLocation(self, newLocation):
7957 oldLocation = self.place
7958 self.place = newLocation
7959 self.notifyModification()
7961 def getOwnRoom(self):
7962 return self.room
7964 def setRoom(self, newRoom):
7965 oldRoom = self.room
7966 self.room = newRoom
7967 self.notifyModification()
7969 def setBoardNumber(self, newBoardNum):
7970 self._boardNumber=str(newBoardNum).strip()
7972 def getBoardNumber(self):
7973 try:
7974 if self._boardNumber:
7975 pass
7976 except AttributeError:
7977 self._boardNumber=""
7978 return self._boardNumber
7980 def verifyStartDate(self, sDate, check=2):
7981 """check parameter:
7982 0: no check at all
7983 1: check and raise error in case of problem
7984 2: check and adapt the owner dates"""
7986 tz = timezone(self.getConference().getTimezone())
7987 if self.getSchEntry().getSchedule():
7988 owner = self.getSchEntry().getSchedule().getOwner()
7989 else:
7990 owner = self.getOwner()
7991 if sDate < owner.getStartDate():
7992 if check == 1:
7993 raise ParentTimingError(_("The contribution <i>\"%s\"</i> cannot start before (%s) its parent (%s)") %\
7994 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
7995 owner.getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
7996 _("Contribution"))
7997 if check == 2:
7998 ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
7999 owner, sDate.astimezone(tz)))
8000 owner.setDates(sDate,owner.getEndDate(), check)
8001 if sDate > owner.getEndDate():
8002 if check == 1:
8003 raise ParentTimingError(_("The contribution <i>\"%s\"</i> cannot start after (%s) its parent end date(%s)") %\
8004 (self.getTitle(), sDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8005 owner.getEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
8006 _("Contribution"))
8007 if check == 2:
8008 owner.setEndDate(sDate+self.getDuration(),check)
8009 # Check that after modifying the start date, the end date is still within the limits of the slot
8010 if self.getDuration() and sDate + self.getDuration() > owner.getEndDate():
8011 if check==1:
8012 raise ParentTimingError("The contribution cannot end after (%s) its parent ends (%s)"%\
8013 ((sDate + self.getDuration()).astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8014 owner.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
8015 _("Contribution"))
8016 elif check==2:
8017 # update the schedule
8018 owner.setEndDate(sDate + self.getDuration(),check)
8019 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
8020 owner, owner.getAdjustedEndDate()))
8022 def setStartDate(self, newDate, check=2, moveEntries=0):
8023 """check parameter:
8024 0: no check at all
8025 1: check and raise error in case of problem
8026 2: check and adapt the owner dates"""
8027 if newDate == None:
8028 self.startDate=None
8029 return
8030 if not newDate.tzname():
8031 raise MaKaCError("date should be timezone aware")
8033 if newDate != None and check != 0:
8034 self.verifyStartDate(newDate, check)
8035 self.startDate=copy.copy(newDate)
8036 self.getSchEntry().synchro()
8037 self.notifyModification()
8039 def getStartDate(self):
8040 return self.startDate
8042 def getAdjustedStartDate(self, tz=None):
8043 if self.getStartDate() is None:
8044 return None
8045 if not tz:
8046 tz = self.getConference().getTimezone()
8047 if tz not in all_timezones:
8048 tz = 'UTC'
8049 return self.getStartDate().astimezone(timezone(tz))
8051 def getEndDate(self):
8052 if self.getStartDate() is None:
8053 return None
8054 return self.getStartDate()+self.getDuration()
8056 def getAdjustedEndDate(self, tz=None):
8057 if not tz:
8058 tz = self.getConference().getTimezone()
8059 if tz not in all_timezones:
8060 tz = 'UTC'
8061 if self.getEndDate():
8062 return self.getEndDate().astimezone(timezone(tz))
8063 return None
8065 def getDuration(self):
8066 return self.duration
8068 def verifyDuration(self, check=2):
8069 """check parameter:
8070 0: no check at all
8071 1: check and raise error in case of problem
8072 2: check and adapt the owner dates"""
8074 tz = timezone(self.getConference().getTimezone())
8076 endDate = self.getEndDate()
8078 if self.getSchEntry().getSchedule() is not None:
8079 owner = self.getSchEntry().getSchedule().getOwner()
8080 if endDate > owner.getEndDate():
8081 if check==1:
8082 raise ParentTimingError(_("The contribution \"%s\" ending date (%s) has to fit between its parent's dates (%s - %s)") %\
8083 (self.getTitle(), endDate.astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8084 owner.getStartDate().astimezone(tz).strftime('%Y-%m-%d %H:%M'),\
8085 owner.getEndDate().astimezone(tz).strftime('%Y-%m-%d %H:%M')),\
8086 _("Contribution"))
8087 elif check==2:
8088 ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
8089 owner, self.getAdjustedEndDate()))
8090 owner.setEndDate(endDate, check)
8092 def setDuration(self, hours=0, minutes=15, check=2, dur=0):
8093 """check parameter:
8094 0: no check at all
8095 1: check and raise error in case of problem
8096 2: check and adapt the owner dates"""
8098 if dur!=0:
8099 self.duration=dur
8100 else:
8101 self.duration=timedelta(hours=int(hours),minutes=int(minutes))
8102 if check != 0:
8103 self.verifyDuration(check)
8104 self.getSchEntry().synchro()
8105 self.notifyModification()
8107 def _addAuthor(self, part):
8110 try:
8111 if self._authors:
8112 pass
8113 except AttributeError:
8114 self._authors = OOBTree()
8115 try:
8116 if self._authorGen:
8117 pass
8118 except AttributeError:
8119 self._authorGen=Counter()
8120 newId = part.getId()
8121 if newId == "":
8122 newId = str( self._authorGen.newCount() )
8123 self._authors[newId] = part
8124 part.includeInContribution( self, newId )
8126 def _removeAuthor(self, part):
8129 try:
8130 if self._authors:
8131 pass
8132 except AttributeError:
8133 self._authors = OOBTree()
8134 if not self._authors.has_key( part.getId() ):
8135 return
8136 del self._authors[ part.getId() ]
8137 if not self.isSpeaker(part):
8138 part.delete()
8140 def addPrimaryAuthor(self, part, index=None):
8143 try:
8144 if self._primaryAuthors:
8145 pass
8146 except AttributeError:
8147 self._primaryAuthors = []
8148 self._addAuthor( part )
8149 if index is not None:
8150 self._primaryAuthors.insert(index, part)
8151 else:
8152 self._primaryAuthors.append( part )
8153 if self.getConference() is not None:
8154 self.getConference().indexAuthor(part)
8155 self.notifyModification(cleanCache = False)
8157 def removePrimaryAuthor(self, part, removeSpeaker=1, removePendingSubm=True):
8160 try:
8161 if self._primaryAuthors:
8162 pass
8163 except AttributeError:
8164 self._primaryAuthors = []
8165 if part not in self._primaryAuthors:
8166 return
8167 if self.getConference() is not None:
8168 self.getConference().unindexAuthor(part)
8169 self._primaryAuthors.remove( part )
8170 if removeSpeaker:
8171 self.removeSpeaker( part )
8172 self._removeAuthor( part )
8173 if removePendingSubm:
8174 #--Pending queue: remove pending participant waiting to became submitter if anything
8175 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8177 self.notifyModification(cleanCache = False)
8179 def recoverPrimaryAuthor(self, pa, isPendingSubmitter):
8180 self.addPrimaryAuthor(pa)
8181 pa.recover()
8182 if isPendingSubmitter:
8183 self.getConference().getPendingQueuesMgr().addPendingSubmitter(pa, False)
8185 def isPrimaryAuthor(self, part):
8188 try:
8189 if self._primaryAuthors:
8190 pass
8191 except AttributeError:
8192 self._primaryAuthors = []
8193 return part in self._primaryAuthors
8195 def isCoAuthor(self, part):
8196 try:
8197 if self._coAuthors:
8198 pass
8199 except AttributeError:
8200 self._coAuthors = []
8201 return part in self._coAuthors
8203 def isPrimaryAuthorByEmail(self, email):
8204 for prAuthor in self.getPrimaryAuthorList():
8205 if prAuthor.getEmail() == email:
8206 return True
8207 return False
8209 def isCoAuthorByEmail(self, email):
8210 for coAuthor in self.getCoAuthorList():
8211 if coAuthor.getEmail() == email:
8212 return True
8213 return False
8215 def isSpeakerByEmail(self, email):
8216 for speaker in self.getSpeakerList():
8217 if speaker.getEmail() == email:
8218 return True
8219 return False
8221 def changePosPrimaryAuthor(self, part, index):
8224 try:
8225 if self._primaryAuthors:
8226 pass
8227 except AttributeError:
8228 self._primaryAuthors=[]
8229 if not part in self._primaryAuthors:
8230 return
8231 self._primaryAuthors.remove(part)
8232 self._primaryAuthors.insert(index,part)
8233 self.notifyModification(cleanCache = False)
8235 def upPrimaryAuthor(self, part):
8238 try:
8239 if self._primaryAuthors:
8240 pass
8241 except AttributeError:
8242 self._primaryAuthors=[]
8243 try:
8244 idx=self._primaryAuthors.index(part)
8245 except ValueError:
8246 return
8247 if idx==0:
8248 return
8249 self._primaryAuthors.remove(part)
8250 self._primaryAuthors.insert(idx-1,part)
8251 self.notifyModification(cleanCache=False)
8253 def downPrimaryAuthor(self, part):
8256 try:
8257 if self._primaryAuthors:
8258 pass
8259 except AttributeError:
8260 self._primaryAuthors=[]
8261 try:
8262 idx=self._primaryAuthors.index(part)
8263 except ValueError:
8264 return
8265 if idx>len(self._primaryAuthors):
8266 return
8267 self._primaryAuthors.remove(part)
8268 self._primaryAuthors.insert(idx+1,part)
8269 self.notifyModification(cleanCache = False)
8271 def newAuthorsList(self, prAuthors, coAuthors):
8272 ''' calculate new lists of both kind of authors, because something has
8273 been changed the position by drag and drop '''
8274 newPrList = self.calculateNewAuthorList(prAuthors, "prAuthor")
8275 newCoList = self.calculateNewAuthorList(coAuthors, "coAuthor")
8276 self.setPrimaryAuthorList(newPrList)
8277 self.setCoAuthorList(newCoList)
8279 def calculateNewAuthorList(self, list, kind):
8280 result = []
8281 if kind == "prAuthor":
8282 for auth in list:
8283 author = self.getPrimaryAuthorById(auth['id'])
8284 if author:
8285 result.append(author)
8286 else:
8287 author = self.getCoAuthorById(auth['id'])
8288 if author:
8289 result.append(author)
8291 elif kind == "coAuthor":
8292 for auth in list:
8293 author = self.getCoAuthorById(auth['id'])
8294 if author:
8295 result.append(author)
8296 else:
8297 author = self.getPrimaryAuthorById(auth['id'])
8298 if author:
8299 result.append(author)
8300 return result
8302 def getPrimaryAuthorById(self, authorId):
8303 for author in self.getPrimaryAuthorList():
8304 if authorId == author.getId():
8305 return author
8306 return None
8308 def getCoAuthorById(self, authorId):
8309 for author in self.getCoAuthorList():
8310 if authorId == author.getId():
8311 return author
8312 return None
8314 def setPrimaryAuthorList(self, l):
8315 self._primaryAuthors = l
8316 self.notifyModification(cleanCache = False)
8318 def setCoAuthorList(self, l):
8319 self._coAuthors = l
8320 self.notifyModification(cleanCache = False)
8322 def changePosCoAuthor(self, part, index):
8325 try:
8326 if self._coAuthors:
8327 pass
8328 except AttributeError:
8329 self._coAuthors=[]
8330 if not part in self._coAuthors:
8331 return
8332 self._coAuthors.remove(part)
8333 self._coAuthors.insert(index,part)
8334 self.notifyModification(cleanCache = False)
8336 def upCoAuthor(self, part):
8339 try:
8340 if self._coAuthors:
8341 pass
8342 except AttributeError:
8343 self._coAuthors=[]
8344 try:
8345 idx=self._coAuthors.index(part)
8346 except ValueError:
8347 return
8348 if idx==0:
8349 return
8350 self._coAuthors.remove(part)
8351 self._coAuthors.insert(idx-1,part)
8352 self.notifyModification(cleanCache = False)
8354 def downCoAuthor(self, part):
8357 try:
8358 if self._coAuthors:
8359 pass
8360 except AttributeError:
8361 self._coAuthors=[]
8362 try:
8363 idx=self._coAuthors.index(part)
8364 except ValueError:
8365 return
8366 if idx>len(self._coAuthors):
8367 return
8368 self._coAuthors.remove(part)
8369 self._coAuthors.insert(idx+1,part)
8370 self.notifyModification(cleanCache = False)
8372 def getPrimaryAuthorList(self):
8375 try:
8376 if self._primaryAuthors:
8377 pass
8378 except AttributeError:
8379 self._primaryAuthors = []
8380 return self._primaryAuthors
8382 getPrimaryAuthorsList = getPrimaryAuthorList
8384 def getAuthorList(self):
8387 try:
8388 if self._authors:
8389 pass
8390 except AttributeError:
8391 self._authors = OOBTree()
8392 return self._authors.values()
8394 def getAllAuthors(self):
8395 """ This method returns a list composed by the primary authors
8396 and co-authors. The different with getAuthorList() is the type
8397 of the output.
8399 return self.getPrimaryAuthorList() + self.getCoAuthorList()
8401 def addCoAuthor(self, part, index=None):
8404 try:
8405 if self._coAuthors:
8406 pass
8407 except AttributeError:
8408 self._coAuthors = []
8409 self._addAuthor( part )
8410 if index is not None:
8411 self._coAuthors.insert(index, part)
8412 else:
8413 self._coAuthors.append( part )
8414 if self.getConference() is not None:
8415 self.getConference().indexAuthor(part)
8416 self.notifyModification(cleanCache = False)
8418 def removeCoAuthor(self, part, removeSpeaker=1, removePendingSubm=True):
8421 try:
8422 if self._coAuthors:
8423 pass
8424 except AttributeError:
8425 self._coAuthors = []
8426 if part not in self._coAuthors:
8427 return
8428 if self.getConference() is not None:
8429 self.getConference().unindexAuthor(part)
8430 self._coAuthors.remove( part )
8431 if removeSpeaker:
8432 self.removeSpeaker( part )
8433 self._removeAuthor( part )
8434 if removePendingSubm:
8435 #--Pending queue: remove pending participant waiting to became submitter if anything
8436 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8438 self.notifyModification(cleanCache = False)
8440 def recoverCoAuthor(self, ca, isPendingSubmitter):
8441 self.addCoAuthor(ca)
8442 ca.recover()
8443 if isPendingSubmitter:
8444 self.getConference().getPendingQueuesMgr().addPendingSubmitter(ca, False)
8446 def getCoAuthorList(self):
8449 try:
8450 if self._coAuthors:
8451 pass
8452 except AttributeError:
8453 self._coAuthors = []
8454 return self._coAuthors
8456 def getAuthorById(self, authorId):
8459 try:
8460 if self._authors:
8461 pass
8462 except AttributeError:
8463 self._authors = OOBTree()
8464 return self._authors.get( authorId.strip(), None )
8466 def isAuthor(self, part):
8469 try:
8470 if self._authors:
8471 pass
8472 except AttributeError:
8473 self._authors = OOBTree()
8474 return self._authors.has_key( part.getId() )
8476 def getSpeakerById(self, authorId):
8479 try:
8480 if self._speakers:
8481 pass
8482 except AttributeError:
8483 self._speakers = []
8484 for spk in self._speakers:
8485 if spk.getId() == authorId:
8486 return spk
8487 return None
8489 def changePosSpeaker(self, part, index):
8492 try:
8493 if self._speakers:
8494 pass
8495 except AttributeError:
8496 self._speakers = []
8497 if not part in self._speakers:
8498 return
8499 self._speakers.remove(part)
8500 self._speakers.insert(index,part)
8501 self.notifyModification()
8503 def addSpeaker(self, part, index=None):
8505 Adds a speaker (ContributionParticipation object) to the contribution
8506 forcing it to be one of the authors of the contribution
8508 try:
8509 if self._speakers:
8510 pass
8511 except AttributeError:
8512 self._speakers = []
8513 if not self.isAuthor( part ):
8514 raise MaKaCError( _("The Specified speaker is not the Author"), _("Contribution"))
8515 if index is not None:
8516 self._speakers.insert(index, part)
8517 else:
8518 self._speakers.append( part )
8519 if self.getConference() is not None:
8520 self.getConference().indexSpeaker(part)
8521 self.notifyModification()
8523 def newSpeaker(self, part):
8525 Adds a new speaker (ContributionParticipation object) to the contribution
8526 setting the speakers ID and the fact it belongs to that contribution
8528 try:
8529 if self._speakers:
8530 pass
8531 except AttributeError:
8532 self._speakers = []
8533 try:
8534 if self._authorGen:
8535 pass
8536 except AttributeError:
8537 self._authorGen=Counter()
8538 self._speakers.append( part )
8539 newId = part.getId()
8540 if newId == "":
8541 newId = str( self._authorGen.newCount() )
8542 part.includeInContribution(self, newId)
8543 if self.getConference() is not None:
8544 self.getConference().indexSpeaker(part)
8545 self.notifyModification()
8547 def removeSpeaker(self, part):
8550 try:
8551 if self._speakers:
8552 pass
8553 except AttributeError:
8554 self._speakers = []
8555 if part not in self._speakers:
8556 return
8557 self._speakers.remove( part )
8558 if self.getConference() is not None:
8559 self.getConference().unindexSpeaker(part)
8560 if part not in self.getAuthorList():
8561 part.delete()
8562 #--Pending queue: remove pending participant waiting to became submitter if anything
8563 self.getConference().getPendingQueuesMgr().removePendingSubmitter(part)
8565 self.notifyModification()
8567 def recoverSpeaker(self, spk, isPendingSubmitter):
8568 self.newSpeaker(spk)
8569 spk.recover()
8570 if isPendingSubmitter:
8571 self.getConference().getPendingQueuesMgr().addPendingSubmitter(spk, False)
8573 def isSpeaker(self, part):
8576 try:
8577 if self._speakers:
8578 pass
8579 except AttributeError:
8580 self._speakers = []
8581 return part in self._speakers
8583 def getSpeakerList(self):
8586 try:
8587 if self._speakers:
8588 pass
8589 except AttributeError:
8590 self._speakers = []
8591 return self._speakers
8593 def getSpeakerText(self):
8594 #to be removed
8595 try:
8596 if self.speakerText:
8597 pass
8598 except AttributeError, e:
8599 self.speakerText = ""
8600 return self.speakerText
8602 def setSpeakerText(self, newText):
8603 self.speakerText = newText.strip()
8605 def appendSpeakerText(self, newText):
8606 self.setSpeakerText("%s, %s" % (self.getSpeakerText(), newText.strip()))
8608 def isProtected(self):
8609 # tells if a contribution is protected or not
8610 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
8612 def getAccessProtectionLevel(self):
8613 return self.__ac.getAccessProtectionLevel()
8615 def isItselfProtected(self):
8616 return self.__ac.isItselfProtected()
8618 def hasAnyProtection(self):
8619 """Tells whether a contribution has any kind of protection over it:
8620 access or domain protection.
8622 if self.__ac.isProtected():
8623 return True
8624 if self.getDomainList():
8625 return True
8626 if self.getAccessProtectionLevel() == -1:
8627 return False
8628 if self.getOwner():
8629 return self.getOwner().hasAnyProtection()
8630 else:
8631 return False
8633 def hasProtectedOwner(self):
8634 if self.getOwner() != None:
8635 return self.getOwner().isProtected()
8636 return False
8638 def setProtection(self, private):
8640 oldValue = 1 if self.isProtected() else -1
8642 self.__ac.setProtection( private )
8643 self.notify_protection_to_owner(self)
8645 if oldValue != private:
8646 # notify listeners
8647 signals.event.contribution_protection_changed.send(self, old=oldValue, new=private)
8649 def grantAccess(self, prin):
8650 self.__ac.grantAccess( prin )
8651 if isinstance(prin, AvatarUserWrapper):
8652 prin.linkTo(self, "access")
8653 self.notifyModification(raiseEvent = False)
8655 def revokeAccess( self, prin ):
8656 self.__ac.revokeAccess( prin )
8657 if isinstance(prin, AvatarUserWrapper):
8658 prin.unlinkTo(self, "access")
8659 self.notifyModification(raiseEvent = False)
8661 def canView( self, aw ):
8662 """tells whether the specified user has access to the current object
8663 or any of its sub-objects
8665 if self.canAccess( aw ):
8666 return True
8667 ################################################################################################
8668 for sc in self.getSubContributionList():
8669 if sc.canView( aw ):
8670 return True
8671 return False
8673 def isAllowedToAccess( self, user ):
8674 if not user:
8675 return False
8676 return (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user )) or\
8677 self.__ac.canUserAccess( user ) or\
8678 self.canUserModify( user ) or \
8679 self.canUserSubmit(user)
8681 def canAccess( self, aw ):
8682 # Allow harvesters (Invenio, offline cache) to access
8683 # protected pages
8684 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
8685 return True
8686 #####################################################
8688 if self.canModify(aw):
8689 return True
8691 if not self.canIPAccess(request.remote_addr) and not self.isAllowedToAccess(aw.getUser()):
8692 return False
8693 if not self.isProtected():
8694 return True
8695 flag = self.isAllowedToAccess( aw.getUser() )
8696 return flag or self.getConference().canKeyAccess(aw)
8698 def grantModification( self, prin ):
8699 self.__ac.grantModification( prin )
8700 if isinstance(prin, AvatarUserWrapper):
8701 prin.linkTo(self, "manager")
8702 self.notifyModification(raiseEvent = False)
8704 def revokeModification( self, prin ):
8705 self.__ac.revokeModification( prin )
8706 if isinstance(prin, AvatarUserWrapper):
8707 prin.unlinkTo(self, "manager")
8708 self.notifyModification(raiseEvent = False)
8710 def canModify(self, aw_or_user):
8711 if hasattr(aw_or_user, 'getUser'):
8712 aw_or_user = aw_or_user.getUser()
8713 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
8715 def canUserModify( self, av ):
8716 """Tells whether a user is allowed to modify the current contribution:
8717 only if the user is granted to modify the contribution or the user
8718 can modify any of its upper objects (i.e. conference or session).
8720 return self.getParent().canUserModify( av ) or self.__ac.canModify( av )
8722 def getManagerList( self ):
8723 return self.__ac.getModifierList()
8725 def getAllowedToAccessList( self ):
8726 return self.__ac.getAccessList()
8728 def addMaterial( self, newMat ):
8729 newMat.setId( str(self.__materialGenerator.newCount()) )
8730 newMat.setOwner( self )
8731 self.materials[ newMat.getId() ] = newMat
8732 self.notifyModification()
8734 def removeMaterial(self, mat):
8735 if mat.getId() in self.materials.keys():
8736 mat.delete()
8737 self.materials[mat.getId()].setOwner(None)
8738 del self.materials[ mat.getId() ]
8739 self.notifyModification()
8740 elif mat.getId().lower() == 'paper':
8741 self.removePaper()
8742 elif mat.getId().lower() == 'slides':
8743 self.removeSlides()
8744 elif mat.getId().lower() == 'video':
8745 self.removeVideo()
8746 elif mat.getId().lower() == 'poster':
8747 self.removePoster()
8748 elif mat.getId().lower() == 'reviewing':
8749 self.removeReviewing()
8751 def recoverMaterial(self, recMat):
8752 # Id must already be set in recMat.
8753 recMat.setOwner( self )
8754 self.materials[ recMat.getId() ] = recMat
8755 recMat.recover()
8756 self.notifyModification()
8758 def getMaterialRegistry(self):
8760 Return the correct material registry for this type
8762 from MaKaC.webinterface.materialFactories import ContribMFRegistry
8763 return ContribMFRegistry
8765 def getMaterialById( self, matId ):
8766 if matId.lower() == 'paper':
8767 return self.getPaper()
8768 elif matId.lower() == 'slides':
8769 return self.getSlides()
8770 elif matId.lower() == 'video':
8771 return self.getVideo()
8772 elif matId.lower() == 'poster':
8773 return self.getPoster()
8774 elif self.materials.has_key(matId):
8775 return self.materials[ matId ]
8776 return None
8778 def getMaterialList( self ):
8779 return self.materials.values()
8781 def getAllMaterialList(self, sort=True):
8782 l = self.getMaterialList()
8783 if self.getPaper():
8784 l.append( self.getPaper() )
8785 if self.getSlides():
8786 l.append( self.getSlides() )
8787 if self.getVideo():
8788 l.append( self.getVideo() )
8789 if self.getPoster():
8790 l.append( self.getPoster() )
8791 if sort:
8792 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
8793 return l
8795 def getAllViewableMaterialList( self, aw=None ):
8796 if not aw:
8797 aw = ContextManager.get("currentAW", ContextManager.get("currentRH").getAW())
8798 return [mat for mat in self.getAllMaterialList() if mat.canView(aw)]
8801 def newSubContribution(self):
8802 newSub = SubContribution()
8803 self.addSubContribution(newSub)
8804 signals.event.subcontribution_created.send(newSub, parent=self)
8805 return newSub
8807 def addSubContribution(self, newSubCont, subcontrib_id=None):
8808 if subcontrib_id is None:
8809 newSubCont.setId(str(self.__subContGenerator.newCount()))
8810 else:
8811 newSubCont.setId(str(subcontrib_id))
8812 self.__subContGenerator.sync(subcontrib_id)
8813 newSubCont.setOwner( self )
8814 self._subConts.append( newSubCont )
8815 self.notifyModification(cleanCache = False)
8817 def removeSubContribution( self, subCont ):
8818 if subCont in self._subConts:
8819 subCont.delete()
8820 subCont.setOwner(None)
8821 self._subConts.remove(subCont)
8822 self.notifyModification(cleanCache = False)
8824 def recoverSubContribution( self, recSubCont ):
8825 # Id must already be set in recSubCont.
8826 recSubCont.setOwner( self )
8827 self._subConts.append( recSubCont )
8828 recSubCont.recover()
8829 self.notifyModification(cleanCache = False)
8831 def getSubContributionById(self, SCId):
8832 for sb in self._subConts:
8833 if sb.getId() == SCId:
8834 return sb
8836 def getSubContributionList(self):
8837 return self._subConts
8839 def iterSubContributions(self):
8840 return iter(self._subConts)
8842 def getNumberOfSubcontributions(self):
8843 return len(self._subConts)
8845 def upSubContribution(self, subcont):
8846 if subcont in self._subConts:
8847 if self._subConts.index(subcont) != 0:
8848 index = self._subConts.index(subcont)
8849 sb = self._subConts.pop(index)
8850 self._subConts.insert(index-1, sb)
8851 self.notifyModification(cleanCache = False)
8853 def downSubContribution(self, subCont):
8854 if subCont in self._subConts:
8855 if self._subConts.index(subCont) < len(self._subConts)-1:
8856 index = self._subConts.index(subCont)
8857 sb = self._subConts.pop(index)
8858 self._subConts.insert(index+1, sb)
8859 self.notifyModification(cleanCache = False)
8861 def setPaper( self, newPaper ):
8862 if self.getPaper() != None:
8863 raise MaKaCError( _("The paper for this contribution has already been set"), _("Contribution"))
8864 self.paper=newPaper
8865 self.paper.setOwner( self )
8866 self.notifyModification()
8868 def removePaper( self ):
8869 if self.paper is None:
8870 return
8871 self.paper.delete()
8872 self.paper.setOwner(None)
8873 self.paper = None
8874 self.notifyModification()
8876 def recoverPaper(self, p):
8877 self.setPaper(p)
8878 p.recover()
8880 def getPaper( self ):
8881 return self.paper
8883 def setSlides( self, newSlides ):
8884 if self.getSlides() != None:
8885 raise MaKaCError( _("The slides for this contribution have already been set"), _("contribution"))
8886 self.slides=newSlides
8887 self.slides.setOwner( self )
8888 self.notifyModification()
8890 def removeSlides( self ):
8891 if self.slides is None:
8892 return
8893 self.slides.delete()
8894 self.slides.setOwner( None )
8895 self.slides= None
8896 self.notifyModification()
8898 def recoverSlides(self, s):
8899 self.setSlides(s)
8900 s.recover()
8902 def getSlides( self ):
8903 return self.slides
8905 def setVideo( self, newVideo ):
8906 if self.getVideo() != None:
8907 raise MaKaCError( _("the video for this contribution has already been set"))
8908 self.video=newVideo
8909 self.video.setOwner( self )
8910 self.notifyModification()
8912 def removeVideo( self ):
8913 if self.getVideo() is None:
8914 return
8915 self.video.delete()
8916 self.video.setOwner(None)
8917 self.video = None
8918 self.notifyModification()
8920 def recoverVideo(self, v):
8921 self.setVideo(v)
8922 v.recover()
8924 def getVideo( self ):
8925 try:
8926 if self.video:
8927 pass
8928 except AttributeError:
8929 self.video = None
8930 return self.video
8932 def setPoster( self, newPoster ):
8933 if self.getPoster() != None:
8934 raise MaKaCError( _("the poster for this contribution has already been set"))
8935 self.poster=newPoster
8936 self.poster.setOwner( self )
8937 self.notifyModification()
8939 def removePoster( self ):
8940 if self.getPoster() is None:
8941 return
8942 self.poster.delete()
8943 self.poster.setOwner(None)
8944 self.poster = None
8945 self.notifyModification()
8947 def recoverPoster(self, p):
8948 self.setPoster(p)
8949 p.recover()
8951 def getPoster( self ):
8952 try:
8953 if self.poster:
8954 pass
8955 except AttributeError:
8956 self.poster = None
8957 return self.poster
8959 def setReviewing( self, newReviewing ):
8960 if self.getReviewing() != None:
8961 raise MaKaCError( _("The reviewing maretial for this contribution has already been set"), _("Contribution"))
8962 self.reviewing=newReviewing
8963 self.reviewing.setOwner( self )
8964 self.notifyModification()
8966 def removeReviewing( self ):
8967 if self.getReviewing() is None:
8968 return
8969 self.reviewing.delete()
8970 self.reviewing.setOwner(None)
8971 self.reviewing = None
8972 self.notifyModification()
8974 def recoverReviewing(self, p):
8975 self.setReviewing(p)
8976 p.recover()
8978 def getReviewing( self ):
8979 try:
8980 if self.reviewing:
8981 pass
8982 except AttributeError, e:
8983 self.reviewing = None
8984 return self.reviewing
8986 def getMasterSchedule( self ):
8987 return self.getOwner().getSchedule()
8989 def requireDomain( self, dom ):
8990 self.__ac.requireDomain( dom )
8992 def freeDomain( self, dom ):
8993 self.__ac.freeDomain( dom )
8995 def getDomainList( self ):
8996 return self.__ac.getRequiredDomainList()
8998 def getTrack( self ):
8999 try:
9000 if self._track:
9001 pass
9002 except AttributeError:
9003 self._track = None
9004 return self._track
9006 def setTrack( self, newTrack ):
9007 currentTrack = self.getTrack()
9008 if newTrack == currentTrack:
9009 return
9010 if currentTrack:
9011 currentTrack.removeContribution( self )
9012 self._track = newTrack
9013 if self._track:
9014 self._track.addContribution( self )
9016 def removeTrack(self, track):
9017 if track == self._track:
9018 self._track = None
9020 def setType( self, newType ):
9021 self._type = newType
9023 def getType( self ):
9024 try:
9025 if self._type:
9026 pass
9027 except AttributeError:
9028 self._type = None
9029 return self._type
9031 def getModificationDate( self ):
9032 """Returns the date in which the contribution was last modified"""
9033 try:
9034 return self._modificationDS
9035 except:
9036 if self.getConference():
9037 self._modificationDS = self.getConference().getModificationDate()
9038 else:
9039 self._modificationDS = nowutc()
9040 return self._modificationDS
9042 def getCurrentStatus(self):
9043 try:
9044 if self._status:
9045 pass
9046 except AttributeError:
9047 self._status=ContribStatusNotSch(self)
9048 return self._status
9049 getStatus = getCurrentStatus
9051 def setStatus(self,newStatus):
9054 self._status=newStatus
9056 def withdraw(self,resp,comment):
9057 """ Remove or put a contribution in a conference
9060 if self.isWithdrawn():
9061 #put back the authors in the author index
9062 for auth in self.getAuthorList():
9063 self.getConference().getAuthorIndex().index(auth)
9064 for spk in self.getSpeakerList():
9065 self.getConference().getSpeakerIndex().index(spk)
9066 #change the status of the contribution
9067 self._status=ContribStatusNotSch(self)
9069 else:
9070 #remove the authors from the author index
9071 if self.getConference() is not None:
9072 for auth in self.getAuthorList():
9073 self.getConference().getAuthorIndex().unindex(auth)
9074 for spk in self.getSpeakerList():
9075 self.getConference().unindexSpeaker(spk)
9076 #remove the contribution from any schedule it is included
9077 if self.isScheduled():
9078 self.getSchEntry().getSchedule().removeEntry(self.getSchEntry())
9079 self.getCurrentStatus().withdraw(resp,comment)
9082 def getSubmitterList(self, no_groups=False):
9083 try:
9084 if self._submitters:
9085 pass
9086 except AttributeError:
9087 self._submitters=[] #create the attribute
9088 self.notifyModification(raiseEvent = False)
9089 if no_groups:
9090 return [s for s in self._submitters if not isinstance(s, GroupWrapper)]
9091 else:
9092 return self._submitters
9094 def _grantSubmission(self,av):
9095 if av not in self.getSubmitterList():
9096 self.getSubmitterList().append(av)
9097 if self.getConference() is not None:
9098 self.getConference().addContribSubmitter(self,av)
9099 if isinstance(av, AvatarUserWrapper):
9100 av.linkTo(self, "submission")
9101 self.notifyModification(raiseEvent = False)
9103 def _grantSubmissionEmail(self, email):
9104 """Returns True if submission email was granted. False if email was already in the list.
9106 if not email.lower() in map(lambda x: x.lower(), self.getSubmitterEmailList()):
9107 self.getSubmitterEmailList().append(email.lower().strip())
9108 return True
9109 return False
9111 def revokeSubmissionEmail(self, email):
9112 if email in self.getSubmitterEmailList():
9113 self.getSubmitterEmailList().remove(email)
9114 self._p_changed=1
9116 def grantSubmission(self, sb, sendEmail=True):
9117 """Grants a user with submission privileges for the contribution
9118 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9120 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9121 ah = AvatarHolder()
9122 results=ah.match({"email":sb.getEmail()}, exact=1, searchInAuthenticators=False)
9123 if not results:
9124 results=ah.match({"email":sb.getEmail()}, exact=1)
9125 r=None
9126 for i in results:
9127 if i.hasEmail(sb.getEmail()):
9129 break
9130 if r and r.isActivated():
9131 self._grantSubmission(r)
9132 elif sb.getEmail():
9133 self.getConference().getPendingQueuesMgr().addPendingSubmitter(sb, False)
9134 submissionEmailGranted = self._grantSubmissionEmail(sb.getEmail())
9135 if submissionEmailGranted and sendEmail:
9136 notif = pendingQueues._PendingSubmitterNotification( [sb] )
9137 mail.GenericMailer.sendAndLog(notif, self.getConference(), 'Contribution')
9138 if self.getConference():
9139 self.getConference().addContribSubmitter(self,sb)
9140 else:
9141 self._grantSubmission(sb)
9143 def _revokeSubmission(self, av):
9144 if av in self.getSubmitterList():
9145 self.getSubmitterList().remove(av)
9146 if self.getConference():
9147 self.getConference().removeContribSubmitter(self, av)
9148 if isinstance(av, AvatarUserWrapper):
9149 av.unlinkTo(self, "submission")
9150 self.notifyModification(raiseEvent = False)
9152 def revokeSubmission(self, sb):
9153 """Removes submission privileges for the specified user
9154 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9156 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9157 ah = AvatarHolder()
9158 results = ah.match({"email": sb.getEmail()}, exact=1, searchInAuthenticators=False)
9159 r = None
9160 for i in results:
9161 if i.hasEmail(sb.getEmail()):
9163 break
9164 if r:
9165 self._revokeSubmission(r)
9166 else:
9167 self.revokeSubmissionEmail(sb.getEmail())
9168 else:
9169 self._revokeSubmission(sb)
9171 def revokeAllSubmitters(self):
9172 self._submitters = []
9173 self.notifyModification(raiseEvent = False)
9175 def getSubmitterEmailList(self):
9176 try:
9177 return self._submittersEmail
9178 except:
9179 self._submittersEmail = []
9180 return self._submittersEmail
9182 def canUserSubmit(self, sb):
9183 """Tells whether a user can submit material for the current contribution
9184 - sb: can be an Avatar or an Author (primary author, co-author, speaker)
9186 if sb is None:
9187 return False
9189 if isinstance(sb, ContributionParticipation) or isinstance(sb, SubContribParticipation):
9190 sbEmail = sb.getEmail()
9192 # Normally, we shouldn't get here unless we're adding someone as a Speaker or similar.
9193 # `no_groups` is used so that we do not consider group membership, as to not confuse the
9194 # user (since there will be speakers with "implicit" privileges) and avoid that hasEmail breaks
9195 return any(submitter.hasEmail(sbEmail) for submitter in self.getSubmitterList(no_groups=True)) or \
9196 any(submitterEmail == sbEmail for submitterEmail in self.getSubmitterEmailList())
9198 for principal in self.getSubmitterList():
9199 if principal != None and principal.containsUser(sb):
9200 return True
9202 return False
9204 def getAccessController(self):
9205 return self.__ac
9207 def getReportNumberHolder(self):
9208 try:
9209 if self._reportNumberHolder:
9210 pass
9211 except AttributeError, e:
9212 self._reportNumberHolder=ReportNumberHolder(self)
9213 return self._reportNumberHolder
9215 def setReportNumberHolder(self, rnh):
9216 self._reportNumberHolder=rnh
9218 @classmethod
9219 def contributionStartDateForSort(cls, contribution):
9220 """ Function that can be used as "key" argument to sort a list of contributions by start date
9221 The contributions with no start date will be at the end with this sort
9223 if contribution.getStartDate():
9224 return contribution.getStartDate()
9225 else:
9226 return maxDatetime()
9228 def getColor(self):
9229 res=""
9230 if self.getSession() is not None:
9231 res=self.getSession().getColor()
9232 return res
9234 def getTextColor(self):
9235 res=""
9236 if self.getSession() is not None:
9237 res=self.getSession().getTextColor()
9238 return res
9241 class AcceptedContribution(Contribution):
9242 """This class represents a contribution which has been created from an
9243 abstract
9246 def __init__(self, abstract):
9247 Contribution.__init__(self)
9248 abstract.getConference().addContribution(self, abstract.getId())
9249 self._abstract = abstract
9250 self.setTitle(abstract.getTitle())
9251 self._setFieldsFromAbstract()
9252 if isinstance(abstract.getCurrentStatus(), review.AbstractStatusAccepted):
9253 self.setTrack(abstract.getCurrentStatus().getTrack())
9254 self.setType(abstract.getCurrentStatus().getType())
9255 for auth in abstract.getAuthorList():
9256 c_auth = ContributionParticipation()
9257 self._setAuthorValuesFromAbstract(c_auth, auth)
9258 if abstract.isPrimaryAuthor(auth):
9259 self.addPrimaryAuthor(c_auth)
9260 else:
9261 self.addCoAuthor(c_auth)
9262 if abstract.isSpeaker(auth):
9263 self.addSpeaker(c_auth)
9264 self._grantSubmission(self.getAbstract().getSubmitter().getUser())
9266 def _setAuthorValuesFromAbstract(self, cAuth, aAuth):
9267 cAuth.setTitle(aAuth.getTitle())
9268 cAuth.setFirstName(aAuth.getFirstName())
9269 cAuth.setFamilyName(aAuth.getSurName())
9270 cAuth.setEmail(aAuth.getEmail())
9271 cAuth.setAffiliation(aAuth.getAffiliation())
9272 cAuth.setAddress(aAuth.getAddress())
9273 cAuth.setPhone(aAuth.getTelephone())
9275 def _setFieldsFromAbstract(self):
9276 for k, v in self._abstract.getFields().iteritems():
9277 self.setField(k, v)
9279 def getAbstract(self):
9280 return self._abstract
9282 def setAbstract(self, abs):
9283 self._abstract = abs
9285 def getSubmitterList(self, no_groups=False):
9286 try:
9287 if self._submitters:
9288 pass
9289 except AttributeError:
9290 self._submitters = [] # create the attribute
9291 self._grantSubmission(self.getAbstract().getSubmitter().getUser())
9292 if no_groups:
9293 return [s for s in self._submitters if not isinstance(s, GroupWrapper)]
9294 else:
9295 return self._submitters
9297 def delete(self):
9298 """deletes a contribution and all of their subitems
9300 abs = self.getAbstract()
9301 if abs:
9302 cs = abs.getCurrentStatus()
9303 if isinstance(cs, review.AbstractStatusAccepted):
9304 if cs.getTrack() is not None:
9305 abs.addTrack(cs.getTrack())
9306 abs.setCurrentStatus(review.AbstractStatusSubmitted(abs))
9307 abs._setContribution(None)
9308 self.setAbstract(None)
9309 Contribution.delete(self)
9312 class ContribStatus(Persistent):
9316 def __init__(self,contribution,responsible):
9317 self._setContrib(contribution)
9318 self._setResponsible(responsible)
9319 self._setDate()
9321 def clone(self, contribution, responsible):
9322 cs = ContribStatus(contribution, responsible)
9323 cs.setDate(self.getDate())
9324 return cs
9326 def _setContrib(self,newContrib):
9327 self._contrib=newContrib
9329 def getContrib(self):
9330 return self._contrib
9332 def _setResponsible(self,newResp):
9333 self._responsible=newResp
9335 def getResponsible(self):
9336 return self._responsible
9338 def _setDate(self):
9339 self._date=nowutc()
9341 def setDate(self, date):
9342 self._date = date
9344 def getDate(self):
9345 return self._date
9347 def withdraw(self,resp,comments=""):
9348 self._contrib.setStatus(ContribStatusWithdrawn(self.getContrib(),resp,comments))
9350 class ContribStatusNotSch(ContribStatus):
9353 def __init__(self,contrib):
9354 ContribStatus.__init__(self,contrib,None)
9356 def clone(self, contribution):
9357 csns = ContribStatusNotSch(contribution)
9358 csns.setDate(self.getDate())
9359 return csns
9361 ContribStatusSubmitted=ContribStatusNotSch
9363 class ContribStatusSch(ContribStatus):
9366 def __init__(self,contrib):
9367 ContribStatus.__init__(self,contrib,None)
9369 def clone(self, contribution):
9370 css = ContribStatusSch(contribution)
9371 css.setDate(self.getDate())
9372 return css
9374 class ContribStatusWithdrawn(ContribStatus):
9377 def __init__(self,contrib,resp,comments):
9378 ContribStatus.__init__(self,contrib,resp)
9379 self._setComment(comments)
9381 def clone(self, contribution):
9382 csw = ContribStatusWithdrawn(contribution)
9383 csw.setDate(self.getDate())
9384 csw.setComment(self.getComment())
9385 return csw
9387 def _setComment(self,text):
9388 self._comment=text.strip()
9390 def getComment(self):
9391 return self._comment
9393 class ContribStatusNone(ContribStatus):
9394 # This is a special status we assign to contributions that are put in the trash can.
9396 def __init__(self,contrib):
9397 ContribStatus.__init__(self,contrib,None)
9399 def clone(self, contribution):
9400 csn = ContribStatusNone(contribution)
9401 csn.setDate(self.getDate())
9402 return csn
9404 class SubContribParticipation(Persistent, Fossilizable):
9406 fossilizes(ISubContribParticipationFossil, ISubContribParticipationFullFossil)
9408 def __init__( self ):
9409 self._subContrib = None
9410 self._id = ""
9411 self._firstName = ""
9412 self._surName = ""
9413 self._email = ""
9414 self._affiliation = ""
9415 self._address = ""
9416 self._phone = ""
9417 self._title = ""
9418 self._fax = ""
9420 def getConference(self):
9421 if self._subContrib is not None:
9422 return self._subContrib.getConference()
9423 return None
9425 def _notifyModification( self ):
9426 if self._subContrib != None:
9427 self._subContrib.notifyModification()
9429 def setValues(self, data):
9430 self.setFirstName(data.get("firstName", ""))
9431 self.setFamilyName(data.get("familyName",""))
9432 self.setAffiliation(data.get("affilation",""))
9433 self.setAddress(data.get("address",""))
9434 self.setEmail(data.get("email",""))
9435 self.setFax(data.get("fax",""))
9436 self.setTitle(data.get("title",""))
9437 self.setPhone(data.get("phone",""))
9438 self._notifyModification()
9440 def getValues(self):
9441 data={}
9442 data["firstName"]=self.getFirstName()
9443 data["familyName"]=self.getFamilyName()
9444 data["affilation"]=self.getAffiliation()
9445 data["address"]=self.getAddress()
9446 data["email"]=self.getEmail()
9447 data["fax"]=self.getFax()
9448 data["title"]=self.getTitle()
9449 data["phone"]=self.getPhone()
9450 return data
9452 def clone(self):
9453 part = SubContribParticipation()
9454 part.setValues(self.getValues())
9455 return part
9457 def setDataFromAvatar(self,av):
9458 # av is an Avatar object.
9459 if av is None:
9460 return
9461 self.setFirstName(av.getName())
9462 self.setFamilyName(av.getSurName())
9463 self.setEmail(av.getEmail())
9464 self.setAffiliation(av.getOrganisation())
9465 self.setAddress(av.getAddress())
9466 self.setPhone(av.getTelephone())
9467 self.setTitle(av.getTitle())
9468 self.setFax(av.getFax())
9469 self._notifyModification()
9471 def setDataFromAuthor(self,au):
9472 # au is a ContributionParticipation object.
9473 if au is None:
9474 return
9475 self.setFirstName(au.getFirstName())
9476 self.setFamilyName(au.getFamilyName())
9477 self.setEmail(au.getEmail())
9478 self.setAffiliation(au.getAffiliation())
9479 self.setAddress(au.getAddress())
9480 self.setPhone(au.getPhone())
9481 self.setTitle(au.getTitle())
9482 self.setFax(au.getFax())
9483 self._notifyModification()
9485 def setDataFromSpeaker(self,spk):
9486 # spk is a SubContribParticipation object.
9487 if spk is None:
9488 return
9489 self.setFirstName(spk.getFirstName())
9490 self.setFamilyName(spk.getFamilyName())
9491 self.setEmail(spk.getEmail())
9492 self.setAffiliation(spk.getAffiliation())
9493 self.setAddress(spk.getAddress())
9494 self.setPhone(spk.getPhone())
9495 self.setTitle(spk.getTitle())
9496 self.setFax(spk.getFax())
9497 self._notifyModification()
9499 def includeInSubContrib( self, subcontrib, id ):
9500 if self.getSubContrib() == subcontrib and self.getId()==id.strip():
9501 return
9502 self._subContrib = subcontrib
9503 self._id = id
9505 def delete( self ):
9506 self._subContrib = None
9507 TrashCanManager().add(self)
9509 def recover(self):
9510 TrashCanManager().remove(self)
9512 @Updates ('MaKaC.conference.SubContribParticipation', 'id')
9513 def setId(self, newId):
9514 self._id = newId
9516 def getId( self ):
9517 return self._id
9519 def getSubContrib( self ):
9520 return self._subContrib
9522 def getContribution( self ):
9523 if self._subContrib is not None:
9524 return self._subContrib.getContribution()
9525 return None
9527 def _unindex(self):
9528 contrib=self.getContribution()
9529 if contrib is not None:
9530 conf=contrib.getConference()
9531 if conf is not None:
9532 conf.unindexAuthor(self)
9533 conf.unindexSpeaker(self)
9535 def _index(self):
9536 contrib=self.getContribution()
9537 if contrib is not None:
9538 conf=contrib.getConference()
9539 if conf is not None:
9540 conf.indexAuthor(self)
9541 conf.indexSpeaker(self)
9543 @Updates ('MaKaC.conference.SubContribParticipation', 'firstName')
9544 def setFirstName( self, newName ):
9545 tmp=newName.strip()
9546 if tmp==self._firstName:
9547 return
9548 self._unindex()
9549 self._firstName=tmp
9550 self._index()
9551 self._notifyModification()
9553 def getFirstName( self ):
9554 return self._firstName
9556 def getName( self ):
9557 return self._firstName
9559 @Updates ('MaKaC.conference.SubContribParticipation', 'familyName')
9560 def setFamilyName( self, newName ):
9561 tmp=newName.strip()
9562 if tmp==self._surName:
9563 return
9564 self._unindex()
9565 self._surName=tmp
9566 self._index()
9567 self._notifyModification()
9569 def getFamilyName( self ):
9570 return self._surName
9572 def getSurName( self ):
9573 return self._surName
9575 @Updates ('MaKaC.conference.SubContribParticipation', 'email')
9576 def setEmail( self, newMail ):
9577 tmp=newMail.strip()
9578 if tmp==self._email:
9579 return
9580 self._unindex()
9581 self._email=newMail.strip()
9582 self._index()
9583 self._notifyModification()
9585 def getEmail( self ):
9586 return self._email
9588 @Updates ('MaKaC.conference.SubContribParticipation', 'affiliation')
9589 def setAffiliation( self, newAffil ):
9590 self._affiliation = newAffil.strip()
9591 self._notifyModification()
9593 def getAffiliation( self ):
9594 return self._affiliation
9596 @Updates ('MaKaC.conference.SubContribParticipation', 'address')
9597 def setAddress( self, newAddr ):
9598 self._address = newAddr.strip()
9599 self._notifyModification()
9601 def getAddress( self ):
9602 return self._address
9604 @Updates ('MaKaC.conference.SubContribParticipation', 'phone')
9605 def setPhone( self, newPhone ):
9606 self._phone = newPhone.strip()
9607 self._notifyModification()
9609 def getPhone( self ):
9610 return self._phone
9612 @Updates ('MaKaC.conference.SubContribParticipation', 'title')
9613 def setTitle( self, newTitle ):
9614 self._title = newTitle.strip()
9615 self._notifyModification()
9617 def getTitle( self ):
9618 return self._title
9620 def setFax( self, newFax ):
9621 self._fax = newFax.strip()
9622 self._notifyModification()
9624 def getFax( self ):
9625 try:
9626 if self._fax:
9627 pass
9628 except AttributeError:
9629 self._fax=""
9630 return self._fax
9632 def getFullName( self ):
9633 res = self.getFullNameNoTitle()
9634 if self.getTitle() != "":
9635 res = "%s %s"%( self.getTitle(), res )
9636 return res
9638 def getFullNameNoTitle(self):
9639 res = safe_upper(self.getFamilyName())
9640 if self.getFirstName():
9641 if res.strip():
9642 res = "%s, %s" % (res, self.getFirstName())
9643 else:
9644 res = self.getFirstName()
9645 return res
9647 def getAbrName(self):
9648 res = self.getFamilyName()
9649 if self.getFirstName():
9650 if res:
9651 res = "%s, " % res
9652 res = "%s%s." % (res, safe_upper(self.getFirstName()[0]))
9653 return res
9655 def getDirectFullName(self):
9656 res = self.getDirectFullNameNoTitle()
9657 if self.getTitle():
9658 res = "%s %s" % (self.getTitle(), res)
9659 return res
9661 def getDirectFullNameNoTitle(self, upper=True):
9662 surName = safe_upper(self.getFamilyName()) if upper else self.getFamilyName()
9663 return "{0} {1}".format(self.getFirstName(), surName).strip()
9666 class SubContribution(CommonObjectBase, Locatable):
9670 fossilizes(ISubContributionFossil, ISubContributionWithSpeakersFossil)
9672 def __init__( self, **subContData ):
9673 self.parent = None
9674 self.id = ""
9675 self.title = ""
9676 self.description = ""
9677 self.__schEntry = None
9678 self.duration = timedelta( minutes=15 )
9679 self.speakers = []
9680 self.speakerText = ""
9682 self.materials = {}
9683 self.__materialGenerator = Counter() # Provides material unique
9684 # identifiers whithin the current
9685 self.poster = None # contribution
9686 self.paper = None
9687 self.slides = None
9688 self.video = None
9689 self.poster = None
9690 self.minutes = None
9691 self._authorGen = Counter()
9692 self._keywords = ""
9694 @return_ascii
9695 def __repr__(self):
9696 if self.parent:
9697 parent_id = self.parent.getId()
9698 event_id = self.getConference().getId() if self.getConference() else None
9699 else:
9700 parent_id = None
9701 event_id = None
9702 return '<SubContribution({}, {}, {}.{})>'.format(self.getId(), self.getTitle(), event_id, parent_id)
9704 @property
9705 @memoize_request
9706 def note(self):
9707 from indico.modules.events.notes.models.notes import EventNote
9708 return EventNote.get_for_linked_object(self)
9710 def updateNonInheritingChildren(self, elem, delete=False):
9711 self.getOwner().updateNonInheritingChildren(elem, delete)
9713 def getAccessController(self):
9714 return self.getOwner().getAccessController()
9716 def getKeywords(self):
9717 try:
9718 return self._keywords
9719 except:
9720 self._keywords = ""
9721 return ""
9723 def setKeywords(self, keywords):
9724 self._keywords = keywords
9726 def getLogInfo(self):
9727 data = {}
9729 data["subject"] = self.getTitle()
9730 data["id"] = self.id
9731 data["title"] = self.title
9732 data["parent title"] = self.getParent().getTitle()
9733 data["description"] = self.description
9734 data["duration"] = "%s"%self.duration
9735 data["minutes"] = self.minutes
9737 for sp in self.speakers :
9738 data["speaker %s"%sp.getId()] = sp.getFullName()
9740 return data
9743 def clone(self, deltaTime, parent, options):
9744 sCont = SubContribution()
9745 sCont.setParent(parent)
9746 sCont.setTitle(self.getTitle())
9747 sCont.setDescription(self.getDescription())
9748 sCont.setKeywords(self.getKeywords())
9749 dur = self.getDuration()
9750 hours = dur.seconds / 3600
9751 minutes = (dur.seconds % 3600) / 60
9752 sCont.setDuration(hours, minutes)
9753 sCont.setReportNumberHolder(self.getReportNumberHolder().clone(self))
9755 # There is no _order attribute in this class
9757 if options.get("authors", False) :
9758 for s in self.getSpeakerList() :
9759 sCont.newSpeaker(s.clone())
9760 sCont.setSpeakerText(self.getSpeakerText())
9762 if options.get("materials", False) :
9763 for m in self.getMaterialList() :
9764 sCont.addMaterial(m.clone(sCont))
9765 if self.getPaper() is not None:
9766 sCont.setPaper(self.getPaper().clone(sCont))
9767 if self.getSlides() is not None:
9768 sCont.setSlides(self.getSlides().clone(sCont))
9769 if self.getVideo() is not None:
9770 sCont.setVideo(self.getVideo().clone(sCont))
9771 if self.getPoster() is not None:
9772 sCont.setPoster(self.getPoster().clone(sCont))
9775 sCont.notifyModification()
9776 return sCont
9778 def notifyModification(self, raiseEvent = True):
9779 parent = self.getParent()
9780 if parent:
9781 parent.setModificationDate()
9782 if raiseEvent:
9783 signals.event.subcontribution_data_changed.send(self)
9784 self._p_changed = 1
9786 def getCategoriesPath(self):
9787 return self.getConference().getCategoriesPath()
9789 def getLocator( self ):
9790 """Gives back a globaly unique identification encapsulated in a Locator
9791 object for the contribution instance
9794 lconf = self.getOwner().getLocator()
9795 lconf["subContId"] = self.getId()
9796 return lconf
9799 def setId( self, newId ):
9800 self.id = newId
9802 def getId( self ):
9803 return self.id
9805 def getUniqueId( self ):
9806 """returns (string) the unique identifier of the item"""
9807 """used mainly in the web session access key table"""
9808 return "%ssc%s" % (self.getParent().getUniqueId(),self.id)
9810 def setTitle( self, newTitle ):
9811 old_title = self.title
9812 self.title = newTitle.strip()
9813 if old_title != self.title:
9814 signals.event.subcontribution_title_changed.send(self, old=old_title, new=self.title)
9815 self.notifyModification()
9817 def getTitle( self ):
9818 if self.title.strip() == "":
9819 return "(no title)"
9820 return self.title
9822 def setDescription( self, newDesc ):
9823 self.description = newDesc.strip()
9824 self.notifyModification()
9826 def getDescription( self ):
9827 return self.description
9829 def setParent(self,parent):
9830 self.parent = parent
9831 if self.parent == None:
9832 return
9834 def getParent( self ):
9835 return self.parent
9837 def setOwner(self, owner):
9838 self.setParent(owner)
9840 def getOwner( self ):
9841 return self.getParent()
9843 def getConference( self ):
9844 return self.parent.getConference()
9846 def getSession( self ):
9847 return self.parent.getSession()
9849 def getContribution(self):
9850 return self.parent
9852 def getDuration( self ):
9853 return self.duration
9855 def setDuration( self, hours, minutes=0, dur=0 ):
9856 if dur!=0:
9857 self.duration=dur
9858 else:
9859 hours = int( hours )
9860 minutes = int( minutes )
9861 self.duration = timedelta(hours=hours,minutes=minutes )
9862 self.notifyModification()
9864 def getLocation( self ):
9865 return self.getOwner().getLocation()
9867 def getRoom( self ):
9868 return self.getOwner().getRoom()
9870 def getSpeakerById( self, id ):
9873 for spk in self.speakers:
9874 if spk.getId() == id:
9875 return spk
9876 return None
9878 def newSpeaker( self, spk ):
9881 self.speakers.append( spk )
9882 try:
9883 if self._authorGen:
9884 pass
9885 except AttributeError:
9886 self._authorGen=Counter()
9887 newId = spk.getId()
9888 if newId == "":
9889 newId = str( self._authorGen.newCount() )
9890 spk.includeInSubContrib(self, newId)
9891 if self.getConference() is not None:
9892 self.getConference().indexSpeaker(spk)
9893 self.notifyModification()
9895 def removeSpeaker( self, spk ):
9898 if spk not in self.speakers:
9899 return
9900 self.speakers.remove( spk )
9901 if self.getConference() is not None:
9902 self.getConference().unindexSpeaker(spk)
9903 spk.delete()
9904 self.notifyModification()
9906 def recoverSpeaker(self, spk):
9907 self.newSpeaker(spk)
9908 spk.recover()
9910 def isSpeaker( self, spk):
9913 return spk in self._speakers
9915 def getSpeakerList ( self ):
9918 return self.speakers
9920 def getSpeakerText( self ):
9921 #to be removed
9922 try:
9923 if self.speakerText:
9924 pass
9925 except AttributeError, e:
9926 self.speakerText = ""
9927 return self.speakerText
9929 def setSpeakerText( self, newText ):
9930 self.speakerText = newText.strip()
9932 def appendSpeakerText( self, newText ):
9933 self.setSpeakerText( "%s, %s"%(self.getSpeakerText(), newText.strip()) )
9935 # """
9936 # There is no _order attribute in this class -
9937 # the methods below are either obsolate or the feature has not been implemented
9938 # """
9939 # def setOrder( self, order ):
9940 # self._order = order
9941 # self.notifyModification()
9943 # def getOrder(self):
9944 # return self._order
9946 def canIPAccess( self, ip ):
9947 return self.getOwner().canIPAccess(ip)
9949 def isProtected( self ):
9950 return self.hasProtectedOwner()
9952 def getAccessProtectionLevel( self ):
9953 return self.getOwner().getAccessProtectionLevel()
9955 def hasAnyProtection( self ):
9956 """Tells whether a subContribution has any kind of protection over it:
9957 access or domain protection.
9959 return self.getOwner().hasAnyProtection()
9961 def getManagerList( self ):
9962 return self.parent.getManagerList()
9964 def hasProtectedOwner( self ):
9965 if self.getOwner() != None:
9966 return self.getOwner().isProtected()
9967 return False
9969 def getAccessKey( self ):
9970 return self.getOwner().getAccessKey()
9972 def getModifKey( self ):
9973 return self.getConference().getModifKey()
9975 def canView( self, aw ):
9976 """tells whether the specified user has access to the current object
9977 or any of its sub-objects
9979 if self.canAccess( aw ):
9980 return True
9981 return False
9983 def isAllowedToAccess( self, user ):
9984 return self.parent.isAllowedToAccess( user )
9986 def canAccess( self, aw ):
9987 return self.getOwner().canAccess(aw)
9989 def canModify(self, aw_or_user):
9990 if hasattr(aw_or_user, 'getUser'):
9991 aw_or_user = aw_or_user.getUser()
9992 return self.canUserModify(aw_or_user) or self.getConference().canKeyModify()
9994 def canUserModify( self, av ):
9995 """Tells whether a user is allowed to modify the current contribution:
9996 only if the user is granted to modify the contribution or the user
9997 can modify any of its upper objects (i.e. conference or session).
9999 return self.getParent().canUserModify( av )
10001 def canUserSubmit( self, av ):
10002 return self.getOwner().canUserSubmit( av )
10004 def getAllowedToAccessList( self ):
10005 """Currently the SubContribution class has no access list.
10006 But instead of returning the owner Contribution's access list,
10007 I am returning an empty list. Methods such as getRecursiveAllowedToAccess()
10008 will call the owner Contribution anyway.
10010 return []
10012 def addMaterial( self, newMat ):
10013 newMat.setId( str(self.__materialGenerator.newCount()) )
10014 newMat.setOwner( self )
10015 self.materials[ newMat.getId() ] = newMat
10016 self.notifyModification()
10018 def removeMaterial( self, mat):
10019 if mat.getId() in self.materials.keys():
10020 mat.delete()
10021 self.materials[mat.getId()].setOwner(None)
10022 del self.materials[ mat.getId() ]
10023 self.notifyModification()
10024 elif mat.getId().lower() == 'paper':
10025 self.removePaper()
10026 self.notifyModification()
10027 elif mat.getId().lower() == 'slides':
10028 self.removeSlides()
10029 self.notifyModification()
10030 elif mat.getId().lower() == 'video':
10031 self.removeVideo()
10032 self.notifyModification()
10033 elif mat.getId().lower() == 'poster':
10034 self.removePoster()
10035 self.notifyModification()
10037 def recoverMaterial(self, recMat):
10038 # Id must already be set in recMat.
10039 recMat.setOwner( self )
10040 self.materials[ recMat.getId() ] = recMat
10041 recMat.recover()
10042 self.notifyModification()
10044 def getMaterialRegistry(self):
10046 Return the correct material registry for this type
10048 from MaKaC.webinterface.materialFactories import SubContributionMFRegistry
10049 return SubContributionMFRegistry
10051 def getMaterialById( self, matId ):
10052 if matId.lower() == 'paper':
10053 return self.getPaper()
10054 elif matId.lower() == 'slides':
10055 return self.getSlides()
10056 elif matId.lower() == 'video':
10057 return self.getVideo()
10058 elif matId.lower() == 'poster':
10059 return self.getPoster()
10060 elif self.materials.has_key(matId):
10061 return self.materials[ matId ]
10062 return None
10064 def getMaterialList( self ):
10065 return self.materials.values()
10067 def getAllMaterialList(self, sort=True):
10068 l = self.getMaterialList()
10069 if self.getPaper():
10070 l.append( self.getPaper() )
10071 if self.getSlides():
10072 l.append( self.getSlides() )
10073 if self.getVideo():
10074 l.append( self.getVideo() )
10075 if self.getPoster():
10076 l.append( self.getPoster() )
10077 if sort:
10078 l.sort(lambda x,y: cmp(x.getTitle(),y.getTitle()))
10079 return l
10081 def setPaper( self, newPaper ):
10082 if self.getPaper() != None:
10083 raise MaKaCError( _("The paper for this subcontribution has already been set"), _("Contribution"))
10084 self.paper=newPaper
10085 self.paper.setOwner( self )
10086 self.notifyModification()
10088 def removePaper( self ):
10089 if self.getPaper() is None:
10090 return
10091 self.paper.delete()
10092 self.paper.setOwner(None)
10093 self.paper = None
10094 self.notifyModification()
10096 def recoverPaper(self, p):
10097 self.setPaper(p)
10098 p.recover()
10100 def getPaper( self ):
10101 return self.paper
10103 def setSlides( self, newSlides ):
10104 if self.getSlides() != None:
10105 raise MaKaCError( _("The slides for this subcontribution have already been set"), _("Contribution"))
10106 self.slides=newSlides
10107 self.slides.setOwner( self )
10108 self.notifyModification()
10110 def removeSlides( self ):
10111 if self.getSlides() is None:
10112 return
10113 self.slides.delete()
10114 self.slides.setOwner( None )
10115 self.slides = None
10116 self.notifyModification()
10118 def recoverSlides(self, s):
10119 self.setSlides(s)
10120 s.recover()
10122 def getSlides( self ):
10123 return self.slides
10125 def setVideo( self, newVideo ):
10126 if self.getVideo() != None:
10127 raise MaKaCError( _("the video for this subcontribution has already been set"))
10128 self.video=newVideo
10129 self.video.setOwner( self )
10130 self.notifyModification()
10132 def removeVideo( self ):
10133 if self.getVideo() is None:
10134 return
10135 self.video.delete()
10136 self.video.setOwner(None)
10137 self.video = None
10138 self.notifyModification()
10140 def recoverVideo(self, v):
10141 self.setVideo(v)
10142 v.recover()
10144 def getVideo( self ):
10145 try:
10146 if self.video:
10147 pass
10148 except AttributeError:
10149 self.video = None
10150 return self.video
10152 def setPoster( self, newPoster ):
10153 if self.getPoster() != None:
10154 raise MaKaCError( _("the poster for this subcontribution has already been set"))
10155 self.poster=newPoster
10156 self.poster.setOwner( self )
10157 self.notifyModification()
10159 def removePoster( self ):
10160 if self.getPoster() is None:
10161 return
10162 self.poster.delete()
10163 self.poster.setOwner(None)
10164 self.poster = None
10165 self.notifyModification()
10167 def recoverPoster(self, p):
10168 self.setPoster(p)
10169 p.recover()
10171 def getPoster( self ):
10172 try:
10173 if self.poster:
10174 pass
10175 except AttributeError:
10176 self.poster = None
10177 return self.poster
10179 def getMasterSchedule( self ):
10180 return self.getOwner().getSchedule()
10182 def delete(self):
10183 signals.event.subcontribution_deleted.send(self, parent=self.getOwner())
10185 while len(self.getSpeakerList()) > 0:
10186 self.removeSpeaker(self.getSpeakerList()[0])
10187 for mat in self.getMaterialList():
10188 self.removeMaterial(mat)
10189 self.removePaper()
10190 self.removeSlides()
10191 self.removeVideo()
10192 self.removePoster()
10193 TrashCanManager().add(self)
10195 #self.unindex()
10197 def recover(self):
10198 TrashCanManager().remove(self)
10200 def getReportNumberHolder(self):
10201 try:
10202 if self._reportNumberHolder:
10203 pass
10204 except AttributeError, e:
10205 self._reportNumberHolder=ReportNumberHolder(self)
10206 return self._reportNumberHolder
10208 def setReportNumberHolder(self, rnh):
10209 self._reportNumberHolder=rnh
10211 class Material(CommonObjectBase):
10212 """This class represents a set of electronic documents (resources) which can
10213 be attached to a conference, a session or a contribution.
10214 A material can be of several types (achieved by specialising this class)
10215 and is like a container of files which have some relation among them.
10216 It contains the minimal set of attributes to store basic meta data and
10217 provides useful operations to access and manage it.
10218 Attributes:
10219 owner -- (Conference, Session or Contribution) Object to which the
10220 material is attached to
10221 id -- (string) Material unique identifier. Normally used to uniquely
10222 identify a material within a conference, session or contribution
10223 title -- (string) Material denomination
10224 description -- (string) Longer text describing in more detail material
10225 intentions
10226 type -- (string) String identifying the material classification
10227 resources -- (PMapping) Collection of resouces grouped within the
10228 material. Dictionary of references to Resource objects indexed
10229 by their unique relative id.
10232 fossilizes(IMaterialMinimalFossil, IMaterialFossil)
10234 def __init__( self, materialData=None ):
10235 self.id = "not assigned"
10236 self.__resources = {}
10237 self.__resourcesIdGen = Counter()
10238 self.title = ""
10239 self.description = ""
10240 self.type = ""
10241 self.owner = None
10242 self.__ac = AccessController(self)
10243 self._mainResource = None
10245 def __cmp__(self, other):
10246 if type(self) is not type(other):
10247 # This is actually dangerous and the ZODB manual says not to do this
10248 # because it relies on memory order. However, this branch should never
10249 # be taken anyway since we do not store different types in the same set
10250 # or use them as keys.
10251 return cmp(hash(self), hash(other))
10252 if self.getConference() == other.getConference():
10253 if self.getId().isdigit() and other.getId().isdigit():
10254 return cmp(int(self.getId()), int(other.getId()))
10255 else:
10256 return cmp(self.getId(), other.getId())
10257 return cmp(self.getConference(), other.getConference())
10259 def updateNonInheritingChildren(self, elem, delete=False):
10260 # We do not want to store the inherited children in a Category because the funcionallity is not used
10261 if not isinstance(self.getOwner(), Category):
10262 self.getAccessController().updateNonInheritingChildren(elem, delete)
10263 self.notify_protection_to_owner(elem, delete)
10265 def notify_protection_to_owner(self, elem, delete=False):
10266 self.getOwner().updateNonInheritingChildren(elem, delete)
10268 def setValues( self, params ):
10269 """Sets all the values of the current material object from a diccionary
10270 containing the following key-value pairs:
10271 title-(str)
10272 description-(str)
10273 Please, note that this method sets ALL values which means that if
10274 the given dictionary doesn't contain any of the keys the value
10275 will set to a default value.
10277 self.setTitle(params.get("title", "NO TITLE ASSIGNED"))
10278 self.setDescription( params.get( "description", "" ) )
10279 self.notifyModification()
10281 def clone ( self, owner):
10282 mat = type(self)()
10283 mat.setTitle(self.getTitle())
10284 mat.setDescription(self.getDescription())
10285 mat.notifyModification()
10287 mat.setId(self.getId())
10288 mat.setOwner(owner)
10289 mat.setType(self.getType())
10291 mat.setProtection(self.getAccessController()._getAccessProtection())
10292 mat.setAccessKey(self.getAccessKey())
10293 rlist = self.getResourceList()
10294 for r in rlist:
10295 newres = r.clone(mat)
10296 mat.addResource(newres)
10298 mat.setMainResource(self.getMainResource())
10300 return mat
10302 def notifyModification( self ):
10303 parent = self.getOwner()
10304 if parent:
10305 parent.notifyModification(raiseEvent = False)
10306 self._p_changed = 1
10308 def getLocator( self ):
10309 if self.owner == None:
10310 return Locator()
10311 lconf = self.owner.getLocator()
10312 lconf["materialId"] = self.getId()
10313 return lconf
10315 def setId( self, newId ):
10316 self.id = str(newId).strip()
10318 def getId( self ):
10319 return self.id
10321 def getUniqueId( self ):
10322 """returns (string) the unique identifier of the item"""
10323 """used mainly in the web session access key table"""
10324 return "%sm%s" % (self.getOwner().getUniqueId(),self.id)
10326 def setOwner(self, newOwner):
10327 self.owner = newOwner
10329 def getOwner( self ):
10330 return self.owner
10332 def getCategory( self ):
10333 if isinstance(self.getOwner(), Category):
10334 return self.getOwner()
10335 return None
10337 def getConference( self ):
10338 owner = self.getOwner()
10339 if owner is None or isinstance(owner, Category):
10340 return None
10341 elif isinstance(owner, Conference):
10342 return owner
10343 else:
10344 return owner.getConference()
10346 def getSession( self ):
10347 if self.getContribution():
10348 return self.getContribution().getSession()
10349 if isinstance(self.getOwner(), Session):
10350 return self.getOwner()
10351 if isinstance(self.getOwner(), SubContribution):
10352 return self.getOwner().getSession()
10353 return None
10355 def getContribution( self ):
10356 if self.getSubContribution():
10357 return self.getSubContribution().getContribution()
10358 if isinstance(self.getOwner(), Contribution):
10359 return self.getOwner()
10360 return None
10362 def getSubContribution( self ):
10363 if isinstance(self.getOwner(), SubContribution):
10364 return self.getOwner()
10365 return None
10367 @Updates (['MaKaC.conference.Material',
10368 'MaKaC.conference.Paper',
10369 'MaKaC.conference.Slides',
10370 'MaKaC.conference.Video',
10371 'MaKaC.conference.Poster',
10372 'MaKaC.conference.Reviewing'],'title')
10373 def setTitle( self, newTitle ):
10374 self.title = newTitle.strip()
10375 self.notifyModification()
10377 def getTitle( self ):
10378 return self.title
10380 @Updates (['MaKaC.conference.Material',
10381 'MaKaC.conference.Paper',
10382 'MaKaC.conference.Slides',
10383 'MaKaC.conference.Video',
10384 'MaKaC.conference.Poster',
10385 'MaKaC.conference.Reviewing'], 'description')
10386 def setDescription( self, newDescription ):
10387 self.description = newDescription.strip()
10388 self.notifyModification()
10390 def getDescription( self ):
10391 return self.description
10393 def setType( self, newType ):
10394 self.type = newType.strip()
10395 self.notifyModification()
10397 def getType( self ):
10398 return self.type
10400 def getReviewingState(self):
10401 """ Returns the reviewing state of a material.
10402 The state is represented by an integer:
10403 0 : there's no reviewing state because the material does not belong to a contribution, or the conference
10404 has not reviewing module enabled, or the module is enabled but the mode is "no reviewing"
10405 1 : the material is not subject to reviewing, because this kind of material is not reviewable in the conference
10406 2 : the material is subject to reviewing, but has not been submitted yet by the author
10407 3 : the material is subject to reviewing, has been submitted by the author, but has not been judged yet
10408 4 : the material is subject to reviewing, has been submitted by the author, and has been judged as Accepted
10409 5 : the material is subject to reviewing, has been submitted by the author, and has been judged as Rejected
10411 if isinstance(self.owner, Contribution):
10412 conference = self.owner.getConference()
10413 if conference.getConfPaperReview().getChoice() == ConferencePaperReview.NO_REVIEWING: #conference has no reviewing process
10414 return 0
10415 else: #conference has reviewing
10416 #if self.id in reviewableMaterials: #material is reviewable
10417 if isinstance(self, Reviewing): #material is reviewable
10418 lastReview = self.owner.getReviewManager().getLastReview()
10419 if lastReview.isAuthorSubmitted(): #author has submitted
10420 refereeJudgement = lastReview.getRefereeJudgement()
10421 if refereeJudgement.isSubmitted(): #referee has submitted judgement
10422 if refereeJudgement.getJudgement() == "Accept":
10423 return 4
10424 elif refereeJudgement.getJudgement() == "Reject":
10425 return 5
10426 else:
10427 #we should never arrive here because referee judgements that are 'To be corrected'
10428 #or a custom state should imply a new review being created, so the state is back to 2
10429 raise MaKaCError("RefereeJudgement should be 'Accept' or 'Reject' in this method")
10430 else: #referee has not submitted judgement
10431 return 3
10432 else: #author has not submitted
10433 return 2
10434 else: #material is not reviewable
10435 return 1
10436 else: #material does not belong to a contribution
10437 return 0
10439 def _getRepository( self ):
10440 dbRoot = DBMgr.getInstance().getDBConnection().root()
10441 try:
10442 fr = dbRoot["local_repositories"]["main"]
10443 except KeyError, e:
10444 fr = fileRepository.MaterialLocalRepository()
10445 dbRoot["local_repositories"] = OOBTree()
10446 dbRoot["local_repositories"]["main"] = fr
10447 return fr
10449 def hasFile( self, name ):
10450 for f in self.getResourceList():
10451 if f.getName() == name:
10452 return True
10453 return False
10455 def addResource( self, newRes, forcedFileId = None ):
10456 newRes.setOwner( self )
10457 newRes.setId( str( self.__resourcesIdGen.newCount() ) )
10458 newRes.archive( self._getRepository(), forcedFileId = forcedFileId )
10459 self.__resources[newRes.getId()] = newRes
10460 self.notifyModification()
10461 Logger.get('storage').debug("Finished storing resource %s for material %s" % (newRes.getId(), self.getLocator()))
10463 def getResourceList(self, sort=True):
10464 list = self.__resources.values()
10465 if sort:
10466 list.sort(utils.sortFilesByName)
10467 return list
10469 def getNbResources(self ):
10470 return len(self.__resources)
10472 def getResourceById( self, id ):
10473 return self.__resources[id]
10475 def removeResource( self, res ):
10476 if res.getId() in self.__resources.keys():
10477 del self.__resources[ res.getId() ]
10478 res.delete()
10479 self.notifyModification()
10480 if self.getMainResource() is not None and \
10481 self._mainResource.getId() == res.getId():
10482 self._mainResource = None
10484 def recoverResource(self, recRes):
10485 recRes.setOwner(self)
10486 self.__resources[recRes.getId()] = recRes
10487 recRes.recover()
10488 self.notifyModification()
10490 def getMainResource(self):
10491 try:
10492 if self._mainResource:
10493 pass
10494 except AttributeError:
10495 self._mainResource = None
10496 return self._mainResource
10498 def setMainResource(self, mr):
10499 self._mainResource = mr
10501 def delete(self):
10502 self.__ac.unlinkAvatars('access')
10503 for res in self.getResourceList():
10504 self.removeResource( res )
10505 if self.getReviewingState():
10506 self.owner._reviewManager = ReviewManager(self.owner)
10507 self.notify_protection_to_owner(self, delete=True)
10508 TrashCanManager().add(self)
10510 def recover(self):
10511 TrashCanManager().remove(self)
10513 def isProtected(self):
10514 # tells if a material is protected or not
10515 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
10517 def getAccessProtectionLevel( self ):
10518 return self.__ac.getAccessProtectionLevel()
10520 def isItselfProtected( self ):
10521 return self.__ac.isItselfProtected()
10524 def hasProtectedOwner( self ):
10525 if self.getOwner() != None:
10526 return self.getOwner().isProtected()
10527 return False
10530 @Updates (['MaKaC.conference.Material',
10531 'MaKaC.conference.Paper',
10532 'MaKaC.conference.Slides',
10533 'MaKaC.conference.Video',
10534 'MaKaC.conference.Poster',
10535 'MaKaC.conference.Reviewing'], 'protection', lambda(x): int(x))
10537 def setProtection( self, private ):
10538 self.__ac.setProtection( private )
10539 self.notify_protection_to_owner(self)
10540 self._p_changed = 1
10542 def isHidden( self ):
10543 return self.__ac.isHidden()
10545 @Updates (['MaKaC.conference.Material',
10546 'MaKaC.conference.Paper',
10547 'MaKaC.conference.Slides',
10548 'MaKaC.conference.Video',
10549 'MaKaC.conference.Poster',
10550 'MaKaC.conference.Reviewing'], 'hidden')
10551 def setHidden( self, hidden ):
10552 self.__ac.setHidden( hidden )
10553 self._p_changed = 1
10556 @Updates (['MaKaC.conference.Material',
10557 'MaKaC.conference.Paper',
10558 'MaKaC.conference.Slides',
10559 'MaKaC.conference.Video',
10560 'MaKaC.conference.Poster',
10561 'MaKaC.conference.Reviewing'], 'accessKey')
10563 def setAccessKey( self, pwd="" ):
10564 self.__ac.setAccessKey(pwd)
10565 self._p_changed = 1
10567 def getAccessKey( self ):
10568 return self.__ac.getAccessKey()
10570 def grantAccess( self, prin ):
10571 self.__ac.grantAccess( prin )
10572 if isinstance(prin, AvatarUserWrapper):
10573 prin.linkTo(self, "access")
10574 self._p_changed = 1
10576 def revokeAccess( self, prin ):
10577 self.__ac.revokeAccess( prin )
10578 if isinstance(prin, AvatarUserWrapper):
10579 prin.unlinkTo(self, "access")
10580 self._p_changed = 1
10582 def canView( self, aw ):
10583 """tells whether the specified user has access to the current object
10584 or any of its sub-objects
10586 if self.isHidden() and not self.canAccess( aw ):
10587 return False
10588 else:
10589 return True
10591 def isAllowedToAccess( self, user ):
10592 return (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user )) or self.__ac.canUserAccess( user ) or self.canUserModify(user)
10594 def canAccess( self, aw ):
10596 # Allow harvesters (Invenio, offline cache) to access
10597 # protected pages
10598 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
10599 return True
10600 #####################################################
10602 # Managers have always access
10603 if self.canModify(aw):
10604 return True
10606 canUserAccess = self.isAllowedToAccess(aw.getUser())
10607 canIPAccess = self.canIPAccess(request.remote_addr)
10608 if not self.isProtected():
10609 return canUserAccess or canIPAccess
10610 else:
10611 canKeyAccess = self.canKeyAccess(aw)
10612 return canUserAccess or canKeyAccess
10614 def canKeyAccess(self, aw):
10615 key = session.get('accessKeys', {}).get(self.getUniqueId())
10616 if self.getAccessKey():
10617 # Material has an access key => require this key
10618 if not key:
10619 return False
10620 return self.__ac.canKeyAccess(key)
10621 elif self.getConference():
10622 # If it has no key we check the conference's key
10623 conf_key = session.get('accessKeys', {}).get(self.getConference().getUniqueId())
10624 return self.getConference().canKeyAccess(aw, conf_key)
10625 return False
10627 def grantModification( self, prin ):
10628 self.__ac.grantModification( prin )
10629 if isinstance(prin, AvatarUserWrapper):
10630 prin.linkTo(self, "manager")
10631 self._p_changed = 1
10633 def revokeModification( self, prin ):
10634 self.__ac.revokeModification( prin )
10635 if isinstance(prin, AvatarUserWrapper):
10636 prin.unlinkTo(self, "manager")
10637 self._p_changed = 1
10639 def canModify(self, aw_or_user):
10640 if hasattr(aw_or_user, 'getUser'):
10641 aw_or_user = aw_or_user.getUser()
10642 return self.canUserModify(aw_or_user) or (self.getConference() and self.getConference().canKeyModify())
10644 def canUserModify( self, user ):
10645 """Tells whether a user is allowed to modify the current contribution:
10646 only if the user is granted to modify the contribution or the user
10647 can modify any of its upper objects (i.e. conference or session).
10649 return self.getOwner().canUserModify( user )
10651 def getModifKey( self ):
10652 return self.getConference().getModifKey()
10654 def getManagerList( self ):
10655 return self.__ac.getModifierList()
10657 def getAllowedToAccessList( self ):
10658 return self.__ac.getAccessList()
10660 def requireDomain( self, dom ):
10661 self.__ac.requireDomain( dom )
10662 self._p_changed = 1
10664 def freeDomain( self, dom ):
10665 self.__ac.freeDomain( dom )
10666 self._p_changed = 1
10668 def getDomainList( self ):
10669 return self.__ac.getRequiredDomainList()
10671 def getAccessController(self):
10672 return self.__ac
10674 def isBuiltin(self):
10675 return False
10677 class BuiltinMaterial(Material):
10679 Non-customizable material types
10681 def isBuiltin(self):
10682 return True
10685 class Reviewing(BuiltinMaterial):
10687 def __init__( self, materialData = None ):
10688 Material.__init__( self, materialData )
10689 self.id = "reviewing"
10691 def setId( self, newId ):
10692 return
10694 def getContribution(self):
10695 if isinstance(self.getOwner(), Review):
10696 return self.getOwner().getContribution()
10697 return Material.getContribution(self)
10699 class Paper(BuiltinMaterial):
10701 def __init__( self, materialData = None ):
10702 Material.__init__( self, materialData )
10703 self.id = "paper"
10705 def setId( self, newId ):
10706 return
10710 class Slides(BuiltinMaterial):
10712 def __init__( self, materialData = None ):
10713 Material.__init__( self, materialData )
10714 self.id = "slides"
10716 def setId( self, newId ):
10717 return
10721 class Video(BuiltinMaterial):
10723 def __init__( self, materialData = None ):
10724 Material.__init__( self, materialData )
10725 self.id = "video"
10727 def setId( self, newId ):
10728 return
10730 class Poster(BuiltinMaterial):
10732 def __init__( self, materialData = None ):
10733 Material.__init__( self, materialData )
10734 self.id = "poster"
10736 def setId( self, newId ):
10737 return
10740 class Resource(CommonObjectBase):
10741 """This is the base class for representing individual resources which can
10742 be included in material containers for lately being attached to
10743 conference objects (i.e. conferences, sessions or contributions). This
10744 class provides basic data and operations to handle this resources.
10745 Resources can be of serveral types (files, links, ...) which means
10746 different specialisations of this class.
10747 Attributes:
10748 id -- (string) Allows to assign the resource a unique identifier. It
10749 is normally used to uniquely identify the resource among other
10750 resources included in a certain material.
10751 name -- (string) Short description about the purpose or the contents
10752 of the resource.
10753 description - (string) detailed and varied information about the
10754 resource.
10755 __owner - (Material) reference to the material object in which the
10756 current resource is included.
10759 fossilizes(IResourceMinimalFossil, IResourceFossil)
10761 def __init__( self, resData = None ):
10762 self.id = "not assigned"
10763 self.name = ""
10764 self.description = ""
10765 self._owner = None
10766 self.__ac = AccessController(self)
10767 self.pdfConversionRequestDate = None
10769 def __cmp__(self, other):
10770 if type(self) is not type(other):
10771 # This is actually dangerous and the ZODB manual says not to do this
10772 # because it relies on memory order. However, this branch should never
10773 # be taken anyway since we do not store different types in the same set
10774 # or use them as keys.
10775 return cmp(hash(self), hash(other))
10776 if self.getConference() == other.getConference():
10777 return cmp(self.getId(), other.getId())
10778 return cmp(self.getConference(), other.getConference())
10780 def clone( self, conf, protection=True ):
10781 res = self.__class__()
10782 res.setName(self.getName())
10783 res.setDescription(self.getDescription())
10784 res.setOwner(conf)
10785 res.notifyModification()
10786 res.setId(self.getId())
10788 if protection:
10789 res.setProtection(self.getAccessController()._getAccessProtection())
10790 #res.__ac = self.getAccessController()
10792 return res
10794 def notifyModification( self ):
10795 parent = self.getOwner()
10796 if parent:
10797 parent.setModificationDate()
10798 self._p_changed = 1
10800 def getLocator( self ):
10801 if self._owner == None:
10802 return Locator()
10803 lconf = self._owner.getLocator()
10804 lconf["resId"] = self.getId()
10805 return lconf
10807 def setId( self, newId ):
10808 self.id = newId.strip()
10810 def getId( self ):
10811 return self.id
10813 def getUniqueId( self ):
10814 """returns (string) the unique identifier of the item
10815 used mainly in the web session access key table
10816 for resources, it is the same as the father material since
10817 only the material can be protected with an access key"""
10818 return self.getOwner().getUniqueId()
10820 def setOwner(self, newOwner):
10821 self._owner = newOwner
10823 def getOwner( self ):
10824 return self._owner
10826 def getCategory( self ):
10827 #raise "%s:%s:%s"%(self.getOwner(), Material, isinstance(self.getOwner, Material))
10829 if isinstance(self.getOwner(), Category):
10830 return self.getOwner()
10831 if isinstance(self.getOwner(), Material):
10832 return self.getOwner().getCategory()
10833 return None
10835 def getConference( self ):
10836 # this check owes itself to the fact that some
10837 # protection checking functions call getConference()
10838 # directly on resources, without caring whether they
10839 # are owned by Conferences or Categories
10840 if self._owner is None or isinstance(self._owner, Category):
10841 return None
10842 else:
10843 return self._owner.getConference()
10845 def getSession( self ):
10846 return self._owner.getSession()
10848 def getContribution( self ):
10849 return self._owner.getContribution()
10851 def getSubContribution( self ):
10852 return self._owner.getSubContribution()
10854 @Updates (['MaKaC.conference.Link',
10855 'MaKaC.conference.LocalFile'], 'name')
10856 def setName( self, newName ):
10857 self.name = newName.strip()
10858 self.notifyModification()
10860 def getName( self ):
10861 return self.name
10863 @Updates (['MaKaC.conference.Link',
10864 'MaKaC.conference.LocalFile'], 'description')
10865 def setDescription( self, newDesc ):
10866 self.description = newDesc.strip()
10867 self.notifyModification()
10869 def getDescription( self ):
10870 return self.description
10872 def archive( self, repository = None, forcedFileId = None ):
10873 """performs necessary operations to ensure the archiving of the
10874 resource. By default is doing nothing as the persistence of the
10875 system already ensures the archiving of the basic resource data"""
10876 return
10878 def delete(self):
10879 if self._owner is not None:
10880 self.notify_protection_to_owner(delete=True)
10881 self._owner.removeResource(self)
10882 self.__ac.unlinkAvatars('access')
10883 self._owner = None
10884 TrashCanManager().add(self)
10886 def recover(self):
10887 TrashCanManager().remove(self)
10889 def isProtected(self):
10890 # tells if a resource is protected or not
10891 return (self.hasProtectedOwner() + self.getAccessProtectionLevel()) > 0
10893 def getAccessProtectionLevel( self ):
10894 return self.__ac.getAccessProtectionLevel()
10896 def isItselfProtected( self ):
10897 return self.__ac.isItselfProtected()
10899 def hasProtectedOwner( self ):
10900 if self.getOwner() != None:
10901 return self.getOwner().isProtected()
10902 return False
10904 def notify_protection_to_owner(self, delete=False):
10905 # Resources can be attached to other objects (e.g. Registrant),
10906 # but we wish to trigger the notification only when attached to materials (except paper reviewing)
10907 if isinstance(self.getOwner(), Material) and not isinstance(self.getOwner(), Reviewing):
10908 self.getOwner().updateNonInheritingChildren(self, delete)
10910 @Updates (['MaKaC.conference.Link',
10911 'MaKaC.conference.LocalFile'],'protection', lambda(x): int(x))
10913 def setProtection( self, private ):
10914 self.__ac.setProtection( private )
10915 self.notify_protection_to_owner()
10917 def grantAccess( self, prin ):
10918 self.__ac.grantAccess( prin )
10919 if isinstance(prin, AvatarUserWrapper):
10920 prin.linkTo(self, "access")
10922 def revokeAccess( self, prin ):
10923 self.__ac.revokeAccess( prin )
10924 if isinstance(prin, AvatarUserWrapper):
10925 prin.unlinkTo(self, "access")
10927 def canView( self, aw ):
10928 """tells whether the specified user has access to the current object
10929 or any of its sub-objects
10931 return self.canAccess( aw )
10933 def isAllowedToAccess( self, user ):
10934 return self.__ac.canUserAccess( user ) or self.canUserModify( user ) or (not self.isItselfProtected() and self.getOwner().isAllowedToAccess( user ))
10936 def canAccess( self, aw ):
10937 # Allow harvesters (Invenio, offline cache) to access
10938 # protected pages
10939 if has_request_context() and self.__ac.isHarvesterIP(request.remote_addr):
10940 return True
10941 #####################################################
10943 # Managers have always access
10944 if self.canModify(aw):
10945 return True
10947 if not self.canIPAccess(request.remote_addr) and not self.canUserModify(aw.getUser()) and \
10948 not self.isAllowedToAccess(aw.getUser()):
10949 return False
10950 if not self.isProtected():
10951 return True
10952 flag = self.isAllowedToAccess( aw.getUser() )
10953 return flag or self.canKeyAccess(aw) or self.getOwner().canKeyAccess(aw) or \
10954 (self.getConference() != None and self.getConference().canKeyAccess(aw) and self.getAccessKey() == "") or \
10955 (self.getConference() != None and self.getConference().canKeyAccess(aw) and self.getAccessKey() == self.getConference().getAccessKey())
10957 def grantModification( self, prin ):
10958 self.__ac.grantModification( prin )
10960 def revokeModification( self, prin ):
10961 self.__ac.revokeModification( prin )
10963 def canModify(self, aw_or_user):
10964 if hasattr(aw_or_user, 'getUser'):
10965 aw_or_user = aw_or_user.getUser()
10966 return self.canUserModify(aw_or_user) or (self.getConference() and self.getConference().canKeyModify())
10968 def canUserModify( self, user ):
10969 """Tells whether a user is allowed to modify the current contribution:
10970 only if the user is granted to modify the contribution or the user
10971 can modify any of its upper objects (i.e. conference or session).
10973 return self.getOwner().canUserModify( user )
10975 def getModifKey( self ):
10976 return self.getConference().getModifKey()
10978 def getManagerList( self ):
10979 return self.__ac.getModifierList()
10981 def getAllowedToAccessList( self ):
10982 return self.__ac.getAccessList()
10984 def getURL( self ):
10985 return ""
10987 def requireDomain( self, dom ):
10988 self.__ac.requireDomain( dom )
10990 def freeDomain( self, dom ):
10991 self.__ac.freeDomain( dom )
10993 def getDomainList( self ):
10994 return self.__ac.getRequiredDomainList()
10996 def getAccessController(self):
10997 return self.__ac
10999 def getAccessKey(self):
11000 if self.getOwner() is not None:
11001 return self.getOwner().getAccessKey()
11002 return ""
11004 def canKeyAccess(self, aw):
11005 accessKey = self.getAccessKey()
11006 key = session.get('accessKeys', {}).get(self.getUniqueId())
11007 if not key:
11008 return False
11009 elif accessKey and key == accessKey:
11010 return True
11011 elif not accessKey and key == self.getConference().getAccessKey():
11012 return True
11013 return False
11015 def getReviewingState(self):
11016 """ Returns the reviewing state of a resource, which is the reviewing state of the material to which it belongs.
11017 The state is represented by an integer:
11018 0 : there's no reviewing state because the resource doesn't belong to a material,
11019 the material does not belong to a contribution, or the conference does not have reviewing.
11020 1 : the material is not subject to reviewing, because this kind of material is not reviewable in the conference
11021 2 : the material is subject to reviewing, but has not been submitted yet by the author
11022 3 : the material is subject to reviewing, has been submitted by the author, but has not been judged yet
11023 4 : the material is subject to reviewing, has been submitted by the author, and has been judged as Accepted
11024 5 : the material is subject to reviewing, has been submitted by the author, and has been judged as Rejected
11026 if isinstance(self.getOwner(), Material):
11027 return self.getOwner().getReviewingState()
11028 else: #ressource does not belong to a material
11029 return 0
11031 def setPDFConversionRequestDate( self, newPdfConversionRequestDate ):
11032 self.pdfConversionRequestDate = newPdfConversionRequestDate
11034 def getPDFConversionStatus(self):
11036 if not hasattr(self, "pdfConversionRequestDate"):
11037 self.pdfConversionRequestDate = None
11039 if self.pdfConversionRequestDate is not None and self.pdfConversionRequestDate + timedelta(seconds=50) > nowutc() :
11040 return 'converting'
11041 return None
11043 class Link(Resource):
11044 """Specialises Resource class in order to represent web links. Objects of
11045 this class will contain necessary information to include in a conference
11046 object links to internet resources through a URL.
11047 Params:
11048 url -- (string) Contains the URL to the internet target resource.
11051 fossilizes(ILinkMinimalFossil, ILinkFossil)
11053 def __init__( self, resData = None ):
11054 Resource.__init__( self, resData )
11055 self.url = ""
11057 def clone( self, conf ):
11058 link = Resource.clone(self, conf)
11059 link.setURL(self.getURL())
11060 return link
11062 @Updates ('MaKaC.conference.Link', 'url')
11063 def setURL( self, newURL ):
11064 self.url = newURL.strip()
11065 self.notifyModification()
11067 def getURL( self ):
11068 return self.url
11070 def getLocator(self):
11071 locator = Resource.getLocator(self)
11072 locator['fileExt'] = 'link'
11073 return locator
11075 class LocalFile(Resource):
11076 """Specialises Resource class in order to represent files which can be
11077 stored in the system. The user can choose to use the system as an
11078 archive of electronic files so he may want to attach a file which is
11079 in his computer to a conference so it remains there and must be kept
11080 in the system. This object contains the file basic metadata and provides
11081 the necessary operations to ensure the corresponding file is archived
11082 (it uses one of the file repositories of the system to do so) and keeps
11083 the reference for being able to access it afterwards.
11084 Params:
11085 fileName -- (string) Name of the file. Normally the original name of
11086 the user submitted file is kept.
11087 filePath -- (string) If it is set, it contains a local path to the
11088 file submitted by the user and uploaded in the system. This
11089 attribute is only temporary used so it keeps a pointer to a
11090 temporary uploaded file.
11091 __repository -- (FileRep) Once a file is archived, it is kept in a
11092 FileRepository for long term. This attribute contains a pointer
11093 to the file repository where the file is kept.
11094 __archivedId -- (string) It contains a unique identifier for the file
11095 inside the repository where it is archived.
11098 fossilizes(ILocalFileMinimalFossil, ILocalFileFossil, ILocalFileExtendedFossil, ILocalFileAbstractMaterialFossil)
11100 def __init__( self, resData = None ):
11101 Resource.__init__( self, resData )
11102 self.fileName= ""
11103 self.fileType = ""
11104 self.filePath = ""
11105 self.__repository = None
11106 self.__archivedId = ""
11108 def clone( self, conf, protection=True ):
11109 localfile = Resource.clone(self, conf, protection)
11110 localfile.setFilePath(self.getFilePath())
11111 localfile.setFileName(self.getFileName())
11112 return localfile
11114 def getLocator(self):
11115 locator = Resource.getLocator(self)
11116 try:
11117 locator['fileExt'] = (self.fileType.lower() or
11118 os.path.splitext(self.fileName)[1].lower().lstrip('.') or None)
11119 except Exception:
11120 locator['fileExt'] = 'bin' # no extension => use a dummy
11121 return locator
11123 def setFileName( self, newFileName, checkArchive=True ):
11124 """While the file is not archived sets the file name of the current
11125 object to the one specified (if a full path is specified the
11126 base name is extracted) replacing on it blanks by underscores.
11128 if checkArchive and self.isArchived():
11129 raise MaKaCError( _("The file name of an archived file cannot be changed"), _("File Archiving"))
11130 #Using os.path.basename is not enough as it only extract filenames
11131 # correclty depending on the server platform. So we need to convert
11132 # to the server platform and apply the basename extraction. As I
11133 # couldn't find a python function for this this is done manually
11134 # although it can contain errors
11135 #On windows basename function seems to work properly with unix file
11136 # paths
11137 if newFileName.count("/"):
11138 #unix filepath
11139 newFileName = newFileName.split("/")[-1]
11140 else:
11141 #windows file path: there "/" is not allowed on windows paths
11142 newFileName = newFileName.split("\\")[-1]
11143 self.fileName = newFileName.strip().replace(" ", "_")
11145 def getFileName( self ):
11146 return self.fileName
11148 def getFileType( self ):
11149 fileExtension = os.path.splitext( self.getFileName() )[1]
11150 if fileExtension != "":
11151 fileExtension = fileExtension[1:]
11152 cfg = Config.getInstance()
11153 if cfg.getFileType( fileExtension ) != "":
11154 return cfg.getFileType( fileExtension )
11155 else:
11156 return fileExtension
11158 def setFilePath( self, filePath ):
11159 if self.isArchived():
11160 raise MaKaCError( _("The path of an archived file cannot be changed"), _("File Archiving"))
11161 if not os.access( filePath.strip(), os.F_OK ):
11162 raise Exception( _("File does not exist : %s")%filePath.strip())
11163 self.filePath = filePath.strip()
11165 def getCreationDate( self):
11166 return self.__repository.getCreationDate(self.__archivedId)
11168 def getFilePath( self ):
11169 if not self.isArchived():
11170 return self.filePath
11171 return self.__repository.getFilePath(self.__archivedId)
11173 def getSize( self ):
11174 if not self.isArchived():
11175 return int(os.stat(self.getFilePath())[stat.ST_SIZE])
11176 return self.__repository.getFileSize( self.__archivedId )
11178 def setArchivedId( self, rep, id ):
11179 self.__repository = rep
11180 self.__archivedId = id
11182 def getRepositoryId( self ):
11183 return self.__archivedId
11185 def setRepositoryId(self, id):
11186 self.__archivedId = id
11188 def isArchived( self ):
11189 return self.__repository != None and self.__archivedId != ""
11191 def readBin( self ):
11192 if not self.isArchived():
11193 raise MaKaCError( _("File not available until it has been archived") , _("File Archiving"))
11194 return self.__repository.readFile( self.__archivedId )
11196 def replaceContent( self, newContent ):
11197 if not self.isArchived():
11198 raise MaKaCError( _("File not available until it has been archived") , _("File Archiving"))
11199 self.__repository.replaceContent( self.__archivedId, newContent )
11201 def archive( self, repository=None, forcedFileId = None ):
11202 if self.isArchived():
11203 raise Exception( _("File is already archived"))
11204 if not repository:
11205 raise Exception( _("Destination repository not set"))
11206 if self.filePath == "":
11207 return _("Nothing to archive")
11208 repository.storeFile( self, forcedFileId = forcedFileId)
11209 self.filePath = ""
11210 self.notifyModification()
11212 def unArchive(self):
11213 # Not used.
11214 self.__repository = None
11215 self.__archivedId = ""
11217 def recover(self):
11218 if not self.isArchived():
11219 raise Exception( _("File is not archived, so it cannot be recovered."))
11220 if not self.__repository:
11221 raise Exception( _("Destination repository not set."))
11222 self.__repository.recoverFile(self)
11223 Resource.recover(self)
11224 self.notifyModification()
11226 def delete( self ):
11227 if not self.isArchived():
11228 os.remove( self.getFilePath() )
11229 try:
11230 self.__repository.retireFile( self )
11231 except AttributeError, e:
11232 pass
11233 Resource.delete( self )
11235 def getRepository(self):
11236 return self.__repository
11238 def __str__( self ):
11239 return self.getFileName()
11242 class TCIndex( Persistent ):
11243 """Index for conference track coordinators.
11245 This class allows to index conference track coordinators so the owner
11246 can answer optimally to the query if a user is coordinating
11247 any conference track.
11248 It is implemented by simply using a BTree where the Avatar id is used
11249 as key (because it is unique and non variable) and a list of
11250 coordinated tracks is kept as keys. It is the responsability of the
11251 index owner (conference) to keep it up-to-date i.e. notify track
11252 coordinator additions and removals.
11255 def __init__( self ):
11256 self._idx = OOBTree()
11259 def getTracks( self, av ):
11260 """Gives a list with the tracks a user is coordinating.
11262 if av == None:
11263 return []
11264 return self._idx.get( av.getId(), [] )
11266 def indexCoordinator( self, av, track ):
11267 """Registers in the index a coordinator of a track.
11269 if av == None or track == None:
11270 return
11271 if not self._idx.has_key( av.getId() ):
11272 l = []
11273 else:
11274 l = self._idx[av.getId()]
11275 if track not in l:
11276 l.append(track)
11277 # necessary, otherwise ZODB won't know it needs to update the BTree
11278 self._idx[av.getId()] = l
11279 self.notifyModification()
11281 def unindexCoordinator( self, av, track ):
11282 if av == None or track == None:
11283 return
11284 l = self._idx.get( av.getId(), [] )
11285 if track in l:
11286 l.remove( track )
11287 self._idx[av.getId()] = l
11288 self.notifyModification()
11290 def notifyModification(self):
11291 self._p_changed = 1
11294 class Track(CoreObject):
11296 def __init__( self ):
11297 self.conference = None
11298 self.id = "not assigned"
11299 self.title = ""
11300 self.description = ""
11301 self.subTracks = {}
11302 self.__SubTrackGenerator = Counter()
11303 self._abstracts = OOBTree()
11304 self._coordinators = []
11305 self._contributions = OOBTree()
11306 self._code=""
11308 def __cmp__(self, other):
11309 if type(self) is not type(other):
11310 # This is actually dangerous and the ZODB manual says not to do this
11311 # because it relies on memory order. However, this branch should never
11312 # be taken anyway since we do not store different types in the same set
11313 # or use them as keys.
11314 return cmp(hash(self), hash(other))
11315 if self.getConference() == other.getConference():
11316 return cmp(self.getId(), other.getId())
11317 return cmp(self.getConference(), other.getConference())
11319 def clone(self, conference):
11320 tr = Track()
11321 tr.setConference(conference)
11322 tr.setTitle(self.getTitle())
11323 tr.setCode(self.getCode())
11324 tr.setDescription(self.getDescription())
11326 for co in self.getCoordinatorList() :
11327 tr.addCoordinator(co)
11329 for subtr in self.getSubTrackList() :
11330 tr.addSubTrack(subtr.clone())
11332 return tr
11335 def delete( self ):
11336 """Deletes a track from the system. All the associated abstracts will
11337 also be notified so the track is no longer associated to them.
11339 #XXX: Should we allow to delete a track when there are some abstracts
11340 # or contributions submitted for it?!?!?!?!
11342 # we must notify each abstract in the track about the deletion of the
11343 # track
11344 while len(self._abstracts)>0:
11345 k = self._abstracts.keys()[0]
11346 abstract = self._abstracts[k]
11347 del self._abstracts[k]
11348 abstract.removeTrack( self )
11350 # we must notify each contribution in the track about the deletion of the
11351 # track
11352 while len(self._contributions)>0:
11353 k = self._contributions.keys()[0]
11354 contrib = self._contributions[k]
11355 del self._contributions[k]
11356 contrib.removeTrack( self )
11358 # we must delete and unindex all the possible track coordinators
11359 while len(self._coordinators)>0:
11360 self.removeCoordinator(self._coordinators[0])
11362 # we must notify the conference about the track deletion
11363 if self.conference:
11364 conf = self.conference
11365 self.conference = None
11366 conf.removeTrack( self )
11368 TrashCanManager().add(self)
11370 def recover(self):
11371 TrashCanManager().remove(self)
11373 def canModify(self, aw_or_user):
11374 return self.conference.canModify(aw_or_user)
11376 def canUserModify( self, av ):
11377 return self.conference.canUserModify( av )
11379 def canView( self, aw ):
11380 return self.conference.canView( aw )
11382 def notifyModification( self ):
11383 parent = self.getConference()
11384 if parent:
11385 parent.setModificationDate()
11386 self._p_changed = 1
11388 def getLocator( self ):
11389 """Gives back a globaly unique identification encapsulated in a Locator
11390 object for the track instance
11392 if self.conference == None:
11393 return Locator()
11394 lconf = self.conference.getLocator()
11395 lconf["trackId"] = self.getId()
11396 return lconf
11398 def setConference(self, conference):
11399 self.conference = conference
11401 def getConference( self ):
11402 return self.conference
11404 def getOwner( self ):
11405 return self.getConference()
11407 def setId( self, newId ):
11408 self.id = str(newId)
11410 def getId( self ):
11411 return self.id
11413 def setTitle( self, newTitle ):
11414 self.title = newTitle
11415 self.notifyModification()
11417 def getTitle( self ):
11418 return self.title
11420 def setDescription(self, newDescription ):
11421 self.description = newDescription
11422 self.notifyModification()
11424 def getDescription(self):
11425 return self.description
11427 def getCode(self):
11428 try:
11429 if self._code:
11430 pass
11431 except AttributeError:
11432 self._code=self.id
11433 return self._code
11435 def setCode(self,newCode):
11436 self._code=str(newCode).strip()
11438 def __generateNewSubTrackId( self ):
11439 return str(self.__SubTrackGenerator.newCount())
11441 def addSubTrack( self, newSubTrack ):
11442 """Registers the contribution passed as parameter within the session
11443 assigning it a unique id.
11445 if newSubTrack in self.subTracks.values():
11446 return
11447 subTrackId = newSubTrack.getId()
11448 if subTrackId == "not assigned":
11449 subTrackId = self.__generateNewSubTrackId()
11450 self.subTracks[subTrackId] = newSubTrack
11451 newSubTrack.setTrack( self )
11452 newSubTrack.setId( subTrackId )
11453 self.notifyModification()
11455 def removeSubTrack( self, subTrack ):
11456 """Removes the indicated contribution from the session
11458 if subTrack in self.subTracks.values():
11459 del self.subTracks[ subTrack.getId() ]
11460 subTrack.setTrack( None )
11461 subTrack.delete()
11462 self.notifyModification()
11464 def recoverSubTrack(self, subTrack):
11465 self.addSubTrack(subTrack)
11466 subTrack.recover()
11468 def newSubTrack( self ):
11469 st = SubTrack()
11470 self.addSubTrack( st )
11471 return st
11473 def getSubTrackById( self, id ):
11474 if self.subTracks.has_key( id ):
11475 return self.subTracks[ id ]
11476 return None
11478 def getSubTrackList( self ):
11479 return self.subTracks.values()
11481 def getAbstractList( self ):
11484 try:
11485 if self._abstracts:
11486 pass
11487 except AttributeError:
11488 self._abstracts = OOBTree()
11489 return self._abstracts.values()
11491 def getAbstractById( self, id ):
11492 try:
11493 if self._abstracts:
11494 pass
11495 except AttributeError:
11496 self._abstracts = OOBTree()
11497 return self._abstracts.get(str(id).strip())
11499 def hasAbstract( self, abstract ):
11502 try:
11503 if self._abstracts:
11504 pass
11505 except AttributeError:
11506 self._abstracts = OOBTree()
11507 return self._abstracts.has_key( abstract.getId() )
11509 def addAbstract( self, abstract ):
11510 """Adds an abstract to the track abstract list.
11512 Notice that this method doesn't notify the abstract about the track
11513 addition.
11515 if not self.hasAbstract( abstract ):
11516 self._abstracts[ abstract.getId() ] = abstract
11517 #abstract.addTrack( self )
11519 def removeAbstract( self, abstract ):
11520 """Removes an abstract from the track abstract list.
11522 Notice that this method doesn't notify the abstract about the track
11523 removal.
11525 if self.hasAbstract( abstract ):
11526 del self._abstracts[ abstract.getId() ]
11527 #abstract.removeTrack( self )
11529 def addCoordinator( self, av ):
11530 """Grants coordination privileges to user.
11532 Arguments:
11533 av -- (AvatarUserWrapper) the user to which
11534 coordination privileges must be granted.
11537 try:
11538 if self._coordinators:
11539 pass
11540 except AttributeError, e:
11541 self._coordinators = []
11542 self.notifyModification()
11544 if not (av in self._coordinators):
11545 self._coordinators.append( av )
11546 self.getConference().addTrackCoordinator( self, av )
11547 av.linkTo(self, "coordinator")
11548 self.notifyModification()
11550 def removeCoordinator( self, av ):
11551 """Revokes coordination privileges to user.
11553 Arguments:
11554 av -- (AvatarUserWrapper) user for which coordination privileges
11555 must be revoked
11557 try:
11558 if self._coordinators:
11559 pass
11560 except AttributeError, e:
11561 self._coordinators = []
11562 self.notifyModification()
11564 if av in self._coordinators:
11565 self._coordinators.remove( av )
11566 self.getConference().removeTrackCoordinator( self, av )
11567 av.unlinkTo(self, "coordinator")
11568 self.notifyModification()
11570 def isCoordinator( self, av ):
11571 """Tells whether the specified user is a coordinator of the track.
11573 Arguments:
11574 av -- (AvatarUserWrapper) user to be checke
11576 Return value: (boolean)
11578 try:
11579 if self._coordinators:
11580 pass
11581 except AttributeError, e:
11582 self._coordinators = []
11584 return av in self._coordinators
11586 def getCoordinatorList( self ):
11587 """Return all users which have privileges to coordinate the track.
11589 Return value: (list)
11591 try:
11592 if self._coordinators:
11593 pass
11594 except AttributeError, e:
11595 self._coordinators = []
11597 return self._coordinators
11599 def canCoordinate( self, aw ):
11600 """Tells if a user has coordination privileges.
11602 Only track coordinators have coordination privileges over a track.
11604 Params:
11605 aw -- (MaKaC.accessControl.AccessWrapper) User access
11606 information for which the coordination privileges must be
11607 checked.
11609 Return value: (boolean)
11611 return self.isCoordinator( aw.getUser() ) or self.canModify( aw )
11613 def addContribution( self, newContrib ):
11616 try:
11617 if self._contributions:
11618 pass
11619 except AttributeError:
11620 self._contributions = OOBTree()
11621 if self._contributions.has_key( newContrib.getId() ):
11622 return
11623 self._contributions[ newContrib.getId() ] = newContrib
11624 newContrib.setTrack( self )
11626 def getModifKey( self ):
11627 return self.getConference().getModifKey()
11629 def removeContribution( self, contrib ):
11632 try:
11633 if self._contributions:
11634 pass
11635 except AttributeError:
11636 self._contributions = OOBTree()
11637 if not self._contributions.has_key( contrib.getId() ):
11638 return
11639 del self._contributions[ contrib.getId() ]
11640 contrib.setTrack( None )
11642 def hasContribution( self, contrib ):
11643 try:
11644 if self._contributions:
11645 pass
11646 except AttributeError:
11647 self._contributions = OOBTree()
11648 return self._contributions.has_key( contrib.getId() )
11650 def getContributionList(self):
11651 try:
11652 if self._contributions:
11653 pass
11654 except AttributeError:
11655 self._contributions = OOBTree()
11656 return self._contributions.values()
11658 def canUserCoordinate( self, av ):
11659 return self.isCoordinator( av ) or self.canUserModify( av )
11662 class SubTrack(CoreObject):
11664 def __init__( self ):
11665 self.track = None
11666 self.id = "not assigned"
11667 self.title = ""
11668 self.description = ""
11670 def clone(self):
11671 sub = SubTrack()
11672 sub.setDescription(self.getDescription())
11673 sub.setTitle(self.getTitle())
11675 return sub
11678 def delete(self):
11679 TrashCanManager().add(self)
11681 def recover(self):
11682 TrashCanManager().remove(self)
11684 def canModify(self, aw_or_user):
11685 return self.track.canModify(aw_or_user)
11687 def canView( self, aw ):
11688 return self.track.canView( aw )
11690 def notifyModification( self ):
11691 parent = self.getTrack()
11692 if parent:
11693 parent.setModificationDate()
11694 self._p_changed = 1
11696 def getLocator( self ):
11697 """Gives back a globaly unique identification encapsulated in a Locator
11698 object for the session instance
11700 if self.track == None:
11701 return Locator()
11702 lconf = self.track.getLocator()
11703 lconf["subTrackId"] = self.getId()
11704 return lconf
11706 def setTrack(self, track):
11707 self.track = track
11708 if track == None:
11709 return
11711 def getTrack( self ):
11712 return self.track
11714 def getOwner( self ):
11715 return self.getTrack()
11717 def setId( self, newId ):
11718 self.id = str(newId)
11720 def getId( self ):
11721 return self.id
11723 def setTitle( self, newTitle ):
11724 self.title = newTitle
11725 self.notifyModification()
11727 def getTitle( self ):
11728 return self.title
11730 def setDescription(self, newDescription ):
11731 self.description = newDescription
11732 self.notifyModification()
11734 def getDescription(self):
11735 return self.description
11738 class ContributionType(Persistent):
11740 def __init__(self, name, description, conference):
11741 self._id = ""
11742 self._name = name
11743 self._description = description
11744 self._conference = conference
11746 def getId(self):
11747 return self._id
11749 def setId(self, id):
11750 self._id = id
11752 def getName(self):
11753 return self._name
11755 def setName(self, name):
11756 self._name = name
11758 def getDescription(self):
11759 return self._description
11761 def setDescription(self, desc):
11762 self._description = desc
11764 def getConference(self):
11765 return self._conference
11767 def setConference(self, conf):
11768 self._conference = conf
11770 def getLocator( self ):
11771 if self._conference == None:
11772 return Locator()
11773 lconf = self._conference.getLocator()
11774 lconf["contribTypeId"] = self.getId()
11775 return lconf
11777 def canModify(self, aw_or_user):
11778 return self._conference.canModify(aw_or_user)
11780 def delete(self):
11781 self.setConference(None)
11782 TrashCanManager().add(self)
11784 def recover(self):
11785 TrashCanManager().remove(self)
11787 def clone(self, conference ):
11788 type = ContributionType(self.getName(), self.getDescription(),conference)
11789 return type
11792 class BOAConfig(Persistent):
11793 """Contains the configuration of the Book of Abstracts of a conference
11795 sortByTypes = {"number": L_("ID"),
11796 "name": L_("Title"),
11797 "sessionTitle": L_("Session title"),
11798 "speaker": L_("Presenter"),
11799 "schedule": L_("Schedule")}
11801 correspondingAuthorTypes = {"none": L_("Nobody"),
11802 "submitter": L_("Submitter"),
11803 "speakers": L_("Speakers")}
11805 def __init__(self,conf):
11806 self._conf=conf
11807 self._text=""
11808 self._showIds= False
11809 self._sortBy = "number"
11810 self._correspondingAuthor = "submitter"
11811 self._modificationDS = nowutc()
11812 self._cache = False
11814 def getText(self):
11815 return self._text
11817 def setText(self,newText):
11818 self._text=newText.strip()
11819 self._notifyModification()
11821 def getShowIds(self):
11822 if not hasattr(self, "_showIds"):
11823 self._showIds=False
11824 return self._showIds
11826 def setShowIds(self,showIds):
11827 self._showIds=showIds
11828 self._notifyModification()
11830 def getSortBy(self):
11831 if not hasattr(self, "_sortBy"):
11832 self._sortBy="number"
11833 return self._sortBy
11835 def setSortBy(self,sortBy):
11836 self._sortBy=sortBy
11837 self._notifyModification()
11839 @staticmethod
11840 def getSortByTypes():
11841 return BOAConfig.sortByTypes
11843 def getCorrespondingAuthor(self):
11844 if not hasattr(self, "_correspondingAuthor"):
11845 self._correspondingAuthor = "submitter"
11846 return self._correspondingAuthor
11848 def setCorrespondingAuthor(self, correspondingAuthor):
11849 self._correspondingAuthor = correspondingAuthor
11850 self._notifyModification()
11852 @staticmethod
11853 def getCorrespondingAuthorTypes():
11854 return BOAConfig.correspondingAuthorTypes
11856 def isCacheEnabled(self):
11857 if not hasattr(self, '_cache'):
11858 self._cache = False
11859 return self._cache
11861 def setCache(self, value):
11862 self._cache = value;
11864 def _notifyModification(self):
11865 self._modificationDS = nowutc()
11867 @property
11868 def lastChanged(self):
11869 if not hasattr(self, '_modificationDS'):
11870 self._modificationDS = nowutc()
11871 return self._modificationDS
11874 class EventCloner(object):
11875 """Base class to let plugins/modules plug into the event cloning mechanism"""
11877 @staticmethod
11878 def get_plugin_items(event):
11879 """Returns the items/checkboxes for the clone options provided by EventCloner"""
11880 plugin_options = []
11881 for plugin_cloner in values_from_signal(signals.event_management.clone.send(event), single_value=True):
11882 with plugin_context(plugin_cloner.plugin):
11883 for name, (title, enabled, checked) in plugin_cloner.get_options().iteritems():
11884 full_name = plugin_cloner.full_option_name(name)
11885 plugin_options.append((
11886 title,
11887 """<li><input type="checkbox" name="cloners" id="cloner-{0}" value="{0}" {2} {3}>{1}</li>"""
11888 .format(full_name, title,
11889 'disabled' if not enabled else '',
11890 'checked' if checked and enabled else '')
11892 return '\n'.join(x[1] for x in sorted(plugin_options))
11894 @staticmethod
11895 def clone_event(old_event, new_event):
11896 """Calls the various cloning methods"""
11897 selected = set(request.values.getlist('cloners'))
11898 for plugin_cloner in values_from_signal(signals.event_management.clone.send(old_event), single_value=True):
11899 with plugin_context(plugin_cloner.plugin):
11900 selected_options = {name for name, (_, enabled, _) in plugin_cloner.get_options().iteritems()
11901 if enabled and plugin_cloner.full_option_name(name) in selected}
11902 plugin_cloner.clone(new_event, selected_options)
11904 def __init__(self, event, plugin=None):
11905 self.event = event
11906 self.plugin = plugin
11908 def full_option_name(self, option):
11909 return '{}-{}'.format(self.__module__, option)
11911 def get_options(self):
11912 """Returns a dict containing the clone options.
11914 :return: dict mapping option names to ``title, enabled, checked`` tuples
11916 raise NotImplementedError
11918 def clone(self, new_event, options):
11919 """Performs the actual cloning.
11921 This method is always called, even if no options are selected!
11923 :param new_event: The new event created during the clone
11924 :param options: A set containing the options provided by
11925 this class which the user has selected
11927 raise NotImplementedError