class _DependencyConnector(Item): """ I am a connector between installed items and their targets. """ installee = reference(doc="The item installed.") target = reference(doc="The item installed upon.") explicitlyInstalled = boolean(doc="Whether this item was installed" "explicitly (and thus whether or not it" "should be automatically uninstalled when" "nothing depends on it)")
class _PowerupConnector(Item): """ I am a connector between the store and a powerup. """ typeName = 'axiom_powerup_connector' powerup = reference() item = reference() interface = text() priority = integer()
class LoginMethod(Item): typeName = 'login_method' schemaVersion = 2 localpart = text(doc=""" A local-part of my user's identifier. """, indexed=True, allowNone=False) domain = text(doc=""" The domain part of my user's identifier. [XXX See TODO below] May be None (generally for "system" users). """, indexed=True) internal = boolean(doc=""" Flag indicating whether this is a method maintained by this server, or if it represents an external contact mechanism (such as a third-party email provider) """, allowNone=False) protocol = text(indexed=True, allowNone=False) account = reference(doc=""" A reference to the LoginAccount for which this is a login method. """, allowNone=False) verified = boolean(indexed=True, allowNone=False)
class TimedEventFailureLog(Item): typeName = 'timed_event_failure_log' schemaVersion = 1 desiredTime = timestamp() actualTime = timestamp() runnable = reference() traceback = bytes()
class SubStoreStartupService(Item, service.Service): """ This class no longer exists. It is here simply to trigger an upgrade which deletes it. Ignore it, please. """ installedOn = reference() parent = inmemory() running = inmemory() name = inmemory() schemaVersion = 2
class TimedEvent(Item): typeName = 'timed_event' schemaVersion = 1 time = timestamp(indexed=True) runnable = reference() def _rescheduleFromRun(self, newTime): """ Schedule this event to be run at the indicated time, or if the indicated time is None, delete this event. """ if newTime is None: self.deleteFromStore() else: self.time = newTime def invokeRunnable(self): """ Run my runnable, and reschedule or delete myself based on its result. Must be run in a transaction. """ runnable = self.runnable if runnable is None: self.deleteFromStore() else: self._rescheduleFromRun(runnable.run()) def handleError(self, now, failureObj): """ An error occurred running my runnable. Check my runnable for an error-handling method called 'timedEventErrorHandler' that will take the given failure as an argument, and execute that if available: otherwise, create a TimedEventFailureLog with information about what happened to this event. Must be run in a transaction. """ errorHandler = getattr(self.runnable, 'timedEventErrorHandler', None) if errorHandler is not None: self._rescheduleFromRun(errorHandler(self, failureObj)) else: self._defaultErrorHandler(now, failureObj) def _defaultErrorHandler(self, now, failureObj): tefl = TimedEventFailureLog(store=self.store, desiredTime=self.time, actualTime=now, runnable=self.runnable, traceback=failureObj.getTraceback()) self.deleteFromStore()
class Tag(Item): typeName = 'tag' schemaVersion = 1 name = text(doc=""" The short string which is being applied as a tag to an Item. """) created = timestamp(doc=""" When this tag was applied to the Item to which it applies. """) object = reference(doc=""" The Item to which this tag applies. """) catalog = reference(doc=""" The L{Catalog} item in which this tag was created. """) tagger = reference(doc=""" An optional reference to the Item which is responsible for this tag's existence. """)
class _TagName(Item): """ Helper class to make Catalog.tagNames very fast. One of these is created for each distinct tag name that is created. _TagName Items are never deleted from the database. """ typeName = 'tagname' name = text(doc=""" The short string which uniquely represents this tag. """, indexed=True) catalog = reference(doc=""" The L{Catalog} item in which this tag exists. """)
class _SubSchedulerParentHook(Item): schemaVersion = 2 typeName = 'axiom_subscheduler_parent_hook' loginAccount = reference() scheduledAt = timestamp(default=None) scheduler = dependsOn(Scheduler) def run(self): self.scheduledAt = None IScheduler(self.loginAccount).tick() def _schedule(self, when): if self.scheduledAt is not None: if when < self.scheduledAt: self.scheduler.reschedule(self, self.scheduledAt, when) self.scheduledAt = when else: self.scheduler.schedule(self, when) self.scheduledAt = when
def dependsOn(itemType, itemCustomizer=None, doc='', indexed=True, whenDeleted=reference.NULLIFY): """ This function behaves like L{axiom.attributes.reference} but with an extra behaviour: when this item is installed (via L{axiom.dependency.installOn} on a target item, the type named here will be instantiated and installed on the target as well. For example:: class Foo(Item): counter = integer() thingIDependOn = dependsOn(Baz, lambda baz: baz.setup()) @param itemType: The Item class to instantiate and install. @param itemCustomizer: A callable that accepts the item installed as a dependency as its first argument. It will be called only if an item is created to satisfy this dependency. @return: An L{axiom.attributes.reference} instance. """ frame = sys._getframe(1) locals = frame.f_locals # Try to make sure we were called from a class def. if (locals is frame.f_globals) or ('__module__' not in locals): raise TypeError("dependsOn can be used only from a class definition.") ref = reference(reftype=itemType, doc=doc, indexed=indexed, allowNone=True, whenDeleted=whenDeleted) if "__dependsOn_advice_data__" not in locals: addClassAdvisor(_dependsOn_advice) locals.setdefault('__dependsOn_advice_data__', []).append( (itemType, itemCustomizer, ref)) return ref
class LoginAccount(Item): """ I am an entry in a LoginBase. @ivar avatars: An Item which is adaptable to various cred client interfaces. Plural because it represents a collection of potentially disparate implementors, such as an IResource for web access and an IContact for SIP access. @ivar disabled: This account has been disabled. It is still database-resident but the user should not be allowed to log in. """ typeName = 'login' schemaVersion = 2 password = text() avatars = reference() # reference to a thing which can be adapted to # implementations for application-level # protocols. In general this is a reference to # a SubStore because this is optimized for # applications where per-user data is a # substantial portion of the cost. disabled = integer() def __conform__(self, interface): """ For convenience, forward adaptation to my 'avatars' attribute. """ ifa = interface(self.avatars, None) return ifa def migrateDown(self): """ Assuming that self.avatars is a SubStore which should contain *only* the LoginAccount for the user I represent, remove all LoginAccounts and LoginMethods from that store and copy all methods from the site store down into it. """ ss = self.avatars.open() def _(): oldAccounts = ss.query(LoginAccount) oldMethods = ss.query(LoginMethod) for x in list(oldAccounts) + list(oldMethods): x.deleteFromStore() self.cloneInto(ss, ss) sched = IScheduler(ss, None) if sched is not None: sched.migrateDown() ss.transact(_) def migrateUp(self): """ Copy this LoginAccount and all associated LoginMethods from my store (which is assumed to be a SubStore, most likely a user store) into the site store which contains it. """ siteStore = self.store.parent def _(): # No convenience method for the following because needing to do it is # *rare*. It *should* be ugly; 99% of the time if you need to do this # you're making a mistake. -glyph siteStoreSubRef = siteStore.getItemByID(self.store.idInParent) self.cloneInto(siteStore, siteStoreSubRef) sched = IScheduler(self.store, None) if sched is not None: sched.migrateUp() siteStore.transact(_) def cloneInto(self, newStore, avatars): """ Create a copy of this LoginAccount and all associated LoginMethods in a different Store. Return the copied LoginAccount. """ la = LoginAccount(store=newStore, password=self.password, avatars=avatars, disabled=self.disabled) for siteMethod in self.store.query(LoginMethod, LoginMethod.account == self): lm = LoginMethod(store=newStore, localpart=siteMethod.localpart, domain=siteMethod.domain, internal=siteMethod.internal, protocol=siteMethod.protocol, verified=siteMethod.verified, account=la) return la def deleteLoginMethods(self): self.store.query(LoginMethod, LoginMethod.account == self).deleteFromStore() def addLoginMethod(self, localpart, domain, protocol=ANY_PROTOCOL, verified=False, internal=False): """ Add a login method to this account, propogating up or down as necessary to site store or user store to maintain consistency. """ # Out takes you west or something if self.store.parent is None: # West takes you in otherStore = self.avatars.open() peer = otherStore.findUnique(LoginAccount) else: # In takes you east otherStore = self.store.parent subStoreItem = self.store.parent.getItemByID(self.store.idInParent) peer = otherStore.findUnique(LoginAccount, LoginAccount.avatars == subStoreItem) # Up and down take you home for store, account in [(otherStore, peer), (self.store, self)]: store.findOrCreate(LoginMethod, account=account, localpart=localpart, domain=domain, protocol=protocol, verified=verified, internal=internal)