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
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
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.
133 self
._modificationDS
= date
135 def __conform__(self
, proto
):
137 if proto
== IIndexableByStartDateTime
:
138 return utc_timestamp(self
.getStartDate())
145 Inherited by objects that imply a physical location:
155 if not self
.getLocation() or not self
.getRoom():
158 location
= self
.getLocation().getName()
159 room
= self
.getRoom().getName()
161 if not location
or not room
:
166 .options(lazyload(Room
.owner
))
167 .filter(Room
.name
== fix_broken_string(room
, True),
168 Location
.name
== fix_broken_string(location
, True))
171 def getLocationParent(self
):
173 Returns the object the location info should be inherited from
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]
188 def getInheritedLocation(self
):
189 return self
.getLocationParent().getLocation()
191 def getOwnRoom(self
):
192 if len(self
.rooms
) > 0:
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:
209 elif len(self
.places
) > 0:
210 self
.places
[0] = newLocation
212 self
.places
.append(newLocation
)
213 self
.notifyModification()
215 def setRoom(self
, newRoom
):
216 oldRoom
= self
.getOwnRoom()
218 if len(self
.rooms
) > 0:
220 elif len(self
.rooms
) > 0:
221 self
.rooms
[0] = newRoom
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:
240 def getRecursiveManagerList(self
):
243 # Get the AccessProtectionLevel for this
244 apl
= self
.getAccessProtectionLevel()
249 for av
in self
.getManagerList():
251 for av
in self
.getOwner().getRecursiveManagerList():
254 for av
in self
.getManagerList():
258 for av
in self
.getOwner().getRecursiveManagerList():
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
273 # Get the AccessProtectionLevel for this
274 apl
= self
.getAccessProtectionLevel()
276 # If this object is "absolutely public", then return an empty set
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)
283 al
= self
.getAllowedToAccessList() + self
.getManagerList() + \
284 self
.getOwner().getRecursiveManagerList()
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()
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:
306 # return set containing whatever avatars/groups we may have collected
309 def canIPAccess(self
, ip
):
310 domains
= self
.getAccessController().getAnyDomainProtection()
312 return any(domain
.belongsTo(ip
) for domain
in domains
)
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)
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
)
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())
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
)
367 root
= DBMgr
.getInstance().getDBConnection().root()
368 if not root
.has_key("rootCategory"):
372 root
["rootCategory"] = r
373 return root
["rootCategory"]
375 def getDefaultConference(self
):
376 dconf
= HelperMaKaCInfo
.getMaKaCInfoInstance().getDefaultConference()
378 return HelperMaKaCInfo
.getMaKaCInfoInstance().setDefaultConference(DefaultConference())
383 class Category(CommonObjectBase
):
384 fossilizes(ICategoryFossil
)
390 self
.description
= ""
391 self
.subcategories
= {}
392 self
.conferences
= OOTreeSet()
393 self
._numConferences
= 0
395 self
._defaultStyle
= {"simple_event": "", "meeting": ""}
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,
404 self
._tasksAllowed
= False
406 self
._taskIdGenerator
= 0
407 self
._tasksPublic
= True
408 self
._tasksCommentPublic
= True
409 self
._tasksManagers
= []
410 self
._tasksCommentators
= []
411 self
._taskAccessList
= []
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())
426 path
= '/'.join(self
.getCategoryPathTitles()[:-1])
427 return '<Category({0}, {1}, {2})>'.format(self
.getId(), self
.getName(), path
)
432 return url_for('misc.index')
434 return url_for('category.categoryDisplay', self
)
436 def getAccessController(self
):
439 def updateNonInheritingChildren(self
, elem
, delete
=False):
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"""
446 return self
._notifyCreationList
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:
468 self
.paper
.setOwner(None)
470 self
.notifyModification()
472 def recoverPaper(self
, p
):
480 except AttributeError:
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:
495 self
.slides
.setOwner(None)
497 self
.notifyModification()
499 def recoverSlides(self
, s
):
507 except AttributeError:
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:
522 self
.video
.setOwner(None)
524 self
.notifyModification()
526 def recoverVideo(self
, v
):
534 except AttributeError:
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:
549 self
.poster
.setOwner(None)
551 self
.notifyModification()
553 def recoverPoster(self
, p
):
561 except AttributeError:
565 def getTaskList(self
):
567 return self
._tasks
.values()
570 return self
._tasks
.values()
582 def getTask(self
, taskId
):
583 return self
.getTasks().get(taskId
, None)
585 def _getTasksAllowed(self
):
587 return self
._tasksAllowed
589 self
._tasksAllowed
= False
590 return self
._tasksAllowed
592 def tasksAllowed(self
):
593 if self
.hasSubcategories():
595 return self
._getTasksAllowed
()
597 def setTasksAllowed(self
):
598 if self
.hasSubcategories():
600 self
._getTasksAllowed
()
601 self
._tasksAllowed
= True
602 self
.notifyModification()
605 def setTasksForbidden(self
):
606 if len(self
.getTaskList()) > 0:
608 self
._getTasksAllowed
()
609 self
._tasksAllowed
= False
610 self
.notifyModification()
613 def _getNewTaskId(self
):
615 if self
._taskIdGenerator
:
618 self
._taskIdGenerator
= 0
619 self
._taskIdGenerator
= self
._taskIdGenerator
+ 1
620 return self
._taskIdGenerator
622 def newTask(self
, user
):
625 newTask
= task
.Task(self
, self
._getNewTaskId
(), user
)
626 self
.getTasks()["%s" % newTask
.getId()] = newTask
627 self
.notifyModification()
630 def tasksPublic(self
):
632 return self
._tasksPublic
634 self
._tasksPublic
= True
635 return self
._tasksPublic
637 def setTasksPublic(self
):
639 self
._tasksPublic
= True
641 def setTasksPrivate(self
):
643 self
._tasksPublic
= False
645 def tasksCommentPublic(self
):
647 return self
._tasksCommentPublic
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
):
662 return self
._tasksManagers
664 self
._tasksManagers
= []
666 return self
._tasksManagers
668 def getTasksManager(self
, index
):
669 length
= len(self
.getTasksManagerList())
670 if index
< 0 or index
>= length
:
672 return self
._tasksManagers
[index
]
674 def addTasksManager(self
, user
):
677 self
.getTasksManagerList().append(user
)
681 def removeTasksManager(self
, index
):
682 length
= len(self
.getTasksManagerList())
683 if index
< 0 or index
>= length
:
685 del self
.getTasksManagerList()[index
]
689 def getTasksCommentatorList(self
):
691 return self
._tasksCommentators
693 self
._tasksCommentators
= []
695 return self
._tasksCommentators
697 def getTasksCommentator(self
, index
):
698 length
= len(self
.getTasksCommentatorList())
699 if index
< 0 or index
>= length
:
701 return self
._tasksCommentators
[index
]
703 def addTasksCommentator(self
, user
):
706 self
.getTasksCommentatorList().append(user
)
710 def removeTasksCommentator(self
, index
):
711 length
= len(self
.getTasksCommentatorList())
712 if index
< 0 or index
>= length
:
714 del self
._tasksCommentators
[index
]
718 def getTasksAccessList(self
):
720 return self
._tasksAccessList
722 self
._tasksAccessList
= []
724 return self
._tasksAccessList
726 def getTasksAccessPerson(self
, index
):
727 length
= len(self
.getTasksAccessList())
728 if index
< 0 or index
>= length
:
730 return self
._tasksAccessList
[index
]
732 def addTasksAccessPerson(self
, user
):
735 self
.getTasksAccessList().append(user
)
739 def removeTasksAccessPerson(self
, index
):
740 length
= len(self
.getTasksAccessList())
741 if index
< 0 or index
>= length
:
743 del self
.getTasksAccessList()[index
]
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
760 return max(0, min(visibility
, owner
.getVisibility() + 1))
764 def setVisibility(self
, visibility
=999):
765 self
._visibility
= int(visibility
)
768 def isSuggestionsDisabled(self
):
770 return self
._suggestions
_disabled
771 except AttributeError:
772 self
._suggestions
_disabled
= False
775 def setSuggestionsDisabled(self
, value
):
776 self
._suggestions
_disabled
= value
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
)
788 return self
.owner
is None
790 def getDefaultStyle(self
, type):
792 return self
._defaultStyle
[type]
796 def setDefaultStyle(self
, type, style
, subcatsStyle
=False):
798 self
._defaultStyle
[type] = style
800 self
._defaultStyle
= {"simple_event": "", "meeting": ""}
801 self
._defaultStyle
[type] = style
802 self
.notifyModification()
803 #raise str(subcatsStyle)
806 categ
= self
.getSubCategoryList()
809 cat
.setDefaultStyle(type, style
, subcatsStyle
)
811 ##################################
812 # Fermi timezone awareness #
813 ##################################
814 def getTimezone(self
):
816 if self
._timezone
not in all_timezones
:
817 self
.setTimezone('UTC')
818 return self
._timezone
820 self
.setTimezone('UTC')
823 def setTimezone(self
, tz
):
826 def changeConfTimezones(self
, tz
):
827 for conference
in self
.getConferenceList():
828 conference
.moveToTimezone(tz
)
830 ##################################
831 # Fermi timezone awareness(end) #
832 ##################################
841 def setOrder(self
, order
):
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 """
854 d
["categId"] = self
.getId()
857 def getCategory(self
):
863 def setOwner(self
, newOwner
):
864 if self
.getOwner() is not None and newOwner
is not None and self
.getOwner() != newOwner
:
867 self
.owner
= newOwner
869 def getCategoryPath(self
):
871 return [self
.getId()]
873 l
= self
.getOwner().getCategoryPath()
874 l
.append(self
.getId())
877 def iterParents(self
):
879 while not categ
.isRoot():
880 categ
= categ
.getOwner()
883 def getCategoryPathTitles(self
):
888 breadcrumbs
.insert(0, cat
.getTitle())
892 def delete(self
, deleteConferences
=0):
893 """removes completely a category (and all its sub-items) from the
896 oldOwner
= self
.getOwner()
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
)
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
)
932 catDateIdx
.indexCateg(self
)
933 catDateAllIdx
.indexCateg(self
)
935 signals
.category
.moved
.send(self
, old_parent
=oldOwner
, new_parent
=newOwner
)
940 def setName(self
, newName
):
942 self
.name
= newName
.strip()
944 # Reindex when name changes
945 nameIdx
= indexes
.IndexesHolder().getIndex('categoryName')
946 nameIdx
.unindex(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"))
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
988 if sc
in self
.getSubCategoryList():
989 self
._decNumConfs
(sc
.getNumConferences())
990 del self
.subcategories
[sc
.getId()]
993 def newSubCategory(self
, protection
):
994 cm
= CategoryManager()
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)
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
):
1038 def getModifKey(self
):
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
)
1048 def unindexConf(self
, conf
):
1049 catIdx
= indexes
.IndexesHolder().getIndex('category')
1050 catIdx
.unindexConf(conf
)
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
)
1059 signals
.event
.created
.send(conf
, parent
=self
)
1063 def removeConference(self
, conf
, notify
=True, delete
=False):
1064 if not (conf
in self
.conferences
):
1067 self
.unindexConf(conf
)
1069 self
.conferences
.remove(conf
)
1072 conf
.removeOwner(self
, notify
)
1073 self
._decNumConfs
(1)
1075 def getSubCategoryList(self
):
1076 subcategs
= self
.subcategories
.values()
1078 for categ
in subcategs
:
1079 cl
.append("%04s%s-%s" % (categ
.getOrder(), categ
.getName().replace("-", ""), categ
.getId()))
1083 id = c
.split("-")[1]
1084 res
.append(self
.subcategories
[id])
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
)
1106 res
.sort(Conference
._cmpTitle
)
1108 res
.sort(Conference
._cmpTitle
)
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
:
1123 for subcateg
in self
.subcategories
.itervalues():
1124 for conf
in subcateg
.iterAllConferences():
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()
1133 for subcateg
in subcategs
:
1134 res
.extend(subcateg
.getAllConferenceList())
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()
1151 nextEvt
= next(categIter
, None)
1157 raise AttributeError("'conf' parameter missing")
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
))
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.
1179 if self
._numConferences
:
1181 except AttributeError:
1182 self
._setNumConferences
()
1183 return self
._numConferences
1185 def _getRepository(self
):
1186 dbRoot
= DBMgr
.getInstance().getDBConnection().root()
1188 fr
= dbRoot
["local_repositories"]["main"]
1190 fr
= fileRepository
.MaterialLocalRepository()
1191 dbRoot
["local_repositories"] = OOBTree()
1192 dbRoot
["local_repositories"]["main"] = fr
1195 def removeResource(self
, res
):
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:
1205 self
._icon
= iconFile
1206 self
.notifyModification()
1212 except AttributeError, e
:
1216 def getIconURL(self
):
1217 if self
.getIcon() is None:
1219 return self
._icon
.getURL()
1221 def removeIcon(self
):
1222 if self
.getIcon() is None:
1226 self
.notifyModification()
1228 def recoverIcon(self
, icon
):
1230 if self
.getIcon() is not None:
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
):
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
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:
1279 if self
.getAccessProtectionLevel() == -1: # PUBLIC
1281 if self
.getOwner() is not None:
1282 return self
.getOwner().hasAnyProtection()
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
):
1305 if not self
.isItselfProtected() and self
.getOwner():
1306 return self
.getOwner().isAllowedToAccess(av
)
1308 def canView(self
, aw
):
1309 if self
.canAccess(aw
):
1311 for conf
in self
.getConferenceList():
1312 if conf
.canView(aw
):
1314 for subcateg
in self
.getSubCategoryList():
1315 if subcateg
.canView(aw
):
1319 def canAccess(self
, aw
):
1320 if not self
.hasAnyProtection():
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")
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")
1363 def getConferenceCreatorList(self
):
1364 return self
.__confCreators
1366 def isAllowedToCreateConference(self
, av
):
1368 if self
.canUserModify(av
):
1371 # Avatar is directly in the list
1372 if av
in self
.__confCreators
:
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
):
1382 def canCreateConference(self
, av
):
1383 if not self
.isConferenceCreationRestricted():
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
):
1401 if self
._statistics
:
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.
1411 signals
.category
.data_changed
.send(self
)
1415 class CustomLocation(Persistent
):
1417 def __init__(self
, **locationData
):
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
):
1429 d
["name"] = self
.getName()
1430 d
["address"] = self
.getAddress()
1431 d
["room"] = self
.getRoom()
1435 newCL
= CustomLocation()
1436 newCL
.setValues(self
.getValues())
1439 def setName(self
, newName
):
1445 def setAddress(self
, newAddress
):
1446 self
.address
= newAddress
1448 def getAddress(self
):
1451 def setRoom(self
, newRoom
):
1458 class CustomRoom(Persistent
):
1463 def setValues(self
, data
):
1464 self
.setName(data
.get("name", ""))
1465 self
.setFullName(data
.get("fullName"))
1467 def getValues(self
):
1469 d
["name"] = self
.getName()
1470 d
["fullName"] = self
.getFullName()
1477 newCR
= CustomRoom()
1478 newCR
.setValues(self
.getValues())
1481 def setName(self
, newName
):
1482 self
.name
= newName
.strip()
1487 def retrieveFullName(self
, location
):
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
)
1512 self
._affiliation
=""
1518 def _notifyModification( self
):
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
):
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()
1544 def setId(self
, newId
):
1550 def setDataFromAvatar(self
,av
):
1551 # av is an Avatar object.
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.
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
()
1579 TrashCanManager().add(self
)
1582 TrashCanManager().remove(self
)
1584 @Updates (['MaKaC.conference.ConferenceParticipation',
1585 'MaKaC.conference.SessionChair',
1586 'MaKaC.conference.SlotChair'], 'firstName')
1587 def setFirstName(self
,newName
):
1589 if tmp
==self
._firstName
:
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
):
1602 if tmp
==self
._surName
:
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
):
1615 if tmp
==self
._email
:
1617 self
._email
=newMail
.strip()
1618 self
._notifyModification
()
1620 def getEmail( self
):
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
()
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
()
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
()
1673 def getFullName( self
):
1674 res
= self
.getFamilyName()
1675 if self
.getFirstName() != "":
1676 if res
.strip() != "":
1677 res
= "%s, %s"%( res
, self
.getFirstName() )
1679 res
= self
.getFirstName()
1680 if self
.getTitle() != "":
1681 res
= "%s %s"%( self
.getTitle(), res
)
1684 def getFullNameNoTitle( self
):
1685 res
= self
.getFamilyName()
1686 if self
.getFirstName() != "":
1687 if res
.strip() != "":
1688 res
= "%s, %s"%( res
, self
.getFirstName() )
1690 res
= self
.getFirstName()
1693 def getDirectFullName( self
):
1694 res
= "%s %s"%( self
.getFirstName(), self
.getFamilyName() )
1696 if self
.getTitle() != "":
1697 res
= "%s %s"%( self
.getTitle(), res
)
1700 def getAbrName(self
):
1701 res
= self
.getFamilyName()
1702 if self
.getFirstName():
1705 res
= "%s%s." % (res
, safe_upper(safe_slice(self
.getFirstName(), 0, 1)))
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
)
1724 ConferenceParticipation
.__init
__(self
)
1726 def _notifyModification( self
):
1727 if self
._conf
!= None:
1728 self
._conf
.notifyModification()
1731 newCC
=ConferenceChair()
1732 newCC
.setValues(self
.getValues())
1735 def getConference(self
):
1741 def includeInConference(self
,conf
,id):
1742 if self
.getConference()==conf
and self
.getId()==id.strip():
1749 ConferenceParticipation
.delete(self
)
1751 def getLocator(self
):
1752 if self
.getConference() is None:
1754 loc
=self
.getConference().getLocator()
1755 loc
["chairId"]=self
.getId()
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
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
1773 def __init__( self
):
1774 self
._idx
= OOBTree()
1775 self
._idxEmail
= OOBTree()
1777 def _getIdxEmail(self
):
1779 return self
._idxEmail
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
1790 ret
= self
._idx
.get(av
.getId(),[])
1792 self
._moveEmailtoId
(av
)
1793 ret
= self
._idx
.get(av
.getId(),[])
1796 def index(self
,av
,contrib
):
1797 """Registers in the index a submitter of a contribution.
1799 if av
==None or contrib
==None:
1801 if not self
._idx
.has_key(av
.getId()):
1803 self
._idx
[av
.getId()]=l
1805 l
=self
._idx
[av
.getId()]
1806 if contrib
not in l
:
1808 self
._idx
[av
.getId()]=l
1810 def indexEmail(self
, email
, contrib
):
1811 if not email
or not contrib
:
1813 if not self
._getIdxEmail
().has_key(email
):
1815 self
._getIdxEmail
()[email
] = l
1817 l
= self
._getIdxEmail
()[email
]
1818 if not contrib
in l
:
1820 self
._getIdxEmail
()[email
] = l
1823 def unindex(self
,av
,contrib
):
1824 if av
==None or contrib
==None:
1826 l
=self
._idx
.get(av
.getId(),[])
1829 self
._idx
[av
.getId()]=l
1831 def unindexEmail(self
, email
, contrib
):
1832 if not email
or not contrib
:
1834 if self
._getIdxEmail
().has_key(email
):
1835 l
= self
._getIdxEmail
()[email
]
1839 del self
._getIdxEmail
()[email
]
1841 self
._getIdxEmail
()[email
] = l
1843 def _moveEmailtoId(self
, av
):
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
):
1861 def addReportNumber(self
, system
, number
):
1862 if system
in self
.getReportNumberKeys() or system
in Config
.getInstance().getReportNumberSystems().keys():
1864 if not number
in self
._reports
[system
]:
1865 self
._reports
[system
].append(number
)
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):
1878 rn
= self
.listReportNumbers()[int(id)]
1879 self
.removeReportNumber(rn
[0], rn
[1])
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
]
1891 def getReportNumberKeys(self
):
1892 return self
._reports
.keys()
1894 def listReportNumbersOnKey(self
, key
):
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
])
1904 def hasReportNumberOnSystem(self
, system
, number
):
1905 if self
.hasReportNumbersBySystem(system
):
1906 if number
in self
._reports
[system
]:
1910 def listReportNumbers(self
):
1912 keys
= self
._reports
.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
])
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
)
1929 def notifyModification(self
):
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
1948 confData -- (Dict) Contains the data the conference object has to
1951 #IndexedObject.__init__(self)
1953 raise MaKaCError( _("A creator must be specified when creating a new Event"), _("Event"))
1954 self
.__creator
= creator
1957 self
.description
= ""
1960 ###################################
1961 # Fermi timezone awareness #
1962 ###################################
1963 self
.startDate
= nowutc()
1964 self
.endDate
= nowutc()
1966 ###################################
1967 # Fermi timezone awareness(end) #
1968 ###################################
1969 self
._screenStartDate
= None
1970 self
._screenEndDate
= None
1971 self
.contactInfo
=""
1972 self
.chairmanText
= ""
1974 self
._chairGen
=Counter()
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
= ""
1984 self
.__programGenerator
= Counter() # Provides track unique
1985 # identifiers for this conference
1986 self
.__ac
= AccessController(self
)
1988 self
.__materialGenerator
= Counter() # Provides material unique
1989 # identifiers for this conference
1994 self
.__schedule
=None
1997 self
._creationDS
= creationDate
1999 self
._creationDS
= nowutc() #creation timestamp
2000 if modificationDate
:
2001 self
._modificationDS
= modificationDate
2003 self
._modificationDS
= nowutc() #modification timestamp
2005 self
.abstractMgr
= review
.AbstractMgr(self
)
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
2023 self
._registrantGenerator
= Counter()
2026 self
._closed
= False
2027 self
._visibility
= 999
2028 self
._pendingQueuesMgr
=pendingQueues
.ConfPendingQueuesMgr(self
)
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
)
2038 self
._confPaperReview
= ConferencePaperReview(self
)
2039 self
._confAbstractReview
= ConferenceAbstractReview(self
)
2042 self
._sortUrlTag
= ""
2044 self
._observers
= []
2048 return '<Conference({0}, {1}, {2})>'.format(self
.getId(), self
.getTitle(), self
.getStartDate())
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)
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
}
2069 from indico
.modules
.events
.notes
.models
.notes
import EventNote
2070 return EventNote
.get_for_linked_object(self
)
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 {}))
2098 def _cmpByDate(self
, toCmp
):
2099 res
= cmp(self
.getStartDate(), toCmp
.getStartDate())
2103 return cmp(self
, toCmp
)
2105 def __cmp__(self
, toCmp
):
2106 if isinstance(toCmp
, Conference
):
2107 return cmp(self
.getId(), toCmp
.getId())
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
):
2122 return self
._sortUrlTag
2124 self
._sortUrlTag
= ""
2125 return self
._sortUrlTag
2127 def setComments(self
,comm
=""):
2128 self
._comments
= comm
.strip()
2130 def getComments(self
):
2134 except AttributeError,e
:
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
):
2150 return self
._orgText
2155 def setOrgText( self
, 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
):
2168 return self
._keywords
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():
2183 return Reservation
.find_all(event_id
=int(self
.getId()))
2185 # ========================================================================
2187 def getParticipation(self
):
2189 if self
._participation
:
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
)
2205 def getVerboseType( self
):
2206 # Like getType, but returns "Lecture" instead of "simple_type"
2207 type = self
.getType()
2208 if type == "simple_event":
2210 return type.capitalize()
2213 def getEnableSessionSlots(self
):
2215 # if self._enableSessionSlots :
2217 #except AttributeError :
2218 # self._enableSessionSlots = True
2219 #if self.getType() == "conference":
2221 #return self._enableSessionSlots
2224 def getEnableSessions(self
):
2226 if self
._enableSessions
:
2228 except AttributeError :
2229 self
._enableSessions
= True
2230 if self
.getType() == "conference":
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:
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)
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()
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() == "":
2285 room
= self
.getRoom()
2289 room
.setName(confData
["roomName"])
2290 self
.notifyModification()
2292 def getVisibility ( self
):
2294 return int(self
._visibility
)
2296 self
._visibility
= 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
):
2315 self
._closed
= 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')
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
2352 return str(self
.___contribTypeGenerator
.newCount())
2354 self
.___contribTypeGenerator
= Counter()
2355 return str(self
.___contribTypeGenerator
.newCount())
2357 def addContribType(self
, ct
):
2359 if self
._contribTypes
:
2362 self
._contribTypes
= {}
2363 if ct
in self
._contribTypes
.values():
2367 id = self
.__generateNewContribTypeId
()
2369 self
._contribTypes
[id] = ct
2370 self
.notifyModification()
2372 def newContribType(self
, name
, description
):
2373 ct
= ContributionType(name
, description
, self
)
2374 self
.addContribType(ct
)
2377 def getContribTypeList(self
):
2379 return self
._contribTypes
.values()
2381 self
._contribTypes
= {}
2382 self
.notifyModification()
2383 return self
._contribTypes
.values()
2385 def getContribTypeById(self
, id):
2387 if self
._contribTypes
:
2390 self
._contribTypes
= {}
2391 self
.notifyModification()
2392 if id in self
._contribTypes
.keys():
2393 return self
._contribTypes
[id]
2396 def removeContribType(self
, ct
):
2398 if self
._contribTypes
:
2401 self
._contribTypes
= {}
2402 if not ct
in self
._contribTypes
.values():
2404 del self
._contribTypes
[ct
.getId()]
2405 for cont
in self
.getContributionList():
2406 if cont
.getType() == ct
:
2409 self
.notifyModification()
2411 def recoverContribType(self
, ct
):
2412 ct
.setConference(self
)
2413 self
.addContribType(ct
)
2416 def _getRepository( self
):
2417 dbRoot
= DBMgr
.getInstance().getDBConnection().root()
2419 fr
= dbRoot
["local_repositories"]["main"]
2421 fr
= fileRepository
.MaterialLocalRepository()
2422 dbRoot
["local_repositories"] = OOBTree()
2423 dbRoot
["local_repositories"]["main"] = fr
2426 def removeResource( self
, res
):
2430 cid
= self
.getUrlTag()
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:
2441 self
._logo
= logoFile
2442 self
.notifyModification()
2444 def getLogo( self
):
2447 def getLogoURL( self
):
2449 if self
._logo
== None:
2451 return self
._logo
.getURL()
2452 except AttributeError:
2456 def removeLogo(self
):
2457 if self
._logo
is None:
2461 self
.notifyModification()
2463 def recoverLogo(self
, logo
):
2465 if self
._logo
!= None:
2469 self
.notifyModification()
2471 def getSession(self
):
2474 def getContribution(self
):
2477 def getSubContribution(self
):
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()
2489 signals
.event
.data_changed
.send(self
, attr
=None, old
=None, new
=None)
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
):
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")
2523 """returns (string) the unique identifier of the conference"""
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 """
2540 d
["confId"] = self
.getId()
2543 def getOwner( self
):
2544 if self
.getOwnerList() == []:
2546 return self
.getOwnerList()[0]
2548 def getOwnerList( self
):
2549 return self
.__owners
2551 def getOwnerPath( self
):
2553 owner
= self
.getOwnerList()[0]
2554 while owner
!= None and owner
.getId() != "0":
2556 owner
= owner
.getOwner()
2559 def getOwnerById( self
, key
):
2560 """Returns one specific category which contains the conference.
2562 - key: The "id" of the category.
2564 for owner
in self
.__owners
:
2565 if key
== owner
.getId():
2569 def addOwner( self
, newOwner
):
2570 if newOwner
== None:
2572 self
.__owners
.append( newOwner
)
2573 self
.notifyModification()
2575 def removeOwner( self
, owner
, notify
=True ):
2576 if not (owner
in self
.__owners
):
2578 self
.__owners
.remove( owner
)
2579 owner
.removeConference( self
)
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
):
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
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)
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
:
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
:
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
2685 self
.setStartDate(sDate
, check
=0, moveEntries
= moveEntries
, index
=False, notifyObservers
= False)
2686 self
.setEndDate(eDate
, check
=0, index
=False, notifyObservers
= False)
2689 self
._checkInnerSchedule
()
2691 # reindex the conference
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():
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 ###################################
2721 self
.verifyStartDate(sDate
)
2722 oldSdate
= self
.getStartDate()
2723 diff
= sDate
- oldSdate
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()[:]
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()
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
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):
2780 tz
= self
.getTimezone()
2781 if tz
not in all_timezones
:
2783 return self
.getStartDate().astimezone(timezone(tz
))
2785 ###################################
2786 # Fermi timezone awareness(end) #
2787 ###################################
2789 def setScreenStartDate(self
, date
):
2790 if date
== self
.getStartDate():
2792 self
._screenStartDate
= date
2793 self
.notifyModification()
2795 def getScreenStartDate(self
):
2797 date
= self
._screenStartDate
2799 date
= self
._screenStartDate
= None
2803 return self
.getStartDate()
2805 def getAdjustedScreenStartDate(self
, tz
=None):
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():
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
)))
2835 self
.verifyEndDate(eDate
)
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()
2847 #if everything went well, we notify the observers that the start date has changed
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"""
2865 ##################################
2866 # Fermi timezone awareness #
2867 ##################################
2869 def getAdjustedEndDate(self
,tz
=None):
2871 tz
= self
.getTimezone()
2872 if tz
not in all_timezones
:
2874 return self
.getEndDate().astimezone(timezone(tz
))
2876 ##################################
2877 # Fermi timezone awareness(end) #
2878 ##################################
2880 def setScreenEndDate(self
, date
):
2881 if date
== self
.getEndDate():
2883 self
._screenEndDate
= date
2884 self
.notifyModification()
2886 def getScreenEndDate(self
):
2888 date
= self
._screenEndDate
2890 date
= self
._screenEndDate
= None
2894 return self
.getEndDate()
2896 def getAdjustedScreenEndDate(self
, tz
=None):
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
):
2912 oldTimezone
= self
.timezone
2913 except AttributeError:
2917 def getTimezone(self
):
2919 return self
.timezone
2923 def moveToTimezone(self
, tz
):
2924 if self
.getTimezone() == tz
:
2926 sd
=self
.getAdjustedStartDate()
2927 ed
=self
.getAdjustedEndDate()
2928 self
.setTimezone(tz
)
2930 sDate
= timezone(tz
).localize(datetime(sd
.year
, \
2935 eDate
= timezone(tz
).localize(datetime(ed
.year
, \
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')),
2948 ####################################
2949 # Fermi timezone awareness(end) #
2950 ####################################
2953 """returns (String) the title of the conference"""
2956 def setTitle(self
, title
):
2957 """changes the current title of the conference to the one specified"""
2958 oldTitle
= self
.title
2961 self
.notifyModification()
2963 nameIdx
= indexes
.IndexesHolder().getIndex('conferenceTitle')
2964 nameIdx
.unindex(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
):
2992 if self
.chairmanText
:
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()
3006 def _resetChairs(self
):
3010 except AttributeError:
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)
3023 def _addChair(self
,newChair
):
3024 for chair
in self
._chairs
:
3025 if newChair
.getEmail() != "" and newChair
.getEmail() == chair
.getEmail():
3030 except AttributeError:
3031 self
._chairGen
=Counter()
3032 id = newChair
.getId()
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
3046 self
._addChair
(newChair
)
3048 def removeChair(self
,chair
):
3049 """removes the specified user from the list of conference
3052 if chair
not in self
._chairs
:
3054 self
._chairs
.remove(chair
)
3055 if isinstance(chair
, AvatarUserWrapper
):
3056 chair
.unlinkTo(self
, "chair")
3058 self
.notifyModification()
3060 def recoverChair(self
, ch
):
3064 def getChairById(self
,id):
3066 for chair
in self
._chairs
:
3067 if chair
.getId()==id:
3071 def getAllSessionsConvenerList(self
) :
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
)
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
3100 def getLocation( self
):
3101 return self
.getOwnLocation()
3103 def getAddress( self
):
3104 if self
.getOwnLocation():
3105 return self
.getOwnLocation().getAddress()
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
3119 def getFavoriteRooms(self
):
3121 roomList
.extend(self
.getRoomList())
3122 #roomList.extend(map(lambda x: x._getName(), self.getBookedRooms()))
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
):
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
):
3149 return self
._modifKey
3150 except AttributeError:
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
3165 1: check and raise error in case of problem
3166 2: check and adapt the owner dates"""
3168 if self
.hasSession(new_session
):
3170 if self
.getSchedule().isOutside(new_session
):
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()),
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
)
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()):
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():
3212 del self
.sessions
[session
.getId()]
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
):
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
)
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
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
:
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
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
):
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
()
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() ):
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() ]
3321 self
.notifyModification()
3323 def recoverContribution(self
, contrib
):
3324 self
.addContribution(contrib
, contrib
.getId())
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)
3334 self
.addContribution( c
, id )
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 ]
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):
3375 return len(filter(lambda c
: c
.isScheduled(), self
.contributions
.itervalues()))
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))
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
):
3395 return self
.programDescription
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
:
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
:
3426 if track
in self
.program
:
3427 self
.program
.remove( track
)
3428 self
.notifyModification()
3430 def recoverTrack(self
, track
):
3431 self
.addTrack(track
)
3434 def newTrack( self
):
3441 def getTrackById( self
, id ):
3444 for track
in self
.program
:
3445 if track
.getId() == id.strip():
3449 def getTrackList( self
):
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
):
3481 newPos
=self
.getTrackPos(track
)-1
3482 self
.moveTrack(track
,newPos
)
3484 def moveDownTrack(self
,track
):
3487 if self
.isLastTrack(track
):
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
)
3497 def sortTrackList(self
, l
):
3498 """Sorts out a list of tracks according to the current programme order.
3508 res
.sort(self
._cmpTracks
)
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():
3539 if self
.getDomainList():
3542 if self
.getAccessProtectionLevel() == -1:
3545 for owner
in self
.getOwnerList():
3546 if owner
.hasAnyProtection():
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
:
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
):
3582 for session
in self
.getSessionList():
3583 if session
.canView( aw
):
3585 for contrib
in self
.getContributionList():
3586 if contrib
.canView( aw
):
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)
3596 if (av
in self
.getChairList()) or (self
.__ac
.canUserAccess( av
)) or (self
.canUserModify( av
)):
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
):
3606 # track coordinators are also allowed to access the conference
3607 for track
in self
.getTrackList():
3608 if track
.isCoordinator( av
):
3611 # paper reviewing team should be also allowed to access
3612 if self
.getConfPaperReview().isInReviewingTeam(av
):
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
3624 # Allow harvesters (Invenio, offline cache) to access
3626 if has_request_context() and self
.__ac
.isHarvesterIP(request
.remote_addr
):
3629 # Managers have always access
3630 if self
.canModify(aw
):
3633 if self
.isProtected():
3634 if self
.isAllowedToAccess( aw
.getUser() ):
3637 return self
.canKeyAccess(aw
)
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()
3646 return key
== accessKey
or session
.get('accessKeys', {}).get(self
.getUniqueId()) == accessKey
3648 def canKeyModify(self
):
3649 modifKey
= self
.getModifKey()
3652 return session
.get('modifKeys', {}).get(self
.id) == modifKey
3654 def grantModification( self
, prin
, sendEmail
=True ):
3656 if isinstance(prin
, ConferenceChair
):
3657 email
= prin
.getEmail()
3658 elif isinstance(prin
, str):
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")
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
):
3690 if ( av
== self
.getCreator()) or self
.getAccessController().canModify( av
):
3692 for owner
in self
.getOwnerList():
3693 if owner
.canUserModify( av
):
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
):
3726 return any(principal
.containsUser(av
) for principal
in self
.getRegistrarList())
3727 except AttributeError:
3730 def getRegistrarList(self
):
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():
3752 self
.materials
[mat
.getId()].setOwner(None)
3753 del self
.materials
[ mat
.getId() ]
3754 self
.notifyModification()
3755 elif mat
.getId().lower() == 'paper':
3757 self
.notifyModification()
3758 elif mat
.getId().lower() == 'slides':
3760 self
.notifyModification()
3761 elif mat
.getId().lower() == 'video':
3763 self
.notifyModification()
3764 elif mat
.getId().lower() == 'poster':
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
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
]
3795 def getMaterialList( self
):
3796 return self
.materials
.values()
3798 def getAllMaterialList(self
, sort
=True):
3799 l
= self
.getMaterialList()
3801 l
.append( self
.getPaper() )
3802 if self
.getSlides():
3803 l
.append( self
.getSlides() )
3805 l
.append( self
.getVideo() )
3806 if self
.getPoster():
3807 l
.append( self
.getPoster() )
3809 l
.sort(lambda x
,y
: cmp(x
.getTitle(),y
.getTitle()))
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.
3820 for res
in material
.getResourceList():
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())
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
,
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
3849 node
['title'] = child
.getTitle()
3852 node
['type'] = child
.getType()
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
)
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
)
3881 def setPaper( self
, newPaper
):
3882 if self
.getPaper() != None:
3883 raise MaKaCError( _("The paper for this conference has already been set"), _("Conference"))
3885 self
.paper
.setOwner( self
)
3886 self
.notifyModification()
3888 def removePaper( self
):
3889 if self
.paper
is None:
3892 self
.paper
.setOwner(None)
3894 self
.notifyModification()
3896 def recoverPaper(self
, p
):
3900 def getPaper( self
):
3904 except AttributeError:
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:
3918 self
.slides
.delete()
3919 self
.slides
.setOwner( None )
3921 self
.notifyModification()
3923 def recoverSlides(self
, s
):
3927 def getSlides( self
):
3931 except AttributeError:
3935 def setVideo( self
, newVideo
):
3936 if self
.getVideo() != None:
3937 raise MaKaCError( _("The video for this conference has already been set"), _("Conference"))
3939 self
.video
.setOwner( self
)
3940 self
.notifyModification()
3942 def removeVideo( self
):
3943 if self
.getVideo() is None:
3946 self
.video
.setOwner(None)
3948 self
.notifyModification()
3950 def recoverVideo(self
, v
):
3954 def getVideo( self
):
3958 except AttributeError:
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:
3972 self
.poster
.delete()
3973 self
.poster
.setOwner(None)
3975 self
.notifyModification()
3977 def recoverPoster(self
, p
):
3981 def getPoster( self
):
3985 except AttributeError:
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
):
3997 if not self
.__schedule
:
3999 except AttributeError, e
:
4001 return self
.__schedule
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:
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():
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()
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"]
4072 confRegistry
= OOBTree
.OOBTree()
4073 db_root
["webfactoryregistry"] = confRegistry
4075 # if the event is a meeting or a lecture
4076 if confRegistry
.get(str(self
.getId()), None) is not None :
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 :
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
)
4156 def getCoordinatedTracks( self
, av
):
4157 """Returns a list with the tracks for which a user is coordinator.
4160 if self
._trackCoordinators
:
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.
4171 if self
._trackCoordinators
:
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.
4185 if self
._trackCoordinators
:
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
):
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
):
4237 if self
._speakerIdx
:
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
):
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
):
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
)
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.
4284 id -- id of the wanted evaluation
4286 for evaluation
in self
.getEvaluations():
4287 if str(evaluation
.getId()) == str(id) :
4289 if Config
.getInstance().getDebug():
4290 raise Exception(_("Error with id: expected '%s', found '%s'.")%(id, self
.getEvaluations()[0].getId()))
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
):
4328 except AttributeError, e
:
4330 self
.notifyModification()
4331 return self
._bookings
4333 def getBookingsList(self
, sort
= False):
4334 bl
= self
.getBookings().values()
4339 def _getBookingGenerator(self
):
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()
4367 def getBookingByType(self
, type):
4368 if self
.getBookings().has_key(type):
4369 return self
.getBookings()[type]
4372 def getBookingById(self
, id):
4373 if self
.getBookings().has_key(id):
4374 return self
.getBookings()[id]
4377 ## End of Videoconference bookings related
4379 def getRegistrants(self
):
4381 if self
._registrants
:
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.
4394 if self
._registrantsByEmail
:
4396 except AttributeError, e
:
4397 self
._registrantsByEmail
= self
._createRegistrantsByEmail
()
4398 self
.notifyModification()
4400 return self
._registrantsByEmail
.get(email
)
4401 return self
._registrantsByEmail
4403 def _createRegistrantsByEmail(self
):
4405 for r
in self
.getRegistrantsList():
4406 dicByEmail
[r
.getEmail()] = r
4409 def getRegistrantsList(self
, sort
= False):
4410 rl
= self
.getRegistrants().values()
4412 rl
.sort(registration
.Registrant
._cmpFamilyName
)
4415 def _getRegistrantGenerator(self
):
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()) )
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]
4459 def _getPrimAuthIndex(self
):
4461 if self
._primAuthIdx
:
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.
4477 if self
._sessionCoordinators
:
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
)
4490 def getManagedSession( self
, av
):
4492 for session
in self
.getSessionList():
4495 for email
in av
.getEmails():
4496 if email
in session
.getAccessController().getModificationEmail():
4499 if av
in session
.getManagerList() or pending
:
4503 def addSessionCoordinator(self
,session
,av
):
4504 """Makes a user become coordinator for a session.
4507 if self
._sessionCoordinators
:
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.
4520 if self
._sessionCoordinators
:
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
):
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
):
4549 except AttributeError:
4550 self
._boa
=BOAConfig(self
)
4553 def getSessionCoordinatorRights(self
):
4555 if self
._sessionCoordinatorRights
:
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
4584 def getPendingQueuesMgr(self
):
4586 if self
._pendingQueuesMgr
:
4588 except AttributeError, e
:
4589 self
._pendingQueuesMgr
=pendingQueues
.ConfPendingQueuesMgr(self
)
4590 return self
._pendingQueuesMgr
4592 def getAccessController(self
):
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
):
4603 if self
._reportNumberHolder
:
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
):
4614 if self
.__badgeTemplateManager
:
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
):
4625 if self
.__posterTemplateManager
:
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
):
4644 admin
= User
.find_first(is_admin
=True)
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"
4660 id = ObjectHolder
._newId
( self
)
4663 def getById(self
, id, quiet
=False):
4665 return CategoryManager().getDefaultConference()
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"))
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
__
4702 conf
= self
.getOwner()
4703 name
= name
+ " of event " + conf
.getId() + "'"
4704 except AttributeError:
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
):
4781 ConferenceParticipation
.__init
__(self
)
4783 def _notifyModification( self
):
4784 if self
._session
!= None:
4785 self
._session
.notifyModification()
4788 chair
= SessionChair()
4789 chair
.setValues(self
.getValues())
4792 def getSession(self
):
4793 return self
._session
4795 def getConference(self
):
4799 return s
.getConference()
4801 def includeInSession(self
,session
,id):
4802 if self
.getSession()==session
and self
.getId()==id.strip():
4804 self
._session
=session
4809 ConferenceParticipation
.delete(self
)
4811 def getLocator(self
):
4812 if self
.getSession() is None:
4814 loc
=self
.getSession().getLocator()
4815 loc
["convId"]=self
.getId()
4818 def isSessionManager(self
):
4820 if self
.getEmail() in self
._session
.getAccessController().getModificationEmail():
4823 for manager
in self
._session
.getManagerList():
4824 if self
.getEmail() == manager
.getEmail():
4828 def isSessionCoordinator(self
):
4829 # pendings coordinators
4830 if self
.getEmail() in self
._session
.getConference().getPendingQueuesMgr().getPendingCoordinatorsKeys():
4833 for coord
in self
._session
.getCoordinatorList():
4834 if self
.getEmail() == coord
.getEmail():
4839 class SlotChair(ConferenceParticipation
):
4844 ConferenceParticipation
.__init
__(self
)
4846 def _notifyModification( self
):
4847 if self
._slot
!= None:
4848 self
._slot
.notifyModification()
4852 chair
.setValues(self
.getValues())
4858 def getSession(self
):
4862 return s
.getSession()
4864 def getConference(self
):
4868 return s
.getConference()
4870 def includeInSlot(self
,slot
,id):
4871 if self
.getSlot()==slot
and self
.getId()==id.strip():
4878 ConferenceParticipation
.delete(self
)
4880 def getLocator(self
):
4881 if self
.getSlot() is None:
4883 loc
=self
.getSlot().getLocator()
4884 loc
["convId"]=self
.getId()
4887 class SessionCoordinatorRights
:
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
):
4900 def getRightList(self
, sort
=False):
4901 l
=self
._rights
.values()
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]
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
):
4931 def getSessions(self
,av
):
4932 """Gives a list with the sessions a user is coordinating.
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:
4943 if not self
._idx
.has_key(av
.getId()):
4945 self
._idx
[av
.getId()]=l
4947 l
=self
._idx
[av
.getId()]
4948 if session
not in l
:
4950 self
.notifyModification()
4952 def unindex(self
,av
,session
):
4953 if av
==None or session
==None:
4955 l
=self
._idx
.get(av
.getId(),[])
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
4980 sessionData -- (Dict) Contains the data the session object has to
4983 self
.conference
=None
4984 self
.id="not assigned"
4987 #################################
4988 # Fermi timezone awareness #
4989 #################################
4990 self
.startDate
= nowutc()
4991 #################################
4992 # Fermi timezone awareness(end) #
4993 #################################
4995 self
.duration
=timedelta(minutes
=1)
4998 self
.conveners
=[] # This attribute must not be used and should disappear someday
5000 self
._convenerGen
=Counter()
5001 self
.convenerText
=""
5002 self
.contributions
={}
5003 self
._contributionDuration
=timedelta(minutes
=20)
5004 self
.__ac
=AccessController(self
)
5006 self
.__materialGenerator
=Counter()
5009 self
.__slotGenerator
=Counter()
5011 self
._coordinators
=OOBTree()
5012 self
._coordinatorsEmail
= []
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()
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())
5043 from indico
.modules
.events
.notes
.models
.notes
import EventNote
5044 return EventNote
.get_for_linked_object(self
)
5046 def getVerboseType(self
):
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
):
5064 return self
._keywords
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()
5079 parent
.setModificationDate(date
)
5081 for slot
in self
.getSlotList():
5085 def getModificationDate( self
):
5086 """Returns the date in which the session was last modified"""
5088 return self
._modificationDS
5090 self
._modificationDS
= nowutc()
5091 return self
._modificationDS
5093 def getCreationDate( self
):
5094 """Returns the date in which the session was created"""
5096 return self
._creationDS
5098 self
._creationDS
= nowutc()
5099 return self
._creationDS
5101 def getLogInfo(self
):
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()
5121 def getEnableSessionSlots(self
):
5123 return self
.getConference().getEnableSessionSlots()
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
):
5136 if self
._registrationSession
:
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():
5151 self
._closed
= 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
5161 for slot
in self
.getSlotList():
5162 conf
.getSchedule().addEntry(slot
.getConfSchEntry(),2)
5163 self
.getConference().addSession(self
)
5164 self
.notifyModification()
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():
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:
5201 lconf
= self
.conference
.getLocator()
5202 lconf
["sessionId"] = self
.getId()
5205 def getConference( self
):
5206 return self
.conference
5208 def getSession( self
):
5211 def getOwner( self
):
5212 return self
.getConference()
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
):
5230 return self
._contributionDuration
5232 self
._contributionDuration
= timedelta(minutes
=20)
5233 return self
._contributionDuration
5235 def setContribDuration(self
, hour
=0, min=20, dur
=None):
5237 self
._contributionDuration
=dur
5239 self
._contributionDuration
= timedelta(hours
=hour
,minutes
=min)
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
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())
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
)
5274 self
.notifyModification()
5276 def recoverSlot(self
, slot
):
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
)
5291 def getMinSlotStartTime(self
):
5293 for slot
in self
.getSlotList():
5294 if slot
.isMoreThanDay():
5296 shour
= slot
.getStartDate().hour
5297 smin
= slot
.getStartDate().minute
5298 if (shour
, smin
) < min:
5302 def getMaxSlotEndTime(self
):
5304 for slot
in self
.getSlotList():
5305 if slot
.isMoreThanDay():
5307 endDate
= slot
.getEndDate()
5308 if (endDate
.hour
, endDate
.minute
) > max:
5309 newEndDate
= endDate
- timedelta(0, 0, 0)
5310 max = (newEndDate
.hour
, newEndDate
.minute
)
5313 def getMinSlotStartDate(self
):
5314 slotList
= self
.getSlotList()
5315 if len(slotList
)==0:
5316 return self
.getStartDate()
5318 sDate
= self
.getEndDate()
5319 for slot
in slotList
:
5320 if slot
.getStartDate() < sDate
:
5321 sDate
= slot
.getStartDate()
5324 def getMaxSlotEndDate(self
):
5325 slotList
= self
.getSlotList()
5326 if len(slotList
)==0:
5327 return self
.getEndDate()
5329 eDate
= self
.getStartDate()
5330 for slot
in slotList
:
5331 if slot
.getEndDate() > eDate
:
5332 eDate
= slot
.getEndDate()
5335 def _getCorrectColor(self
, color
):
5336 if not color
.startswith("#"):
5338 m
= re
.match("^#[0-9A-Fa-f]{6}$", color
)
5343 def _getCorrectBgColor(self
, color
):
5344 color
=self
._getCorrectColor
(color
)
5349 def _getCorrectTextColor(self
, color
):
5350 color
=self
._getCorrectColor
(color
)
5352 return self
._textColor
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:
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
5371 durMin - (int) => hours of duration for each entry in the session
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")
5390 self
.setCode(self
.getId())
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()))
5401 self
.setTextColor(self
._getCorrectTextColor
(textcolor
))
5402 self
.setTextColorToLinks(sessionData
.has_key("textcolortolinks"))
5404 if "locationName" in sessionData
:
5405 loc
= self
.getOwnLocation()
5407 loc
= CustomLocation()
5408 self
.setLocation( loc
)
5409 loc
.setName( sessionData
["locationName"] )
5410 loc
.setAddress( sessionData
.get("locationAddress", "") )
5412 self
.setLocation(None)
5414 #same as for the location
5415 if "roomName" in sessionData
:
5416 room
= self
.getOwnRoom()
5419 self
.setRoom( room
)
5420 room
.setName( sessionData
["roomName"] )
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")
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):
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()
5504 def setTitle( self
, newTitle
):
5505 self
.title
= newTitle
5506 self
.notifyModification()
5508 def getTitle( self
):
5511 def setDescription(self
, newDescription
):
5512 self
.description
= newDescription
5513 self
.notifyModification()
5515 def getDescription(self
):
5516 return self
.description
5522 except AttributeError:
5526 def setCode(self
,newCode
):
5527 self
._code
=str(newCode
).strip()
5533 except AttributeError:
5534 self
._color
="#e3f2d3"
5538 def setColor(self
,newColor
):
5539 self
._color
=str(newColor
).strip()
5540 self
.notifyModification()
5543 def getTextColor(self
):
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
):
5557 if self
._textColorToLink
:
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):
5572 tz
= self
.getConference().getTimezone()
5573 if tz
not in all_timezones
:
5575 return self
.startDate
.astimezone(timezone(tz
))
5577 def verifyStartDate(self
, sdate
, check
=2):
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():
5588 raise ParentTimingError( _("The session starting date cannot be prior to the event starting date"), _("Session"))
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
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")
5605 self
.verifyStartDate(newDate
,check
)
5606 oldSdate
= self
.getStartDate()
5608 tz
= str(self
.getStartDate().tzinfo
)
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()[:]
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
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):
5652 1: check and raise error in case of problem
5653 2: check and adapt the owner dates
5656 tz
= timezone(self
.getConference().getTimezone())
5658 tz
= timezone('UTC')
5659 # compare end date with start date
5660 if edate
<=self
.getStartDate():
5662 raise MaKaCError( _("End date cannot be prior to the Start date"), _("Session"))
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
):
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')),\
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")
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):
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')),
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):
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"))
5721 self
.verifyEndDate(self
.getEndDate())
5722 self
.notifyModification()
5724 def getStartOnDay(self
, day
, tz
=None):
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() :
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()
5741 def getEndOnDay(self
, day
, tz
=None):
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() :
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()
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
5772 def addLocation(self
, newPlace
):
5773 self
.places
.append( newPlace
)
5774 self
.notifyModification()
5776 def _resetConveners(self
):
5780 except AttributeError:
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
)
5798 def _addConvener(self
,newConv
):
5799 if newConv
in self
._conveners
:
5802 if self
._convenerGen
:
5804 except AttributeError:
5805 self
._convenerGen
=Counter()
5806 id = newConv
.getId()
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
:
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")
5833 self
.notifyModification()
5835 def recoverConvener(self
, con
):
5836 self
.addConvener(con
)
5839 def getConvenerById(self
,id):
5841 for conv
in self
._conveners
:
5842 if conv
.getId()==id:
5846 def getConvenerText( self
):
5849 if self
.convenerText
:
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
):
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
):
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 ):
5904 self
.addContribution( c
, id )
5907 def getContributionById(self
,id):
5909 if self
.contributions
.has_key( id ):
5910 return self
.contributions
[ id ]
5913 def getContributionList( self
):
5914 return self
.contributions
.values()
5916 def getNumberOfContributions(self
, only_scheduled
=False):
5918 return len(filter(lambda c
: c
.isScheduled(), self
.contributions
.itervalues()))
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():
5938 if self
.getDomainList():
5940 if self
.getAccessProtectionLevel() == -1:
5943 return self
.getOwner().hasAnyProtection()
5945 def hasProtectedOwner( self
):
5946 if self
.getOwner() != None:
5947 return self
.getOwner().isProtected()
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
):
5971 for contrib
in self
.getContributionList():
5972 if contrib
.canView( aw
):
5976 def isAllowedToAccess( self
, user
):
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
)):
5984 def canAccess( self
, aw
):
5985 # Allow harvesters (Invenio, offline cache) to access
5987 if has_request_context() and self
.__ac
.isHarvesterIP(request
.remote_addr
):
5989 #####################################################
5991 # Managers have always access
5992 if self
.canModify(aw
):
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
:
5999 if not self
.isProtected():
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
):
6006 results
= ah
.match({"email": sb
.getEmail()}, exact
=1)
6009 if sb
.getEmail().lower().strip() in [j
.lower().strip() for j
in i
.getEmails()]:
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')
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():
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
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
]
6082 def getMaterialList( self
):
6083 return self
.materials
.values()
6085 def getAllMaterialList(self
, sort
=True):
6086 l
= self
.getMaterialList()
6088 l
.sort(lambda x
,y
: cmp(x
.getTitle(),y
.getTitle()))
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
):
6099 if self
.__schedule
is None or not isinstance(self
.__schedule
,SessionSchedule
):
6101 except AttributeError, e
:
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
):
6124 except AttributeError,e
:
6126 return self
._comments
6128 def _addCoordinator(self
, av
):
6129 if av
is None or self
._coordinators
.has_key(av
.getId()):
6131 self
._coordinators
[av
.getId()]=av
6132 if self
.getConference() is not None:
6133 self
.getConference().addSessionCoordinator(self
,av
)
6135 def getCoordinatorEmailList(self
):
6137 return self
._coordinatorsEmail
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
)
6151 def addCoordinator( self
, sb
, sendEmail
=True ):
6152 """Grants coordination privileges to user.
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.
6163 if self
._coordinators
:
6165 except AttributeError, e
:
6166 self
._coordinators
=OOBTree()
6168 if isinstance(sb
, SessionChair
):
6170 results
=ah
.match({"email":sb
.getEmail()}, exact
=1)
6174 if sb
.getEmail().lower().strip() in [j
.lower().strip() for j
in i
.getEmails()]:
6178 if r
is not None and r
.isActivated():
6180 self
._addCoordinator
(r
)
6181 r
.linkTo(self
, "coordinator")
6183 self
.getConference().getPendingQueuesMgr().addPendingCoordinator(sb
)
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.
6193 av -- (AvatarUserWrapper) user for which coordination privileges
6197 if self
._coordinators
:
6199 except AttributeError, e
:
6200 self
._coordinators
=OOBTree()
6202 if av
is None or not self
._coordinators
.has_key(av
.getId()):
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.
6214 av -- (AvatarUserWrapper) user to be checked
6216 Return value: (boolean)
6219 if self
._coordinators
:
6221 except AttributeError, e
:
6222 self
._coordinators
=OOBTree()
6223 if (av
is not None) and self
._coordinators
.has_key(av
.getId()):
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
)
6234 def hasConvenerByEmail(self
, email
):
6235 for convener
in self
.getConvenerList():
6236 if email
== convener
.getEmail():
6241 def getCoordinatorList( self
):
6242 """Return all users which have privileges to coordinate the session.
6244 Return value: (list)
6247 if self
._coordinators
:
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
6261 aw -- (MaKaC.accessControl.AccessWrapper) User access
6262 information for which the coordination privileges must be
6265 Return value: (boolean)
6267 if hasattr(aw_or_user
, 'getUser'):
6268 aw_or_user
= aw_or_user
.getUser()
6270 return self
.isCoordinator(aw_or_user
) and self
.getConference().hasSessionCoordinatorRight(right
)
6271 return self
.isCoordinator(aw_or_user
)
6274 def getScheduleType(self
):
6278 except AttributeError:
6279 self
._ttType
=SlotSchTypeFactory
.getDefaultId()
6282 def setScheduleType(self
,t
):
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
:
6292 for slot
in self
.getSlotList():
6293 slot
.setScheduleType(t
)
6295 def getAccessController(self
):
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"
6315 self
.duration
= timedelta(minutes
=1)
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
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
):
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
)
6342 for p
in self
.places
:
6343 data
["place %s"%i] = p
.getName()
6346 for r
in self
.rooms
:
6347 data
["room %s"%i] = r
.getName()
6349 for c
in self
._conveners
:
6350 data
["convener %s"%c.getId()] = c
.getFullName()
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)
6363 if self
.getOwnLocation() is not None:
6364 slot
.setLocation(self
.getOwnLocation().clone())
6366 if self
.getOwnRoom() is not None:
6367 slot
.setRoom(self
.getOwnRoom().clone())
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)
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
):
6410 if i
+1 == len(entries
):
6411 dur
=self
.getEndDate()-entry
.getStartDate()
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
)
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
6428 #if not isinstance(entry, BreakTimeSchEntry):
6429 # st=entry.getEndDate()+diff
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):
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()
6458 loc
= CustomLocation()
6459 self
.setLocation( loc
)
6460 loc
.setName( data
["locationName"] )
6461 loc
.setAddress( data
.get("locationAddress", "") )
6463 self
.setLocation( None )
6465 if "roomName" in data
:
6466 room
= self
.getOwnRoom()
6469 self
.setRoom( room
)
6470 room
.setName( data
["roomName"] )
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
)
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"))
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
:
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
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.
6540 if self
._contributionDuration
:
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)
6549 self
.cleanCache(cleanCacheEntries
)
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()
6565 def getConference( self
):
6566 return self
.getSession().getConference()
6568 def getSession(self
):
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
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
):
6598 if self
._confSchEntry
:
6600 except AttributeError:
6601 self
._confSchEntry
=LinkedTimeSchEntry(self
)
6602 return self
._confSchEntry
6604 def getSessionSchEntry( self
):
6606 if self
._sessionSchEntry
:
6608 except AttributeError:
6609 self
._sessionSchEntry
=self
._schEntry
6610 return self
._sessionSchEntry
6612 def setId( self
, newId
):
6614 self
.notifyModification()
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
):
6626 self
.notifyModification()
6628 def getTitle( self
):
6632 except AttributeError,e
:
6636 def getFullTitle( self
):
6637 return self
.getSession().getTitle() + (": " + self
.getTitle() if self
.getTitle() else "")
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):
6648 1: check and raise error in case of problem
6649 2: check and adapt the owner dates"""
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()
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
6669 se
= slotEntry
.getOwner()
6670 se
.startDate
= se
.getStartDate() + diff
6671 self
.getSchedule().reSchedule()
6673 def verifyStartDate(self
, sDate
,check
=2):
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():
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')),\
6688 self
.getSession().setStartDate(sDate
, check
, 0)
6690 def setStartDate(self
,sDate
,check
=2,moveEntries
=0,checkDuration
=True):
6693 1: check and raise error in case of problem
6694 2: check and adapt the owner dates"""
6697 if not sDate
.tzname():
6698 raise MaKaCError("date should be timezone aware")
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
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")
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):
6744 tz
= self
.getConference().getTimezone()
6745 if tz
not in all_timezones
:
6747 return self
.startDate
.astimezone(timezone(tz
))
6749 def getEndDate( self
):
6750 if self
.startDate
is None:
6752 return self
.startDate
+self
.duration
6754 def getAdjustedEndDate( self
, tz
=None ):
6756 tz
= self
.getConference().getTimezone()
6757 if tz
not in all_timezones
:
6759 if self
.getEndDate():
6760 return self
.getEndDate().astimezone(timezone(tz
))
6763 def getDuration( self
):
6764 return self
.duration
6766 def isMoreThanDay(self
):
6767 if self
.getDuration() >= timedelta(days
=1):
6771 def verifyDuration(self
, dur
, check
=1):
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"))
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
:
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')),\
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()
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')),\
6809 def setDuration(self
, days
=0,hours
=0,minutes
=0,dur
=0,check
=1):
6812 1: check and raise error in case of problem
6813 2: check and adapt the owner dates"""
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",
6820 dur
= timedelta(minutes
=1)
6821 if dur
> timedelta(days
=1) and check
==2:
6822 pass#dur = timedelta(days=1)
6824 self
.verifyDuration(dur
, check
)
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
6839 signals
.event
.session_slot_deleted
.send(self
)
6840 self
.getSchedule().clear()
6841 if self
.getSession() is not None:
6842 self
.getSession().removeSlot(self
)
6844 TrashCanManager().add(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
:
6869 self
._setSchedule
(SlotSchTypeFactory
.getScheduleKlass(id))
6871 def getConvenerList(self
):
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
:
6885 if self
._convenerGen
:
6887 except AttributeError:
6888 self
._convenerGen
=Counter()
6889 id = newConv
.getId()
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
:
6899 self
._conveners
.remove(conv
)
6901 self
.notifyModification()
6903 def recoverConvener(self
, con
):
6904 self
.addConvener(con
)
6907 def getConvenerById(self
,id):
6909 for conv
in self
._conveners
:
6910 if conv
.getId()==id:
6914 def getOwnConvenerList(self
):
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()
6929 if self
.getSession() is not None:
6930 res
=self
.getSession().getColor()
6933 def getTextColor(self
):
6935 if self
.getSession() is not None:
6936 res
=self
.getSession().getTextColor()
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
6958 self
._firstName
= ""
6961 self
._affiliation
= ""
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
):
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()
6995 part
= ContributionParticipation()
6996 part
.setValues(self
.getValues())
6999 def setDataFromAvatar(self
,av
):
7000 # av is an Avatar object.
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.
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():
7030 self
._contrib
= contrib
7034 self
._contrib
= None
7035 TrashCanManager().add(self
)
7038 TrashCanManager().remove(self
)
7040 def setId(self
, newId
):
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:
7055 loc
=self
.getContribution().getLocator()
7056 loc
["authorId"]=self
.getId()
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
)
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
):
7078 if tmp
==self
._firstName
:
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
):
7095 if tmp
==self
._surName
:
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
):
7112 if tmp
==self
._email
:
7115 self
._email
=newMail
.strip()
7117 self
._notifyModification
()
7119 def getEmail( self
):
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":
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
):
7148 @Updates ('MaKaC.conference.ContributionParticipation', 'title')
7149 def setTitle( self
, newTitle
):
7150 self
._title
= newTitle
.strip()
7151 self
._notifyModification
()
7153 def getTitle( self
):
7156 @Updates ('MaKaC.conference.ContributionParticipation', 'fax')
7157 def setFax( self
, newFax
):
7158 self
._fax
= newFax
.strip()
7159 self
._notifyModification
()
7165 except AttributeError:
7169 def getDirectFullName( self
):
7170 res
= self
.getDirectFullNameNoTitle()
7171 if self
.getTitle() != "":
7172 res
= "%s %s"%( self
.getTitle(), 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()
7182 res
= "%s %s" % (self
.getTitle(), res
)
7185 def getFullNameNoTitle(self
):
7186 res
= safe_upper(self
.getFamilyName())
7187 if self
.getFirstName():
7189 res
= "%s, %s" % (res
, self
.getFirstName())
7191 res
= self
.getFirstName()
7194 def getAbrName(self
):
7195 res
= self
.getFamilyName()
7196 if self
.getFirstName():
7199 res
= "%s%s." % (res
, safe_upper(self
.getFirstName()[0]))
7202 def isSubmitter(self
):
7203 if self
.getContribution() is None:
7205 return self
.getContribution().canUserSubmit(self
)
7207 def isPendingSubmitter(self
):
7208 if self
.getContribution() is None:
7210 if self
.getContribution().getConference() is None:
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
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
):
7233 def _getKey(self
,author
):
7234 k
= "%s %s %s"%(author
.getFamilyName().lower(),author
.getFirstName().lower(),author
.getEmail().lower())
7237 def index(self
,author
):
7238 key
=self
._getKey
(author
)
7239 if not self
._idx
.has_key(key
):
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
]:
7253 if len(self
._idx
[key
])<=0:
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
7273 def iteritems(self
):
7274 return self
._idx
.iteritems()
7276 def match(self
, criteria
, exact
=0):
7277 self
._options
= ['organisation', 'surName', 'name', 'email']
7279 for item
in self
.getParticipations():
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()):
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()):
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()):
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()):
7303 if len(ok
) > 0 and not False in ok
:
7307 class _AuthIdx(Persistent
):
7309 def __init__(self
,conf
):
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
):
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:
7344 def match(self
,query
):
7345 query
=query
.lower().strip()
7347 for k
in self
._idx
.keys():
7348 if k
.find(query
)!=-1:
7349 res
=union(res
,self
._idx
[k
])
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():
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
):
7374 self
._session
= None
7378 self
.description
= ""
7379 self
.startDate
= None
7380 self
.duration
= timedelta(0)
7382 self
.speakerText
= ""
7385 self
._boardNumber
= ""
7386 self
._resetSchEntry
()
7387 self
.__ac
= AccessController(self
)
7389 self
.__materialGenerator
= Counter()
7391 self
.__subContGenerator
= Counter()
7396 self
.reviewing
= None
7397 self
._authorGen
= Counter()
7398 self
._authors
= OOBTree()
7399 self
._primaryAuthors
= []
7400 self
._coAuthors
= []
7404 self
._status
= ContribStatusNotSch(self
)
7405 #List of allowed users to submit material
7406 self
._submitters
= []
7407 self
._submittersEmail
= []
7408 self
._modificationDS
= nowutc()
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())
7425 parent_id
= self
.parent
.getId() if self
.parent
else None
7426 return u
'<Contribution({}, {}, {})>'.format(self
.getId(), to_unicode(self
.getTitle()), parent_id
)
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
):
7454 return self
._keywords
7459 def setKeywords(self
, keywords
):
7460 if type(keywords
) is list:
7461 self
._keywords
= keywords
[0]
7463 self
._keywords
= keywords
7464 self
.notifyModification(cleanCache
=False)
7466 def getFields(self
, valueonly
=False):
7470 except AttributeError:
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
):
7486 self
.getFields()[fid
].value
= v
7488 # `AttritbuteError` may happen if the field is not yet an AbstractFieldContent
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
)
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
)
7512 def getLogInfo(self
):
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():
7525 field
= self
.getField(id)
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()
7552 data
["submitter"] = s
.getName()
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:
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
7568 durationHours, durationMinutes - (str)
7572 1: check and raise error in case of problem
7573 2: check and adapt the owner dates
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():
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()
7608 loc
= CustomLocation()
7609 self
.setLocation(loc
)
7610 loc
.setName(data
["locationName"])
7611 loc
.setAddress(data
.get("locationAddress", ""))
7613 self
.setLocation(None)
7615 #same as for the location
7616 if "roomName" in data
:
7617 room
= self
.getOwnRoom()
7621 room
.setName(data
["roomName"])
7622 room
.retrieveFullName(data
.get("locationName", ""))
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')),
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
)
7653 h
= data
.get("durHours", "").strip()
7654 m
= data
.get("durMins", "").strip()
7655 if h
!= "" or m
!= "":
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():
7674 cont
.setKeywords( self
.getKeywords() )
7676 deltaTime
= parent
.getStartDate() - self
.getOwner().getStartDate()
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() :
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() :
7709 if options
.get("access", False) :
7710 cont
.setProtection(self
.getAccessController()._getAccessProtection
())
7711 for u
in self
.getAllowedToAccessList() :
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())
7748 def notifyModification( self
, date
= None, raiseEvent
= True, cleanCache
= True):
7749 self
.setModificationDate(date
)
7752 signals
.event
.contribution_data_changed
.send(self
)
7757 parent
= self
.getParent()
7759 parent
.setModificationDate()
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:
7783 lconf
= self
.getConference().getLocator()
7784 if self
.getSession() is not None:
7785 lconf
["sessionId"] = self
.getSession().getId()
7786 lconf
["contribId"] = self
.getId()
7789 def _setConference( self
, conf
):
7792 def _setId( self
, 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")
7802 self
._setConference
( conf
)
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
)
7815 for mat
in self
.getMaterialList():
7816 self
.removeMaterial(mat
)
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
)
7849 TrashCanManager().remove(self
)
7851 def setId( self
, newId
):
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()
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() == "":
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
):
7884 self
.notifyModification(cleanCache
= False)
7885 if self
.parent
==None:
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
):
7902 def getSession( self
):
7906 except AttributeError:
7908 return self
._session
7910 def setSession(self
,session
):
7911 if self
.getSession()==session
:
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
):
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
):
7956 def setLocation(self
, newLocation
):
7957 oldLocation
= self
.place
7958 self
.place
= newLocation
7959 self
.notifyModification()
7961 def getOwnRoom(self
):
7964 def setRoom(self
, newRoom
):
7967 self
.notifyModification()
7969 def setBoardNumber(self
, newBoardNum
):
7970 self
._boardNumber
=str(newBoardNum
).strip()
7972 def getBoardNumber(self
):
7974 if self
._boardNumber
:
7976 except AttributeError:
7977 self
._boardNumber
=""
7978 return self
._boardNumber
7980 def verifyStartDate(self
, sDate
, check
=2):
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()
7990 owner
= self
.getOwner()
7991 if sDate
< owner
.getStartDate():
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')),\
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():
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')),\
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():
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')),\
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):
8025 1: check and raise error in case of problem
8026 2: check and adapt the owner dates"""
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:
8046 tz
= self
.getConference().getTimezone()
8047 if tz
not in all_timezones
:
8049 return self
.getStartDate().astimezone(timezone(tz
))
8051 def getEndDate(self
):
8052 if self
.getStartDate() is None:
8054 return self
.getStartDate()+self
.getDuration()
8056 def getAdjustedEndDate(self
, tz
=None):
8058 tz
= self
.getConference().getTimezone()
8059 if tz
not in all_timezones
:
8061 if self
.getEndDate():
8062 return self
.getEndDate().astimezone(timezone(tz
))
8065 def getDuration(self
):
8066 return self
.duration
8068 def verifyDuration(self
, check
=2):
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():
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')),\
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):
8095 1: check and raise error in case of problem
8096 2: check and adapt the owner dates"""
8101 self
.duration
=timedelta(hours
=int(hours
),minutes
=int(minutes
))
8103 self
.verifyDuration(check
)
8104 self
.getSchEntry().synchro()
8105 self
.notifyModification()
8107 def _addAuthor(self
, part
):
8113 except AttributeError:
8114 self
._authors
= OOBTree()
8118 except AttributeError:
8119 self
._authorGen
=Counter()
8120 newId
= part
.getId()
8122 newId
= str( self
._authorGen
.newCount() )
8123 self
._authors
[newId
] = part
8124 part
.includeInContribution( self
, newId
)
8126 def _removeAuthor(self
, part
):
8132 except AttributeError:
8133 self
._authors
= OOBTree()
8134 if not self
._authors
.has_key( part
.getId() ):
8136 del self
._authors
[ part
.getId() ]
8137 if not self
.isSpeaker(part
):
8140 def addPrimaryAuthor(self
, part
, index
=None):
8144 if self
._primaryAuthors
:
8146 except AttributeError:
8147 self
._primaryAuthors
= []
8148 self
._addAuthor
( part
)
8149 if index
is not None:
8150 self
._primaryAuthors
.insert(index
, part
)
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):
8161 if self
._primaryAuthors
:
8163 except AttributeError:
8164 self
._primaryAuthors
= []
8165 if part
not in self
._primaryAuthors
:
8167 if self
.getConference() is not None:
8168 self
.getConference().unindexAuthor(part
)
8169 self
._primaryAuthors
.remove( part
)
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
)
8182 if isPendingSubmitter
:
8183 self
.getConference().getPendingQueuesMgr().addPendingSubmitter(pa
, False)
8185 def isPrimaryAuthor(self
, part
):
8189 if self
._primaryAuthors
:
8191 except AttributeError:
8192 self
._primaryAuthors
= []
8193 return part
in self
._primaryAuthors
8195 def isCoAuthor(self
, part
):
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
:
8209 def isCoAuthorByEmail(self
, email
):
8210 for coAuthor
in self
.getCoAuthorList():
8211 if coAuthor
.getEmail() == email
:
8215 def isSpeakerByEmail(self
, email
):
8216 for speaker
in self
.getSpeakerList():
8217 if speaker
.getEmail() == email
:
8221 def changePosPrimaryAuthor(self
, part
, index
):
8225 if self
._primaryAuthors
:
8227 except AttributeError:
8228 self
._primaryAuthors
=[]
8229 if not part
in self
._primaryAuthors
:
8231 self
._primaryAuthors
.remove(part
)
8232 self
._primaryAuthors
.insert(index
,part
)
8233 self
.notifyModification(cleanCache
= False)
8235 def upPrimaryAuthor(self
, part
):
8239 if self
._primaryAuthors
:
8241 except AttributeError:
8242 self
._primaryAuthors
=[]
8244 idx
=self
._primaryAuthors
.index(part
)
8249 self
._primaryAuthors
.remove(part
)
8250 self
._primaryAuthors
.insert(idx
-1,part
)
8251 self
.notifyModification(cleanCache
=False)
8253 def downPrimaryAuthor(self
, part
):
8257 if self
._primaryAuthors
:
8259 except AttributeError:
8260 self
._primaryAuthors
=[]
8262 idx
=self
._primaryAuthors
.index(part
)
8265 if idx
>len(self
._primaryAuthors
):
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
):
8281 if kind
== "prAuthor":
8283 author
= self
.getPrimaryAuthorById(auth
['id'])
8285 result
.append(author
)
8287 author
= self
.getCoAuthorById(auth
['id'])
8289 result
.append(author
)
8291 elif kind
== "coAuthor":
8293 author
= self
.getCoAuthorById(auth
['id'])
8295 result
.append(author
)
8297 author
= self
.getPrimaryAuthorById(auth
['id'])
8299 result
.append(author
)
8302 def getPrimaryAuthorById(self
, authorId
):
8303 for author
in self
.getPrimaryAuthorList():
8304 if authorId
== author
.getId():
8308 def getCoAuthorById(self
, authorId
):
8309 for author
in self
.getCoAuthorList():
8310 if authorId
== author
.getId():
8314 def setPrimaryAuthorList(self
, l
):
8315 self
._primaryAuthors
= l
8316 self
.notifyModification(cleanCache
= False)
8318 def setCoAuthorList(self
, l
):
8320 self
.notifyModification(cleanCache
= False)
8322 def changePosCoAuthor(self
, part
, index
):
8328 except AttributeError:
8330 if not part
in self
._coAuthors
:
8332 self
._coAuthors
.remove(part
)
8333 self
._coAuthors
.insert(index
,part
)
8334 self
.notifyModification(cleanCache
= False)
8336 def upCoAuthor(self
, part
):
8342 except AttributeError:
8345 idx
=self
._coAuthors
.index(part
)
8350 self
._coAuthors
.remove(part
)
8351 self
._coAuthors
.insert(idx
-1,part
)
8352 self
.notifyModification(cleanCache
= False)
8354 def downCoAuthor(self
, part
):
8360 except AttributeError:
8363 idx
=self
._coAuthors
.index(part
)
8366 if idx
>len(self
._coAuthors
):
8368 self
._coAuthors
.remove(part
)
8369 self
._coAuthors
.insert(idx
+1,part
)
8370 self
.notifyModification(cleanCache
= False)
8372 def getPrimaryAuthorList(self
):
8376 if self
._primaryAuthors
:
8378 except AttributeError:
8379 self
._primaryAuthors
= []
8380 return self
._primaryAuthors
8382 getPrimaryAuthorsList
= getPrimaryAuthorList
8384 def getAuthorList(self
):
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
8399 return self
.getPrimaryAuthorList() + self
.getCoAuthorList()
8401 def addCoAuthor(self
, part
, index
=None):
8407 except AttributeError:
8408 self
._coAuthors
= []
8409 self
._addAuthor
( part
)
8410 if index
is not None:
8411 self
._coAuthors
.insert(index
, part
)
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):
8424 except AttributeError:
8425 self
._coAuthors
= []
8426 if part
not in self
._coAuthors
:
8428 if self
.getConference() is not None:
8429 self
.getConference().unindexAuthor(part
)
8430 self
._coAuthors
.remove( part
)
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
)
8443 if isPendingSubmitter
:
8444 self
.getConference().getPendingQueuesMgr().addPendingSubmitter(ca
, False)
8446 def getCoAuthorList(self
):
8452 except AttributeError:
8453 self
._coAuthors
= []
8454 return self
._coAuthors
8456 def getAuthorById(self
, authorId
):
8462 except AttributeError:
8463 self
._authors
= OOBTree()
8464 return self
._authors
.get( authorId
.strip(), None )
8466 def isAuthor(self
, part
):
8472 except AttributeError:
8473 self
._authors
= OOBTree()
8474 return self
._authors
.has_key( part
.getId() )
8476 def getSpeakerById(self
, authorId
):
8482 except AttributeError:
8484 for spk
in self
._speakers
:
8485 if spk
.getId() == authorId
:
8489 def changePosSpeaker(self
, part
, index
):
8495 except AttributeError:
8497 if not part
in self
._speakers
:
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
8511 except AttributeError:
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
)
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
8531 except AttributeError:
8536 except AttributeError:
8537 self
._authorGen
=Counter()
8538 self
._speakers
.append( part
)
8539 newId
= part
.getId()
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
):
8553 except AttributeError:
8555 if part
not in self
._speakers
:
8557 self
._speakers
.remove( part
)
8558 if self
.getConference() is not None:
8559 self
.getConference().unindexSpeaker(part
)
8560 if part
not in self
.getAuthorList():
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
)
8570 if isPendingSubmitter
:
8571 self
.getConference().getPendingQueuesMgr().addPendingSubmitter(spk
, False)
8573 def isSpeaker(self
, part
):
8579 except AttributeError:
8581 return part
in self
._speakers
8583 def getSpeakerList(self
):
8589 except AttributeError:
8591 return self
._speakers
8593 def getSpeakerText(self
):
8596 if self
.speakerText
:
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():
8624 if self
.getDomainList():
8626 if self
.getAccessProtectionLevel() == -1:
8629 return self
.getOwner().hasAnyProtection()
8633 def hasProtectedOwner(self
):
8634 if self
.getOwner() != None:
8635 return self
.getOwner().isProtected()
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
:
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
):
8667 ################################################################################################
8668 for sc
in self
.getSubContributionList():
8669 if sc
.canView( aw
):
8673 def isAllowedToAccess( self
, user
):
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
8684 if has_request_context() and self
.__ac
.isHarvesterIP(request
.remote_addr
):
8686 #####################################################
8688 if self
.canModify(aw
):
8691 if not self
.canIPAccess(request
.remote_addr
) and not self
.isAllowedToAccess(aw
.getUser()):
8693 if not self
.isProtected():
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():
8737 self
.materials
[mat
.getId()].setOwner(None)
8738 del self
.materials
[ mat
.getId() ]
8739 self
.notifyModification()
8740 elif mat
.getId().lower() == 'paper':
8742 elif mat
.getId().lower() == 'slides':
8744 elif mat
.getId().lower() == 'video':
8746 elif mat
.getId().lower() == 'poster':
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
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
]
8778 def getMaterialList( self
):
8779 return self
.materials
.values()
8781 def getAllMaterialList(self
, sort
=True):
8782 l
= self
.getMaterialList()
8784 l
.append( self
.getPaper() )
8785 if self
.getSlides():
8786 l
.append( self
.getSlides() )
8788 l
.append( self
.getVideo() )
8789 if self
.getPoster():
8790 l
.append( self
.getPoster() )
8792 l
.sort(lambda x
,y
: cmp(x
.getTitle(),y
.getTitle()))
8795 def getAllViewableMaterialList( self
, aw
=None ):
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
)
8807 def addSubContribution(self
, newSubCont
, subcontrib_id
=None):
8808 if subcontrib_id
is None:
8809 newSubCont
.setId(str(self
.__subContGenerator
.newCount()))
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
:
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
:
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"))
8865 self
.paper
.setOwner( self
)
8866 self
.notifyModification()
8868 def removePaper( self
):
8869 if self
.paper
is None:
8872 self
.paper
.setOwner(None)
8874 self
.notifyModification()
8876 def recoverPaper(self
, p
):
8880 def getPaper( self
):
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:
8893 self
.slides
.delete()
8894 self
.slides
.setOwner( None )
8896 self
.notifyModification()
8898 def recoverSlides(self
, s
):
8902 def getSlides( self
):
8905 def setVideo( self
, newVideo
):
8906 if self
.getVideo() != None:
8907 raise MaKaCError( _("the video for this contribution has already been set"))
8909 self
.video
.setOwner( self
)
8910 self
.notifyModification()
8912 def removeVideo( self
):
8913 if self
.getVideo() is None:
8916 self
.video
.setOwner(None)
8918 self
.notifyModification()
8920 def recoverVideo(self
, v
):
8924 def getVideo( self
):
8928 except AttributeError:
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:
8942 self
.poster
.delete()
8943 self
.poster
.setOwner(None)
8945 self
.notifyModification()
8947 def recoverPoster(self
, p
):
8951 def getPoster( self
):
8955 except AttributeError:
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:
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
)
8978 def getReviewing( self
):
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
):
9002 except AttributeError:
9006 def setTrack( self
, newTrack
):
9007 currentTrack
= self
.getTrack()
9008 if newTrack
== currentTrack
:
9011 currentTrack
.removeContribution( self
)
9012 self
._track
= newTrack
9014 self
._track
.addContribution( self
)
9016 def removeTrack(self
, track
):
9017 if track
== self
._track
:
9020 def setType( self
, newType
):
9021 self
._type
= newType
9023 def getType( self
):
9027 except AttributeError:
9031 def getModificationDate( self
):
9032 """Returns the date in which the contribution was last modified"""
9034 return self
._modificationDS
9036 if self
.getConference():
9037 self
._modificationDS
= self
.getConference().getModificationDate()
9039 self
._modificationDS
= nowutc()
9040 return self
._modificationDS
9042 def getCurrentStatus(self
):
9046 except AttributeError:
9047 self
._status
=ContribStatusNotSch(self
)
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
)
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):
9084 if self
._submitters
:
9086 except AttributeError:
9087 self
._submitters
=[] #create the attribute
9088 self
.notifyModification(raiseEvent
= False)
9090 return [s
for s
in self
._submitters
if not isinstance(s
, GroupWrapper
)]
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())
9111 def revokeSubmissionEmail(self
, email
):
9112 if email
in self
.getSubmitterEmailList():
9113 self
.getSubmitterEmailList().remove(email
)
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
):
9122 results
=ah
.match({"email":sb
.getEmail()}, exact
=1, searchInAuthenticators
=False)
9124 results
=ah
.match({"email":sb
.getEmail()}, exact
=1)
9127 if i
.hasEmail(sb
.getEmail()):
9130 if r
and r
.isActivated():
9131 self
._grantSubmission
(r
)
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
)
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
):
9158 results
= ah
.match({"email": sb
.getEmail()}, exact
=1, searchInAuthenticators
=False)
9161 if i
.hasEmail(sb
.getEmail()):
9165 self
._revokeSubmission
(r
)
9167 self
.revokeSubmissionEmail(sb
.getEmail())
9169 self
._revokeSubmission
(sb
)
9171 def revokeAllSubmitters(self
):
9172 self
._submitters
= []
9173 self
.notifyModification(raiseEvent
= False)
9175 def getSubmitterEmailList(self
):
9177 return self
._submittersEmail
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)
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
):
9204 def getAccessController(self
):
9207 def getReportNumberHolder(self
):
9209 if self
._reportNumberHolder
:
9211 except AttributeError, e
:
9212 self
._reportNumberHolder
=ReportNumberHolder(self
)
9213 return self
._reportNumberHolder
9215 def setReportNumberHolder(self
, rnh
):
9216 self
._reportNumberHolder
=rnh
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()
9226 return maxDatetime()
9230 if self
.getSession() is not None:
9231 res
=self
.getSession().getColor()
9234 def getTextColor(self
):
9236 if self
.getSession() is not None:
9237 res
=self
.getSession().getTextColor()
9241 class AcceptedContribution(Contribution
):
9242 """This class represents a contribution which has been created from an
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
)
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():
9279 def getAbstract(self
):
9280 return self
._abstract
9282 def setAbstract(self
, abs):
9283 self
._abstract
= abs
9285 def getSubmitterList(self
, no_groups
=False):
9287 if self
._submitters
:
9289 except AttributeError:
9290 self
._submitters
= [] # create the attribute
9291 self
._grantSubmission
(self
.getAbstract().getSubmitter().getUser())
9293 return [s
for s
in self
._submitters
if not isinstance(s
, GroupWrapper
)]
9295 return self
._submitters
9298 """deletes a contribution and all of their subitems
9300 abs = self
.getAbstract()
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
)
9321 def clone(self
, contribution
, responsible
):
9322 cs
= ContribStatus(contribution
, responsible
)
9323 cs
.setDate(self
.getDate())
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
9341 def setDate(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())
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())
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())
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())
9404 class SubContribParticipation(Persistent
, Fossilizable
):
9406 fossilizes(ISubContribParticipationFossil
, ISubContribParticipationFullFossil
)
9408 def __init__( self
):
9409 self
._subContrib
= None
9411 self
._firstName
= ""
9414 self
._affiliation
= ""
9420 def getConference(self
):
9421 if self
._subContrib
is not None:
9422 return self
._subContrib
.getConference()
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
):
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()
9453 part
= SubContribParticipation()
9454 part
.setValues(self
.getValues())
9457 def setDataFromAvatar(self
,av
):
9458 # av is an Avatar object.
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.
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.
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():
9502 self
._subContrib
= subcontrib
9506 self
._subContrib
= None
9507 TrashCanManager().add(self
)
9510 TrashCanManager().remove(self
)
9512 @Updates ('MaKaC.conference.SubContribParticipation', 'id')
9513 def setId(self
, newId
):
9519 def getSubContrib( self
):
9520 return self
._subContrib
9522 def getContribution( self
):
9523 if self
._subContrib
is not None:
9524 return self
._subContrib
.getContribution()
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
)
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
):
9546 if tmp
==self
._firstName
:
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
):
9562 if tmp
==self
._surName
:
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
):
9578 if tmp
==self
._email
:
9581 self
._email
=newMail
.strip()
9583 self
._notifyModification
()
9585 def getEmail( self
):
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
):
9612 @Updates ('MaKaC.conference.SubContribParticipation', 'title')
9613 def setTitle( self
, newTitle
):
9614 self
._title
= newTitle
.strip()
9615 self
._notifyModification
()
9617 def getTitle( self
):
9620 def setFax( self
, newFax
):
9621 self
._fax
= newFax
.strip()
9622 self
._notifyModification
()
9628 except AttributeError:
9632 def getFullName( self
):
9633 res
= self
.getFullNameNoTitle()
9634 if self
.getTitle() != "":
9635 res
= "%s %s"%( self
.getTitle(), res
)
9638 def getFullNameNoTitle(self
):
9639 res
= safe_upper(self
.getFamilyName())
9640 if self
.getFirstName():
9642 res
= "%s, %s" % (res
, self
.getFirstName())
9644 res
= self
.getFirstName()
9647 def getAbrName(self
):
9648 res
= self
.getFamilyName()
9649 if self
.getFirstName():
9652 res
= "%s%s." % (res
, safe_upper(self
.getFirstName()[0]))
9655 def getDirectFullName(self
):
9656 res
= self
.getDirectFullNameNoTitle()
9658 res
= "%s %s" % (self
.getTitle(), 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
):
9676 self
.description
= ""
9677 self
.__schEntry
= None
9678 self
.duration
= timedelta( minutes
=15 )
9680 self
.speakerText
= ""
9683 self
.__materialGenerator
= Counter() # Provides material unique
9684 # identifiers whithin the current
9685 self
.poster
= None # contribution
9691 self
._authorGen
= Counter()
9697 parent_id
= self
.parent
.getId()
9698 event_id
= self
.getConference().getId() if self
.getConference() else None
9702 return '<SubContribution({}, {}, {}.{})>'.format(self
.getId(), self
.getTitle(), event_id
, parent_id
)
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
):
9718 return self
._keywords
9723 def setKeywords(self
, keywords
):
9724 self
._keywords
= keywords
9726 def getLogInfo(self
):
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()
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()
9778 def notifyModification(self
, raiseEvent
= True):
9779 parent
= self
.getParent()
9781 parent
.setModificationDate()
9783 signals
.event
.subcontribution_data_changed
.send(self
)
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()
9799 def setId( self
, newId
):
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() == "":
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:
9834 def getParent( self
):
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
):
9852 def getDuration( self
):
9853 return self
.duration
9855 def setDuration( self
, hours
, minutes
=0, dur
=0 ):
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:
9878 def newSpeaker( self
, spk
):
9881 self
.speakers
.append( spk
)
9885 except AttributeError:
9886 self
._authorGen
=Counter()
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
:
9900 self
.speakers
.remove( spk
)
9901 if self
.getConference() is not None:
9902 self
.getConference().unindexSpeaker(spk
)
9904 self
.notifyModification()
9906 def recoverSpeaker(self
, spk
):
9907 self
.newSpeaker(spk
)
9910 def isSpeaker( self
, spk
):
9913 return spk
in self
._speakers
9915 def getSpeakerList ( self
):
9918 return self
.speakers
9920 def getSpeakerText( self
):
9923 if self
.speakerText
:
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()) )
9936 # There is no _order attribute in this class -
9937 # the methods below are either obsolate or the feature has not been implemented
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()
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
):
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.
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():
10021 self
.materials
[mat
.getId()].setOwner(None)
10022 del self
.materials
[ mat
.getId() ]
10023 self
.notifyModification()
10024 elif mat
.getId().lower() == 'paper':
10026 self
.notifyModification()
10027 elif mat
.getId().lower() == 'slides':
10028 self
.removeSlides()
10029 self
.notifyModification()
10030 elif mat
.getId().lower() == 'video':
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
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
]
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() )
10078 l
.sort(lambda x
,y
: cmp(x
.getTitle(),y
.getTitle()))
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:
10091 self
.paper
.delete()
10092 self
.paper
.setOwner(None)
10094 self
.notifyModification()
10096 def recoverPaper(self
, p
):
10100 def getPaper( self
):
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:
10113 self
.slides
.delete()
10114 self
.slides
.setOwner( None )
10116 self
.notifyModification()
10118 def recoverSlides(self
, s
):
10122 def getSlides( self
):
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:
10135 self
.video
.delete()
10136 self
.video
.setOwner(None)
10138 self
.notifyModification()
10140 def recoverVideo(self
, v
):
10144 def getVideo( self
):
10148 except AttributeError:
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:
10162 self
.poster
.delete()
10163 self
.poster
.setOwner(None)
10165 self
.notifyModification()
10167 def recoverPoster(self
, p
):
10171 def getPoster( self
):
10175 except AttributeError:
10179 def getMasterSchedule( self
):
10180 return self
.getOwner().getSchedule()
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
)
10190 self
.removeSlides()
10192 self
.removePoster()
10193 TrashCanManager().add(self
)
10198 TrashCanManager().remove(self
)
10200 def getReportNumberHolder(self
):
10202 if self
._reportNumberHolder
:
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.
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
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()
10239 self
.description
= ""
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()))
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:
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
):
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()
10295 newres
= r
.clone(mat
)
10296 mat
.addResource(newres
)
10298 mat
.setMainResource(self
.getMainResource())
10302 def notifyModification( self
):
10303 parent
= self
.getOwner()
10305 parent
.notifyModification(raiseEvent
= False)
10306 self
._p
_changed
= 1
10308 def getLocator( self
):
10309 if self
.owner
== None:
10311 lconf
= self
.owner
.getLocator()
10312 lconf
["materialId"] = self
.getId()
10315 def setId( self
, newId
):
10316 self
.id = str(newId
).strip()
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
):
10332 def getCategory( self
):
10333 if isinstance(self
.getOwner(), Category
):
10334 return self
.getOwner()
10337 def getConference( self
):
10338 owner
= self
.getOwner()
10339 if owner
is None or isinstance(owner
, Category
):
10341 elif isinstance(owner
, Conference
):
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()
10355 def getContribution( self
):
10356 if self
.getSubContribution():
10357 return self
.getSubContribution().getContribution()
10358 if isinstance(self
.getOwner(), Contribution
):
10359 return self
.getOwner()
10362 def getSubContribution( self
):
10363 if isinstance(self
.getOwner(), SubContribution
):
10364 return self
.getOwner()
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
):
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
):
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
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":
10424 elif refereeJudgement
.getJudgement() == "Reject":
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
10432 else: #author has not submitted
10434 else: #material is not reviewable
10436 else: #material does not belong to a contribution
10439 def _getRepository( self
):
10440 dbRoot
= DBMgr
.getInstance().getDBConnection().root()
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
10449 def hasFile( self
, name
):
10450 for f
in self
.getResourceList():
10451 if f
.getName() == name
:
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()
10466 list.sort(utils
.sortFilesByName
)
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() ]
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
10488 self
.notifyModification()
10490 def getMainResource(self
):
10492 if self
._mainResource
:
10494 except AttributeError:
10495 self
._mainResource
= None
10496 return self
._mainResource
10498 def setMainResource(self
, mr
):
10499 self
._mainResource
= mr
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
)
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()
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
):
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
10598 if has_request_context() and self
.__ac
.isHarvesterIP(request
.remote_addr
):
10600 #####################################################
10602 # Managers have always access
10603 if self
.canModify(aw
):
10606 canUserAccess
= self
.isAllowedToAccess(aw
.getUser())
10607 canIPAccess
= self
.canIPAccess(request
.remote_addr
)
10608 if not self
.isProtected():
10609 return canUserAccess
or canIPAccess
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
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
)
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
):
10674 def isBuiltin(self
):
10677 class BuiltinMaterial(Material
):
10679 Non-customizable material types
10681 def isBuiltin(self
):
10685 class Reviewing(BuiltinMaterial
):
10687 def __init__( self
, materialData
= None ):
10688 Material
.__init
__( self
, materialData
)
10689 self
.id = "reviewing"
10691 def setId( self
, newId
):
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
)
10705 def setId( self
, newId
):
10710 class Slides(BuiltinMaterial
):
10712 def __init__( self
, materialData
= None ):
10713 Material
.__init
__( self
, materialData
)
10716 def setId( self
, newId
):
10721 class Video(BuiltinMaterial
):
10723 def __init__( self
, materialData
= None ):
10724 Material
.__init
__( self
, materialData
)
10727 def setId( self
, newId
):
10730 class Poster(BuiltinMaterial
):
10732 def __init__( self
, materialData
= None ):
10733 Material
.__init
__( self
, materialData
)
10736 def setId( self
, newId
):
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.
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
10753 description - (string) detailed and varied information about the
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"
10764 self
.description
= ""
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())
10785 res
.notifyModification()
10786 res
.setId(self
.getId())
10789 res
.setProtection(self
.getAccessController()._getAccessProtection
())
10790 #res.__ac = self.getAccessController()
10794 def notifyModification( self
):
10795 parent
= self
.getOwner()
10797 parent
.setModificationDate()
10798 self
._p
_changed
= 1
10800 def getLocator( self
):
10801 if self
._owner
== None:
10803 lconf
= self
._owner
.getLocator()
10804 lconf
["resId"] = self
.getId()
10807 def setId( self
, newId
):
10808 self
.id = newId
.strip()
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
):
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()
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
):
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
):
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"""
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')
10884 TrashCanManager().add(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()
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
10939 if has_request_context() and self
.__ac
.isHarvesterIP(request
.remote_addr
):
10941 #####################################################
10943 # Managers have always access
10944 if self
.canModify(aw
):
10947 if not self
.canIPAccess(request
.remote_addr
) and not self
.canUserModify(aw
.getUser()) and \
10948 not self
.isAllowedToAccess(aw
.getUser()):
10950 if not self
.isProtected():
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
):
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
):
10999 def getAccessKey(self
):
11000 if self
.getOwner() is not None:
11001 return self
.getOwner().getAccessKey()
11004 def canKeyAccess(self
, aw
):
11005 accessKey
= self
.getAccessKey()
11006 key
= session
.get('accessKeys', {}).get(self
.getUniqueId())
11009 elif accessKey
and key
== accessKey
:
11011 elif not accessKey
and key
== self
.getConference().getAccessKey():
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
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'
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.
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
)
11057 def clone( self
, conf
):
11058 link
= Resource
.clone(self
, conf
)
11059 link
.setURL(self
.getURL())
11062 @Updates ('MaKaC.conference.Link', 'url')
11063 def setURL( self
, newURL
):
11064 self
.url
= newURL
.strip()
11065 self
.notifyModification()
11067 def getURL( self
):
11070 def getLocator(self
):
11071 locator
= Resource
.getLocator(self
)
11072 locator
['fileExt'] = 'link'
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.
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
)
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())
11114 def getLocator(self
):
11115 locator
= Resource
.getLocator(self
)
11117 locator
['fileExt'] = (self
.fileType
.lower() or
11118 os
.path
.splitext(self
.fileName
)[1].lower().lstrip('.') or None)
11120 locator
['fileExt'] = 'bin' # no extension => use a dummy
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
11137 if newFileName
.count("/"):
11139 newFileName
= newFileName
.split("/")[-1]
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
)
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"))
11205 raise Exception( _("Destination repository not set"))
11206 if self
.filePath
== "":
11207 return _("Nothing to archive")
11208 repository
.storeFile( self
, forcedFileId
= forcedFileId
)
11210 self
.notifyModification()
11212 def unArchive(self
):
11214 self
.__repository
= None
11215 self
.__archivedId
= ""
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() )
11230 self
.__repository
.retireFile( self
)
11231 except AttributeError, e
:
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.
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:
11271 if not self
._idx
.has_key( av
.getId() ):
11274 l
= self
._idx
[av
.getId()]
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:
11284 l
= self
._idx
.get( av
.getId(), [] )
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"
11300 self
.description
= ""
11301 self
.subTracks
= {}
11302 self
.__SubTrackGenerator
= Counter()
11303 self
._abstracts
= OOBTree()
11304 self
._coordinators
= []
11305 self
._contributions
= OOBTree()
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
):
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())
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
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
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
)
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()
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:
11394 lconf
= self
.conference
.getLocator()
11395 lconf
["trackId"] = self
.getId()
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
)
11413 def setTitle( self
, newTitle
):
11414 self
.title
= newTitle
11415 self
.notifyModification()
11417 def getTitle( self
):
11420 def setDescription(self
, newDescription
):
11421 self
.description
= newDescription
11422 self
.notifyModification()
11424 def getDescription(self
):
11425 return self
.description
11431 except AttributeError:
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():
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 )
11462 self
.notifyModification()
11464 def recoverSubTrack(self
, subTrack
):
11465 self
.addSubTrack(subTrack
)
11468 def newSubTrack( self
):
11470 self
.addSubTrack( st
)
11473 def getSubTrackById( self
, id ):
11474 if self
.subTracks
.has_key( id ):
11475 return self
.subTracks
[ id ]
11478 def getSubTrackList( self
):
11479 return self
.subTracks
.values()
11481 def getAbstractList( self
):
11485 if self
._abstracts
:
11487 except AttributeError:
11488 self
._abstracts
= OOBTree()
11489 return self
._abstracts
.values()
11491 def getAbstractById( self
, id ):
11493 if self
._abstracts
:
11495 except AttributeError:
11496 self
._abstracts
= OOBTree()
11497 return self
._abstracts
.get(str(id).strip())
11499 def hasAbstract( self
, abstract
):
11503 if self
._abstracts
:
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
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
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.
11533 av -- (AvatarUserWrapper) the user to which
11534 coordination privileges must be granted.
11538 if self
._coordinators
:
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.
11554 av -- (AvatarUserWrapper) user for which coordination privileges
11558 if self
._coordinators
:
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.
11574 av -- (AvatarUserWrapper) user to be checke
11576 Return value: (boolean)
11579 if self
._coordinators
:
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)
11592 if self
._coordinators
:
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.
11605 aw -- (MaKaC.accessControl.AccessWrapper) User access
11606 information for which the coordination privileges must be
11609 Return value: (boolean)
11611 return self
.isCoordinator( aw
.getUser() ) or self
.canModify( aw
)
11613 def addContribution( self
, newContrib
):
11617 if self
._contributions
:
11619 except AttributeError:
11620 self
._contributions
= OOBTree()
11621 if self
._contributions
.has_key( newContrib
.getId() ):
11623 self
._contributions
[ newContrib
.getId() ] = newContrib
11624 newContrib
.setTrack( self
)
11626 def getModifKey( self
):
11627 return self
.getConference().getModifKey()
11629 def removeContribution( self
, contrib
):
11633 if self
._contributions
:
11635 except AttributeError:
11636 self
._contributions
= OOBTree()
11637 if not self
._contributions
.has_key( contrib
.getId() ):
11639 del self
._contributions
[ contrib
.getId() ]
11640 contrib
.setTrack( None )
11642 def hasContribution( self
, contrib
):
11644 if self
._contributions
:
11646 except AttributeError:
11647 self
._contributions
= OOBTree()
11648 return self
._contributions
.has_key( contrib
.getId() )
11650 def getContributionList(self
):
11652 if self
._contributions
:
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
):
11666 self
.id = "not assigned"
11668 self
.description
= ""
11672 sub
.setDescription(self
.getDescription())
11673 sub
.setTitle(self
.getTitle())
11679 TrashCanManager().add(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()
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:
11702 lconf
= self
.track
.getLocator()
11703 lconf
["subTrackId"] = self
.getId()
11706 def setTrack(self
, track
):
11711 def getTrack( self
):
11714 def getOwner( self
):
11715 return self
.getTrack()
11717 def setId( self
, newId
):
11718 self
.id = str(newId
)
11723 def setTitle( self
, newTitle
):
11724 self
.title
= newTitle
11725 self
.notifyModification()
11727 def getTitle( self
):
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
):
11743 self
._description
= description
11744 self
._conference
= conference
11749 def setId(self
, id):
11755 def setName(self
, 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:
11773 lconf
= self
._conference
.getLocator()
11774 lconf
["contribTypeId"] = self
.getId()
11777 def canModify(self
, aw_or_user
):
11778 return self
._conference
.canModify(aw_or_user
)
11781 self
.setConference(None)
11782 TrashCanManager().add(self
)
11785 TrashCanManager().remove(self
)
11787 def clone(self
, conference
):
11788 type = ContributionType(self
.getName(), self
.getDescription(),conference
)
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
):
11808 self
._showIds
= False
11809 self
._sortBy
= "number"
11810 self
._correspondingAuthor
= "submitter"
11811 self
._modificationDS
= nowutc()
11812 self
._cache
= False
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
()
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
()
11853 def getCorrespondingAuthorTypes():
11854 return BOAConfig
.correspondingAuthorTypes
11856 def isCacheEnabled(self
):
11857 if not hasattr(self
, '_cache'):
11858 self
._cache
= False
11861 def setCache(self
, value
):
11862 self
._cache
= value
;
11864 def _notifyModification(self
):
11865 self
._modificationDS
= nowutc()
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"""
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((
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
))
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):
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