class GroupWrapper(Fossilizable): """Group-like wrapper class that holds a DB-stored (or remote) group.""" fossilizes(IGroupFossil) def __init__(self, group_id): self.id = to_unicode(group_id).encode('utf-8') @property def group(self): """Return the underlying GroupProxy. :rtype: indico.modules.groups.core.GroupProxy """ raise NotImplementedError def getId(self): return self.id def getName(self): raise NotImplementedError def getEmail(self): return '' @return_ascii def __repr__(self): return u'<{} {}: {}>'.format(type(self).__name__, self.id, self.group)
class TaskOccurrence(TimedEvent): """ Wraps around a PeriodicTask object, and represents an occurrence of this task """ fossilizes(ITaskOccurrenceFossil) def __init__(self, task): self._task = task self._startedOn = task.getStartedOn() self._endedOn = task.getEndedOn() self._id = None def getId(self): return self._id def getUniqueId(self): return "%s:%s" % (self._task.getUniqueId(), self._id) def setId(self, occId): self._id = occId def getStartedOn(self): return self._startedOn def getEndedOn(self): return self._endedOn def getTask(self): return self._task
class Consumer(Persistent): fossilizes(IConsumerFossil) def __init__(self, key, secret, name, trusted=False, blocked=False): self._key = key self._secret = secret self._name = name self._trusted = trusted self._blocked = blocked def getId(self): return self._key def getSecret(self): return self._secret def getName(self): return self._name def isTrusted(self): return self._trusted def setTrusted(self, trusted): self._trusted = trusted def isBlocked(self): return self._blocked def setBlocked(self, blocked): self._blocked = blocked
class HTTPAPIResult(Fossilizable): fossilizes(IHTTPAPIResultFossil) def __init__(self, results, path='', query='', ts=None, complete=True, extra=None): if ts is None: ts = int(time.time()) self._results = results self._path = path self._query = query self._ts = ts self._complete = complete self._extra = extra or {} def getTS(self): return self._ts def getURL(self): prefix = config.BASE_URL if self._query: return prefix + self._path + '?' + self._query return prefix + self._path def getCount(self): return len(self._results) def getResults(self): return self._results def getComplete(self): return self._complete def getAdditionalInfo(self): return self._extra
class AvatarProvisionalWrapper(Fossilizable): """ Wraps provisional data for users that are not in the DB yet """ fossilizes(IAvatarFossil, IAvatarMinimalFossil) def __init__(self, identity_info): self.identity_info = identity_info self.data = identity_info.data def getId(self): return u"{}:{}".format(self.identity_info.provider.name, self.identity_info.identifier) id = property(getId) @encode_utf8 def getEmail(self): return self.data['email'] def getEmails(self): return [self.data['email']] @encode_utf8 def getFirstName(self): return self.data['first_name'] @encode_utf8 def getFamilyName(self): return self.data['last_name'] def getStraightFullName(self): return '{first_name} {last_name}'.format(**self.data.to_dict()) def getTitle(self): return u'' @encode_utf8 def getTelephone(self): return self.data['phone'] @encode_utf8 def getOrganisation(self): return self.data['affiliation'] def getFax(self): return None def getAddress(self): return u'' @return_ascii def __repr__(self): return u'<AvatarProvisionalWrapper {}: {} ({first_name} {last_name})>'.format( self.identity_info.provider.name, self.identity_info.identifier, **self.data.to_dict())
class EmailPrincipal(Fossilizable): """Wrapper for email principals :param email: The email address. """ principal_type = PrincipalType.email is_network = False is_group = False is_single_person = True is_event_role = False is_category_role = False is_registration_form = False principal_order = 0 fossilizes(IEmailPrincipalFossil) def __init__(self, email): self.email = email.lower() @property def name(self): return self.email @property def as_legacy(self): return self @property def user(self): from indico.modules.users import User return User.query.filter(~User.is_deleted, User.all_emails == self.email).first() @property def identifier(self): return 'Email:{}'.format(self.email) def __eq__(self, other): return isinstance(other, EmailPrincipal) and self.email == other.email def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.email) def __contains__(self, user): if not user: return False return self.email in user.all_emails @return_ascii def __repr__(self): return format_repr(self, 'email')
class HTTPAPIError(Exception, Fossilizable): fossilizes(IHTTPAPIErrorFossil) def __init__(self, message, code=None): self.message = message self.code = code def getMessage(self): return self.message def getCode(self): return self.code
class TaskOccurrence(TimedEvent): """ Wraps around a PeriodicTask object, and represents an occurrence of this task """ fossilizes(ITaskOccurrenceFossil) def __init__(self, task): self._task = task self._startedOn = task.getStartedOn() self._endedOn = task.getEndedOn() self._id = None def __cmp__(self, obj): if obj is None: return 1 elif isinstance(obj, BaseTask): task_cmp = cmp(self.getTask(), obj) if task_cmp == 0: return 1 else: return task_cmp elif not isinstance(obj, TaskOccurrence) or (self._id == obj._id and self._id is None): return cmp(hash(self), hash(obj)) occ_task_cmp = cmp(self.getTask(), obj.getTask()) if occ_task_cmp == 0: return cmp(self.getId(), obj.getId()) else: return occ_task_cmp def getId(self): return self._id def getUniqueId(self): return "%s:%s" % (self._task.getUniqueId(), self._id) def setId(self, occId): self._id = occId def getStartedOn(self): return self._startedOn def getEndedOn(self): return self._endedOn def getTask(self): return self._task
class GroupWrapper(Fossilizable): """Group-like wrapper class that holds a DB-stored (or remote) group.""" fossilizes(IGroupFossil) def __init__(self, group_id): self.id = to_unicode(group_id).encode('utf-8') @property def group(self): """Returns the underlying GroupProxy :rtype: indico.modules.groups.core.GroupProxy """ raise NotImplementedError def getId(self): return self.id def getName(self): raise NotImplementedError def getFullName(self): return self.getName() def getEmail(self): return '' def exists(self): return self.group.group is not None def __eq__(self, other): if not hasattr(other, 'group') or not isinstance( other.group, GroupProxy): return False return self.group == other.group def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.group) @return_ascii def __repr__(self): return u'<{} {}: {}>'.format(type(self).__name__, self.id, self.group)
class EmailPrincipal(Fossilizable): """Wrapper for email principals :param email: The email address. """ principal_type = PrincipalType.email is_group = False fossilizes(IEmailPrincipalFossil) def __init__(self, email): self.email = email.lower() @property def name(self): return self.email @property def as_legacy(self): return self @property def as_new(self): return self @property def user(self): from indico.modules.users import User return User.find_first(~User.is_deleted, User.all_emails.contains(self.email)) def __eq__(self, other): return isinstance(other, EmailPrincipal) and self.email == other.email def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.email) def __contains__(self, user): return self.email in user.all_emails @return_ascii def __repr__(self): return format_repr(self, 'email')
class SyncAgent(Fossilizable, Persistent): """ Represents an "agent" (service) """ fossilizes(IAgentFossil) _extraOptions = {} # TODO: Subclass into PushSyncAgent(task)/PullSyncAgent? def __init__(self, aid, name, description, updateTime, access=None): self._id = aid self._name = name self._description = description self._updateTime = updateTime self._manager = None self._active = False self._recording = False self._access = access def record_str(self, (obj, objId, status)): """
class Wrapper(Persistent): """ Very simple wrapper around Attachment/AttachmentFolder that allows them to be stored in obj.nonInheritingChildren """ fossilizes(IWrapperFossil) def __init__(self, obj): self.id = obj.id def getId(self): return self.id @property def title(self): return self.as_new.title @property def as_new(self): return self.new_class.get(self.id) def __eq__(self, other): if self.__class__ is other.__class__: return self.id == other.id else: return False def __ne__(self, other): return not(self == other) def __hash__(self): return hash((self.__class__.__name__, self.id)) def getAccessController(self): return AccessControllerAdapter(self)
class GroupWrapper(Persistent, Fossilizable): """Group-like wrapper class that holds a DB-stored (or remote) group.""" fossilizes(IGroupFossil) def __init__(self, group_id): self.id = to_unicode(group_id).encode('utf-8') @property def group(self): """Returns the underlying GroupProxy :rtype: indico.modules.groups.core.GroupProxy """ raise NotImplementedError def getId(self): return self.id def getName(self): raise NotImplementedError def getFullName(self): return self.getName() def getDescription(self): return '' def setDescription(self, value): pass def getEmail(self): return '' def isObsolete(self): return False def containsUser(self, avatar): if self.group is None or avatar is None: return False return avatar.user in self.group def getMemberList(self): return sorted([x.as_avatar for x in self.group.get_members()], key=attrgetter('user.last_name')) def canModify(self, aw_or_user): if hasattr(aw_or_user, 'getUser'): aw_or_user = aw_or_user.getUser() return self.canUserModify(aw_or_user) def canUserModify(self, avatar): return avatar.user.is_admin def getLocator(self): return Locator(groupId=self.id) def exists(self): return self.group.group is not None @property def as_new(self): return self.group def __eq__(self, other): if not hasattr(other, 'group') or not isinstance( other.group, GroupProxy): return False return self.group == other.group def __ne__(self, other): return not (self == other) def __hash__(self): return hash(self.group) @return_ascii def __repr__(self): return u'<{} {}: {}>'.format(type(self).__name__, self.id, self.group)
class SupportInfo(Persistent): """ Stores support-related information for events """ fossilizes(ISupportInfoFossil) def __init__(self, owner, caption="", email="", telephone=""): self._owner = owner self._caption = caption self._email = email self._telephone = telephone def getOwner(self): return self._owner def getCaption(self): return self._caption def setCaption(self, caption): self._caption = caption def getEmail(self, returnNoReply=False, caption=False): """ Returns the support email address associated with the conference :param returnNoReply: Return no-reply address in case there's no support e-mail (default True) :type returnNoReply: bool """ if self._email.strip() == "" and returnNoReply: # In case there's no conference support e-mail, return the no-reply # address, and the 'global' support e-mail if there isn't one return Config.getInstance().getNoReplyEmail() else: if caption and self._caption: return '"%s" <%s>' % (self._caption, self._email) else: return self._email def setEmail(self, email): self._email = email.strip() def hasEmail(self): return self._email != "" and self._email != None def getTelephone(self): return self._telephone def setTelephone(self, telephone): self._telephone = telephone.strip() def hasTelephone(self): return self._telephone != "" and self._telephone != None def isEmpty(self): return not self._email and not self._telephone def clone(self, owner): supportInfo = SupportInfo(owner) supportInfo.setCaption(self.getCaption()) supportInfo.setEmail(self.getEmail()) supportInfo.setTelephone(self.getTelephone()) return supportInfo
class BaseStatisticsReport(Fossilizable, object): fossilizes(IReportFossil) def __init__(self): # These are the containers which the report returns to the view. self._reportImageSources = {} self._reportWidgetSources = {} self._reportValueSources = {} self._reportGenerators = {} self._reportMethods = {} self._reportSetters = {} self._reportGenerated = None self._startDate = None self._endDate = None def _buildReports(self): """ All reports for this instance should be logged with a generator in self._reportGenerators. This method cyclces through these generators, executes the get command on the query object and then the set command on the report object. """ for resultType, generators in self._reportGenerators.iteritems(): for resultName, gen in generators.iteritems(): setMethod = self._reportSetters.get(resultType) setFunc = getattr(self, setMethod) if setFunc is None: raise Exception('Method %s not implemented' % setMethod) setFunc(resultName, gen.getResult()) self._reportGenerated = utc2server(nowutc(), naive=False) def getDateGenerated(self): return self._reportGenerated def getImagesSources(self): return self._reportImageSources def getValueSources(self): return self._reportValueSources def getWidgetSources(self): return self._reportWidgetSources def getStartDate(self): return self._startDate def getEndDate(self): return self._endDate def setImageSource(self, name, source): """ Only want a 1:1 map of key value, each image should have its own title, therefore no appending to lists etc. The values of these associations will be the raw, base64 encoded value of the image itself. """ sources = self.getImagesSources() sources[name] = source def setValueSource(self, name, source): """ This is a map of key : value relating to metrics retrieved from the API which have only a string or numerical value. """ sources = self.getValueSources() sources[name] = source def setWidgetSource(self, name, source): """ The values of this dictionary will be the full Piwik API URLs for widget iframe values. """ sources = self.getWidgetSources() sources[name] = source
class AvatarUserWrapper(Persistent, Fossilizable): """Avatar-like wrapper class that holds a DB-stored user.""" fossilizes(IAvatarFossil, IAvatarMinimalFossil) def __init__(self, user_id): self.id = str(user_id) @property @memoize_request def _original_user(self): # A proper user, with an id that can be mapped directly to sqlalchemy if isinstance(self.id, int) or self.id.isdigit(): return User.get(int(self.id)) # A user who had no real indico account but an ldap identifier/email. # In this case we try to find his real user and replace the ID of this object # with that user's ID. data = self.id.split(':') # TODO: Once everything is in SQLAlchemy this whole thing needs to go away! user = None if data[0] == 'LDAP': identifier = data[1] email = data[2] # You better have only one ldap provider or at least different identifiers ;) identity = Identity.find_first(Identity.provider != 'indico', Identity.identifier == identifier) if identity: user = identity.user elif data[0] == 'Nice': email = data[1] else: return None if not user: user = User.find_first(User.all_emails.contains(email)) if user: self._old_id = self.id self.id = str(user.id) logger.info("Updated legacy user id (%s => %s)", self._old_id, self.id) return user @property @memoize_request def user(self): user = self._original_user if user is not None and user.is_deleted and user.merged_into_id is not None: while user.merged_into_id is not None: user = user.merged_into_user return user def getId(self): return str(self.user.id) if self.user else str(self.id) @property def api_key(self): return self.user.api_key if self.user else None def linkTo(self, obj, role): if not self.user or self.user.is_deleted: return link = self.user.link_to(obj, role) if link and redis_write_client: event = avatar_links.event_from_obj(obj) if event: avatar_links.add_link(self.user, event, '{}_{}'.format(link.type, link.role)) def unlinkTo(self, obj, role): if not self.user or self.user.is_deleted: return links = self.user.unlink_to(obj, role) if redis_write_client: for link in links: event = avatar_links.event_from_obj(obj) if event: avatar_links.del_link(self.user, event, '{}_{}'.format(link.type, link.role)) def getStatus(self): return 'deleted' if not self.user or self.user.is_deleted else 'activated' def isActivated(self): # All accounts are activated during the transition period return True def isDisabled(self): # The user has been blocked or deleted (due to merge) return not self.user or self.user.is_blocked or self.user.is_deleted def setName(self, name, reindex=False): self.user.first_name = to_unicode(name) @encode_utf8 def getName(self): return self.user.first_name if self.user else '' getFirstName = getName def setSurName(self, surname, reindex=False): self.user.last_name = to_unicode(surname) @encode_utf8 def getSurName(self): return self.user.last_name if self.user else '' getFamilyName = getSurName @encode_utf8 def getFullName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=True, last_name_upper=True, abbrev_first_name=False, show_title=False) @encode_utf8 def getStraightFullName(self, upper=True): if not self.user: return '' return self.user.get_full_name(last_name_first=False, last_name_upper=upper, abbrev_first_name=False, show_title=False) getDirectFullNameNoTitle = getStraightFullName @encode_utf8 def getAbrName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=True, last_name_upper=False, abbrev_first_name=True, show_title=False) @encode_utf8 def getStraightAbrName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=False, last_name_upper=False, abbrev_first_name=True, show_title=False) def setOrganisation(self, affiliation, reindex=False): self.user.affiliation = to_unicode(affiliation) @encode_utf8 def getOrganisation(self): return self.user.affiliation if self.user else '' getAffiliation = getOrganisation def setTitle(self, title): self.user.title = to_unicode(title) @encode_utf8 def getTitle(self): return self.user.title if self.user else '' def setTimezone(self, tz): self.user.settings.set('timezone', to_unicode(tz)) @encode_utf8 def getTimezone(self): default = Config.getInstance().getDefaultTimezone() return self.user.settings.get('timezone', default) if self.user else default def getDisplayTZMode(self): return 'MyTimezone' if self.user and self.user.settings.get('force_timezone') else 'Event Timezone' def setDisplayTZMode(self, display_tz='Event Timezone'): self.user.settings.set('force_timezone', display_tz == 'MyTimezone') @encode_utf8 def getAddress(self): return self.user.address if self.user else '' def setAddress(self, address): self.user.address = to_unicode(address) def getEmails(self): # avoid 'stale association proxy' user = self.user return set(user.all_emails) if user else set() @encode_utf8 def getEmail(self): return self.user.email if self.user else '' email = property(getEmail) def setEmail(self, email, reindex=False): self.user.email = to_unicode(email) def hasEmail(self, email): user = self.user # avoid 'stale association proxy' if not user: return False return email.lower() in user.all_emails @encode_utf8 def getTelephone(self): return self.user.phone if self.user else '' def getFax(self): # Some older code still clones fax, etc... # it's never shown in the interface anyway. return '' getPhone = getTelephone def setTelephone(self, phone): self.user.phone = to_unicode(phone) setPhone = setTelephone def isRegisteredInConf(self, conf): if not self.user: return False return any(obj for obj in self.user.get_linked_objects('registration', 'registrant') if obj.getConference() == conf) def getRegistrantById(self, conf_id): if not self.user: return None return next((obj for obj in self.user.get_linked_objects('registration', 'registrant') if obj.getConference().id == conf_id), None) def containsUser(self, avatar): if self.user is None: return False return int(avatar.id) == self.user.id if avatar else False containsMember = containsUser def canModify(self, aw_or_user): if hasattr(aw_or_user, 'getUser'): aw_or_user = aw_or_user.getUser() return self.canUserModify(aw_or_user) def canUserModify(self, avatar): if not self.user: return False return avatar.id == str(self.user.id) or avatar.user.is_admin def getLocator(self): d = Locator() if self.user: d["userId"] = self.user.id return d def isAdmin(self): if not self.user: return False return self.user.is_admin @property def as_new(self): return self.user def __eq__(self, other): if not isinstance(other, (AvatarUserWrapper, User)): return False elif str(self.id) == str(other.id): return True elif self.user: return str(self.user.id) == str(other.id) else: return False def __ne__(self, other): return not (self == other) def __hash__(self): return hash(str(self.id)) @return_ascii def __repr__(self): if self.user is None: return u'<AvatarUserWrapper {}: user does not exist>'.format(self.id) elif self._original_user.merged_into_user: return u'<AvatarUserWrapper {}: {} ({}) [{}]>'.format( self.id, self._original_user.full_name, self._original_user.email, self.user.id) else: return u'<AvatarUserWrapper {}: {} ({})>'.format(self.id, self.user.full_name, self.user.email)
class BaseTask(TimedEvent): """ A base class for tasks. `expiryDate` is the last point in time when the task can run. A task will refuse to run if current time is past `expiryDate` """ DISABLE_ZODB_HOOK = False fossilizes(ITaskFossil) # seconds to consider a task AWOL _AWOLThresold = 6000 def __init__(self, expiryDate=None): self.createdOn = self._getCurrentDateTime() self.expiryDate = expiryDate self.typeId = self.__class__.__name__ self.id = None self.reset() self.status = 0 self.startedOn = None self.endedOn = None def __cmp__(self, obj): from indico.modules.scheduler.tasks.periodic import TaskOccurrence if obj is None: return 1 elif isinstance(obj, TaskOccurrence): task_cmp = cmp(self, obj.getTask()) if task_cmp == 0: return -1 else: return task_cmp # This condition will mostlike never happen elif not isinstance(obj, BaseTask) or (self.id == obj.id and self.id is None): return cmp(hash(self), hash(obj)) # This is the 'default case' where we are comparing 2 Tasks else: return cmp(self.id, obj.id) def reset(self): '''Resets a task to its state before being run''' self.running = False self.onRunningListSince = None # Time methods def getCreatedOn(self): return self.createdOn def getEndedOn(self): return self.endedOn def setEndedOn(self, dateTime): self.endedOn = dateTime def getStartedOn(self): return self.startedOn def setStartedOn(self, dateTime): self.startedOn = dateTime def setOnRunningListSince(self, sometime): self.onRunningListSince = sometime self._p_changed = 1 def getOnRunningListSince(self): return self.onRunningListSince def setStatus(self, newstatus): self.getLogger().info("%s set status %s" % (self, base.status(newstatus))) self.status = newstatus def getStatus(self): return self.status def getId(self): return self.id def getUniqueId(self): return "task%s" % self.id def getTypeId(self): return self.typeId def initialize(self, newid, newstatus): self.id = newid self.setStatus(newstatus) def plugLogger(self, logger): self._v_logger = logger def getLogger(self): if not hasattr(self, '_v_logger') or not self._v_logger: self._v_logger = logging.getLogger('task/%s' % self.typeId) return self._v_logger def prepare(self): """ This information will be saved regardless of the task being repeated or not """ curTime = self._getCurrentDateTime() tsDiff = int_timestamp(self.getStartOn()) - int_timestamp(curTime) if tsDiff > 0: self.getLogger().debug('Task %s will wait for some time. (%s) > (%s)' % \ (self.id, self.getStartOn(), curTime)) base.TimeSource.get().sleep(tsDiff) if self.expiryDate and curTime > self.expiryDate: self.getLogger().warning( 'Task %s will not be executed, expiryDate (%s) < current time (%s)' % \ (self.id, self.expiryDate, curTime)) return False self.startedOn = curTime self.running = True self.setStatus(base.TASK_STATUS_RUNNING) def start(self, delay): if self.DISABLE_ZODB_HOOK: update_session_options(db) self._executionDelay = delay try: self.run() self.endedOn = self._getCurrentDateTime() finally: LDAPConnector.destroy() self.running = False def tearDown(self): """ If a task needs to do something once it has run and been removed from runningList, overload this method """ pass def __str__(self): return "[%s:%s|%s]" % (self.typeId, self.id, base.status(self.status))
class PiwikReport(PiwikReportBase): fossilizes(IPiwikReportFossil) _defaultReportInterval = 14 def __init__(self, startDate, endDate, confId, contribId=None): """ Builds the map of generators to fill this object's variables before fossilization. """ PiwikReportBase.__init__(self) report = BaseReportGenerator self._conf = ConferenceHolder().getById(confId) self._confId = confId self._contribId = contribId self._buildDateRange(startDate, endDate) self._contributions = [] params = { 'startDate': self._startDate, 'endDate': self._endDate, 'confId': confId } if contribId: params['contribId'] = contribId # This report only has need for images and values, not for widgets. self._reportGenerators = { 'values': { 'visits': report(pq.PiwikQueryMetricConferenceVisits, params), 'uniqueVisits': report(pq.PiwikQueryMetricConferenceUniqueVisits, params), 'visitLength': report(pq.PiwikQueryMetricConferenceVisitLength, params), 'referrers': report(pq.PiwikQueryMetricConferenceReferrers, params), 'peakDate': report(pq.PiwikQueryMetricConferencePeakDateAndVisitors, params) } } self._buildReports() self._buildConferenceContributions() def _buildDateRange(self, startDate, endDate): """ If the default values are passed through, computes the appropriate date range based on whether today is before or after the conference end date. If after, end of period is set as conference end date. Start date is then calculated by the _defaultReportInterval difference. """ def getStartDate(): interval = datetime.timedelta(days=self._defaultReportInterval) adjustedStartDate = self._endDateTime - interval return str(adjustedStartDate.date()) def getEndDate(): today = nowutc() confEndDate = self._conf.getEndDate() self._endDateTime = confEndDate if today > confEndDate else today return str(self._endDateTime.date()) self._endDate = endDate if endDate else getEndDate() self._startDate = startDate if startDate else getStartDate() def _buildConferenceContributions(self): """ As this implementation permits the viewing of individual contributions, we make a list of tuples associating the uniqueId with the title (and start time) to be fossilized. """ contributions = self._conf.getContributionList() if contributions: self._contributions.append( ('None', str(_('Conference: ') + self._conf.getTitle()))) for ctrb in contributions: if not ctrb.isScheduled(): continue ctrbTime = str(ctrb.getStartDate().hour) + ':' + str( ctrb.getStartDate().minute) ctrbInfo = _( 'Contribution: ') + ctrb.getTitle() + ' (' + ctrbTime + ')' value = (ctrb.getUniqueId(), ctrbInfo) self._contributions.append(value) else: self._contributions = False def _getContributions(self): return self._contributions def getConferenceId(self): return self._confId def getContributionId(self): return self._contribId
class AvatarUserWrapper(Fossilizable): """Avatar-like wrapper class that holds a DB-stored user.""" fossilizes(IAvatarFossil, IAvatarMinimalFossil) def __init__(self, user_id): self.id = str(user_id) @property @memoize_request def _original_user(self): # A proper user, with an id that can be mapped directly to sqlalchemy if isinstance(self.id, int) or self.id.isdigit(): return User.get(int(self.id)) # A user who had no real indico account but an ldap identifier/email. # In this case we try to find his real user and replace the ID of this object # with that user's ID. data = self.id.split(':') # TODO: Once everything is in SQLAlchemy this whole thing needs to go away! user = None if data[0] == 'LDAP': identifier = data[1] email = data[2] # You better have only one ldap provider or at least different identifiers ;) identity = Identity.query.filter(Identity.provider != 'indico', Identity.identifier == identifier).first() if identity: user = identity.user elif data[0] == 'Nice': email = data[1] else: return None if not user: user = User.query.filter(User.all_emails == email).first() if user: self._old_id = self.id self.id = str(user.id) logger.info("Updated legacy user id (%s => %s)", self._old_id, self.id) return user @property @memoize_request def user(self): user = self._original_user if user is not None and user.is_deleted and user.merged_into_id is not None: while user.merged_into_id is not None: user = user.merged_into_user return user def getId(self): return str(self.user.id) if self.user else str(self.id) @property def api_key(self): return self.user.api_key if self.user else None def getStatus(self): return 'deleted' if not self.user or self.user.is_deleted else 'activated' def isActivated(self): # All accounts are activated during the transition period return True def isDisabled(self): # The user has been blocked or deleted (due to merge) return not self.user or self.user.is_blocked or self.user.is_deleted def setName(self, name, reindex=False): self.user.first_name = to_unicode(name) @encode_utf8 def getName(self): return self.user.first_name if self.user else '' getFirstName = getName def setSurName(self, surname, reindex=False): self.user.last_name = to_unicode(surname) @encode_utf8 def getSurName(self): return self.user.last_name if self.user else '' getFamilyName = getSurName @encode_utf8 def getFullName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=True, last_name_upper=True, abbrev_first_name=False, show_title=False) @encode_utf8 def getStraightFullName(self, upper=True): if not self.user: return '' return self.user.get_full_name(last_name_first=False, last_name_upper=upper, abbrev_first_name=False, show_title=False) getDirectFullNameNoTitle = getStraightFullName @encode_utf8 def getAbrName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=True, last_name_upper=False, abbrev_first_name=True, show_title=False) @encode_utf8 def getStraightAbrName(self): if not self.user: return '' return self.user.get_full_name(last_name_first=False, last_name_upper=False, abbrev_first_name=True, show_title=False) def setOrganisation(self, affiliation, reindex=False): self.user.affiliation = to_unicode(affiliation) @encode_utf8 def getOrganisation(self): return self.user.affiliation if self.user else '' getAffiliation = getOrganisation def setTitle(self, title): self.user.title = to_unicode(title) @encode_utf8 def getTitle(self): return self.user.title if self.user else '' def setTimezone(self, tz): self.user.settings.set('timezone', to_unicode(tz)) @encode_utf8 def getAddress(self): return self.user.address if self.user else '' def setAddress(self, address): self.user.address = to_unicode(address) def getEmails(self): # avoid 'stale association proxy' user = self.user return set(user.all_emails) if user else set() @encode_utf8 def getEmail(self): return self.user.email if self.user else '' email = property(getEmail) def setEmail(self, email, reindex=False): self.user.email = to_unicode(email) def hasEmail(self, email): user = self.user # avoid 'stale association proxy' if not user: return False return email.lower() in user.all_emails @encode_utf8 def getTelephone(self): return self.user.phone if self.user else '' def getFax(self): # Some older code still clones fax, etc... # it's never shown in the interface anyway. return '' getPhone = getTelephone def setTelephone(self, phone): self.user.phone = to_unicode(phone) setPhone = setTelephone def canUserModify(self, avatar): if not self.user: return False return avatar.id == str(self.user.id) or avatar.user.is_admin @locator_property def locator(self): d = {} if self.user: d['userId'] = self.user.id return d def isAdmin(self): if not self.user: return False return self.user.is_admin @property def as_new(self): return self.user def __eq__(self, other): if not isinstance(other, (AvatarUserWrapper, User)): return False elif str(self.id) == str(other.id): return True elif self.user: return str(self.user.id) == str(other.id) else: return False def __ne__(self, other): return not (self == other) def __hash__(self): return hash(str(self.id)) @return_ascii def __repr__(self): if self.user is None: return u'<AvatarUserWrapper {}: user does not exist>'.format(self.id) elif self._original_user.merged_into_user: return u'<AvatarUserWrapper {}: {} ({}) [{}]>'.format( self.id, self._original_user.full_name, self._original_user.email, self.user.id) else: return u'<AvatarUserWrapper {}: {} ({})>'.format(self.id, self.user.full_name, self.user.email)
class AvatarProvisionalWrapper(Fossilizable): """Wrap provisional data for users that are not in the DB yet.""" fossilizes(IAvatarFossil, IAvatarMinimalFossil) def __init__(self, identity_info): self.identity_info = identity_info self.data = identity_info.data def getId(self): return u"{}:{}".format(self.identity_info.provider.name, self.identity_info.identifier) id = property(getId) @encode_utf8 def getEmail(self): return self.data['email'] def getEmails(self): return [self.data['email']] @encode_utf8 def getFirstName(self): return self.data.get('first_name', '') @encode_utf8 def getFamilyName(self): return self.data.get('last_name', '') @encode_utf8 def getStraightFullName(self, upper=False): last_name = to_unicode(self.data.get('last_name', '')) if upper: last_name = last_name.upper() return u'{} {}'.format(to_unicode(self.data.get('first_name', '')), last_name) def getTitle(self): return '' @encode_utf8 def getTelephone(self): return self.data.get('phone', '') getPhone = getTelephone @encode_utf8 def getOrganisation(self): return self.data.get('affiliation', '') getAffiliation = getOrganisation def getFax(self): return None def getAddress(self): return u'' @return_ascii def __repr__(self): return u'<AvatarProvisionalWrapper {}: {} ({first_name} {last_name})>'.format( self.identity_info.provider.name, self.identity_info.identifier, **self.data.to_dict())
class AvatarUserWrapper(Persistent, Fossilizable): """Avatar-like wrapper class that holds a DB-stored user.""" fossilizes(IAvatarFossil, IAvatarMinimalFossil) def __init__(self, user_id): self.id = str(user_id) @property @memoize_request def _original_user(self): # A proper user, with an id that can be mapped directly to sqlalchemy if isinstance(self.id, int) or self.id.isdigit(): return User.get(int(self.id)) # A user who had no real indico account but an ldap identifier/email. # In this case we try to find his real user and replace the ID of this object # with that user's ID. data = self.id.split(':') # TODO: Once everything is in SQLAlchemy this whole thing needs to go away! user = None if data[0] == 'LDAP': identifier = data[1] email = data[2] # You better have only one ldap provider or at least different identifiers ;) identity = Identity.find_first(Identity.provider != 'indico', Identity.identifier == identifier) if identity: user = identity.user elif data[0] == 'Nice': email = data[1] else: return None if not user: user = User.find_first(User.all_emails.contains(email)) if user: self._old_id = self.id self.id = str(user.id) return user @property @memoize_request def user(self): user = self._original_user if user is None: return None elif user.is_deleted: return user.merged_into_user if user.merged_into_id is not None else None else: return user def getId(self): return str(self.user.id) @property def api_key(self): return self.user.api_key def linkTo(self, obj, role): # deleted users shouldn't be able to be linked # TODO: log deleted users? if not self.user.is_deleted: link = self.user.link_to(obj, role) if link and redis_write_client: event = avatar_links.event_from_obj(obj) if event: avatar_links.add_link(self, event, '{}_{}'.format(link.type, link.role)) def unlinkTo(self, obj, role): # deleted users shouldn't be able to be linked # TODO: log deleted users? if not self.user.is_deleted: links = self.user.unlink_to(obj, role) if redis_write_client: for link in links: event = avatar_links.event_from_obj(obj) if event: avatar_links.del_link( self, event, '{}_{}'.format(link.type, link.role)) def getLinkedTo(self): return None def getStatus(self): return 'deleted' if self.user.is_deleted else 'activated' def isActivated(self): # All accounts are activated during the transition period return True def isDisabled(self): # The user has been blocked or deleted (due to merge) return self.user.is_blocked or self.user.is_deleted def setName(self, name, reindex=False): self.user.first_name = to_unicode(name) @encode_utf8 def getName(self): return self.user.first_name getFirstName = getName def setSurName(self, surname, reindex=False): self.user.last_name = to_unicode(surname) @encode_utf8 def getSurName(self): return self.user.last_name getFamilyName = getSurName @encode_utf8 def getFullName(self): return self.user.get_full_name(last_name_first=True, last_name_upper=True, abbrev_first_name=False, show_title=False) @encode_utf8 def getStraightFullName(self, upper=True): return self.user.get_full_name(last_name_first=False, last_name_upper=True, abbrev_first_name=False, show_title=False) getDirectFullNameNoTitle = getStraightFullName @encode_utf8 def getAbrName(self): return self.user.get_full_name(last_name_first=True, last_name_upper=False, abbrev_first_name=True, show_title=False) @encode_utf8 def getStraightAbrName(self): return self.user.get_full_name(last_name_first=False, last_name_upper=False, abbrev_first_name=True, show_title=False) def setOrganisation(self, affiliation, reindex=False): self.user.affiliation = to_unicode(affiliation) @encode_utf8 def getOrganisation(self): return self.user.affiliation getAffiliation = getOrganisation def setTitle(self, title): self.user.title = to_unicode(title) @encode_utf8 def getTitle(self): return self.user.title def setTimezone(self, tz): self.user.settings.set('timezone', to_unicode(tz)) @encode_utf8 def getTimezone(self): return self.user.settings.get( 'timezone', HelperMaKaCInfo.getMaKaCInfoInstance().getTimezone()) def getDisplayTZMode(self): return 'MyTimezone' if self.user.settings.get( 'force_timezone') else 'Event Timezone' def setDisplayTZMode(self, display_tz='Event Timezone'): self.user.settings.set('force_timezone', display_tz == 'MyTimezone') @encode_utf8 def getAddresses(self): return [self.user.address] @encode_utf8 def getAddress(self): return self.user.address def setAddress(self, address): self.user.address = to_unicode(address) def getEmails(self): # avoid 'stale association proxy' user = self.user return set(user.all_emails) @encode_utf8 def getEmail(self): return self.user.email def setEmail(self, email, reindex=False): self.user.email = to_unicode(email) def getSecondaryEmails(self): return self.user.secondary_emails def addSecondaryEmail(self, email, reindex=False): return self.user.secondary_emails.add(to_unicode( email.strip().lower())) def removeSecondaryEmail(self, email, reindex=False): self.user.secondary_emails.remove(email) def setSecondaryEmails(self, emails, reindex=False): self.user.secondary_emails = { to_unicode(email.strip().lower()) for email in emails } def hasEmail(self, email): return email.lower() in self.user.all_emails def hasSecondaryEmail(self, email): return email.lower() in self.user.secondary_emails @encode_utf8 def getTelephone(self): return self.user.phone def getFax(self): # Some older code still clones fax, etc... # it's never shown in the interface anyway. return '' getPhone = getTelephone def setTelephone(self, phone): self.user.phone = to_unicode(phone) setPhone = setTelephone def clearAuthenticatorPersonalData(self): pass def setAuthenticatorPersonalData(self, field, value): pass def getIdentityById(self, id, tag): return True def addRegistrant(self, r): # This doesn't seem to be needed, as it is stored in linkedTo as well # TODO: Check that it can be deleted pass def removeRegistrant(self, r): # This doesn't seem to be needed, as it is stored in linkedTo as well # TODO: Check that it can be deleted pass def isRegisteredInConf(self, conf): return any(obj for obj in self.user.get_linked_objects( 'registration', 'registrant') if obj.getConference() == conf) def getRegistrantById(self, conf_id): return next((obj for obj in self.user.get_linked_objects( 'registration', 'registrant') if obj.getConference().id == conf_id), None) def hasSubmittedEvaluation(self, evaluation): for submission in evaluation.getSubmissions(): submitter = submission.getSubmitter() if submitter and submitter.id == self.user.id: return True return False def containsUser(self, avatar): if self.user is None: return False return int(avatar.id) == self.user.id if avatar else False containsMember = containsUser def canModify(self, aw_or_user): if hasattr(aw_or_user, 'getUser'): aw_or_user = aw_or_user.getUser() return self.canUserModify(aw_or_user) def canUserModify(self, avatar): return avatar.id == str(self.user.id) or avatar.user.is_admin def getLocator(self): d = Locator() d["userId"] = self.user.id return d def is_member_of_group(self, group_name): group_provider = multipass.default_group_provider group = GroupProxy(group_name, group_provider.name if group_provider else None) return group.has_member(self.user) def isAdmin(self): return self.user.is_admin @memoize_request def isRBAdmin(self): """Convenience method for checking whether this user is an admin for the RB module.""" return rb_is_admin(self) @property @memoize_request def has_rooms(self): """Checks if the user has any rooms""" from indico.modules.rb.models.rooms import Room # avoid circular import return Room.user_owns_rooms(self) @memoize_request def get_rooms(self): """Returns the rooms this user is responsible for""" from indico.modules.rb.models.rooms import Room # avoid circular import return Room.get_owned_by(self) @encode_utf8 def getLang(self): return self.user.settings.get('lang') def setLang(self, lang): self.user.settings.set('lang', to_unicode(lang)) def __eq__(self, other): if not isinstance(other, (AvatarUserWrapper, User)): return False elif str(self.id) == str(other.id): return True elif self.user: return str(self.user.id) == str(other.id) else: return False def __ne__(self, other): return not (self == other) def __hash__(self): return hash(str(self.id)) @return_ascii def __repr__(self): if self.user is None: return u'<AvatarUserWrapper {}: user does not exist>'.format( self.id) elif self._original_user.merged_into_user: return u'<AvatarUserWrapper {}: {} ({}) [{}]>'.format( self.id, self._original_user.full_name, self._original_user.email, self.user.id) else: return u'<AvatarUserWrapper {}: {} ({})>'.format( self.id, self.user.full_name, self.user.email)