grzywacz@2058: # -*- coding: iso-8859-1 -*- grzywacz@2058: """ grzywacz@2058: MoinMoin - event (notification) framework grzywacz@2058: grzywacz@2058: This code abstracts event handling in MoinMoin, grzywacz@2058: currently for notifications. It implements the observer pattern. grzywacz@2058: grzywacz@2064: @copyright: 2007 by Karol Nowak grzywacz@2064: @license: GNU GPL, see COPYING for details. grzywacz@2058: """ grzywacz@2058: grzywacz@2323: import logging grzywacz@2323: grzywacz@2058: from MoinMoin import wikiutil grzywacz@2058: from MoinMoin.util import pysupport grzywacz@2073: from MoinMoin.wikiutil import PluginAttributeError grzywacz@2058: grzywacz@2058: # Create a list of extension actions from the package directory grzywacz@2058: modules = pysupport.getPackageModules(__file__) grzywacz@2058: grzywacz@2323: # A module-wide Logger, don't use it from outside grzywacz@2323: logger = logging.getLogger("events") grzywacz@2323: logger.setLevel(logging.DEBUG) grzywacz@2323: logger.addHandler(logging.StreamHandler()) grzywacz@2323: grzywacz@2464: # Dummy pseudo-getText function used in event descriptions, grzywacz@2464: # so that they get into .po files grzywacz@2464: _ = lambda x: x grzywacz@2086: grzywacz@2152: class Event(object): grzywacz@2058: """A class handling information common to all events.""" grzywacz@2064: def __init__(self, request): grzywacz@2064: self.request = request grzywacz@2086: tw@2286: grzywacz@2091: class PageEvent(Event): grzywacz@2061: """An event related to a page change""" grzywacz@2094: def __init__(self, request): grzywacz@2094: Event.__init__(self, request) grzywacz@2086: tw@2286: grzywacz@2061: class PageChangedEvent(PageEvent): tw@2286: grzywacz@2469: description = _(u"""Page has been modified""") grzywacz@2152: req_superuser = False grzywacz@2337: grzywacz@2469: def __init__(self, request, page, comment): grzywacz@2094: PageEvent.__init__(self, request) grzywacz@2064: self.page = page grzywacz@2064: self.comment = comment grzywacz@2469: grzywacz@2469: grzywacz@2469: class TrivialPageChangedEvent(PageEvent): grzywacz@2469: grzywacz@2469: description = _(u"Page has been modified in a trivial fashion") grzywacz@2469: req_superuser = False grzywacz@2469: grzywacz@2469: def __init__(self, request, page, comment): grzywacz@2469: PageEvent.__init__(self, request) grzywacz@2469: self.page = page grzywacz@2469: self.comment = comment grzywacz@2086: grzywacz@2337: grzywacz@2061: class PageRenamedEvent(PageEvent): grzywacz@2337: grzywacz@2464: description = _(u"""Page has been renamed""") grzywacz@2329: req_superuser = False grzywacz@2337: grzywacz@2461: def __init__(self, request, page, old_page, comment=""): grzywacz@2329: PageEvent.__init__(self, request) grzywacz@2329: self.page = page grzywacz@2329: self.old_page = old_page grzywacz@2329: self.comment = comment grzywacz@2109: grzywacz@2109: grzywacz@2061: class PageDeletedEvent(PageEvent): grzywacz@2337: grzywacz@2464: description = _(u"""Page has been deleted""") grzywacz@2152: req_superuser = False grzywacz@2337: grzywacz@2109: def __init__(self, request, page, comment): grzywacz@2109: PageEvent.__init__(self, request) grzywacz@2109: self.page = page grzywacz@2109: self.comment = comment grzywacz@2106: grzywacz@2106: grzywacz@2385: class PageCopiedEvent(PageEvent): grzywacz@2385: grzywacz@2464: description = _(u"""Page has been copied""") grzywacz@2385: req_superuser = False grzywacz@2385: grzywacz@2385: def __init__(self, request, page, old_page, comment): grzywacz@2385: PageEvent.__init__(self, request) grzywacz@2385: self.page = page grzywacz@2385: self.old_page = old_page grzywacz@2385: self.comment = comment grzywacz@2385: grzywacz@2385: grzywacz@2064: class FileAttachedEvent(PageEvent): grzywacz@2337: grzywacz@2464: description = _(u"""A new attachment has been added""") grzywacz@2152: req_superuser = False grzywacz@2337: grzywacz@2356: def __init__(self, request, pagename, name, size): grzywacz@2107: PageEvent.__init__(self, request) grzywacz@2106: self.request = request grzywacz@2106: self.pagename = pagename grzywacz@2356: self.name = name grzywacz@2106: self.size = size grzywacz@2064: grzywacz@2086: grzywacz@2064: class PageRevertedEvent(PageEvent): grzywacz@2337: grzywacz@2464: description = _(u"""A page has been reverted to a previous state""") grzywacz@2152: req_superuser = False grzywacz@2337: grzywacz@2064: def __init__(self, request, pagename, previous, current): grzywacz@2094: PageEvent.__init__(self, request) grzywacz@2064: self.pagename = pagename grzywacz@2064: self.previous = previous grzywacz@2337: self.current = current grzywacz@2064: grzywacz@2086: grzywacz@2064: class SubscribedToPageEvent(PageEvent): grzywacz@2337: grzywacz@2467: description = _(u"""A user has subscribed to a page""") grzywacz@2152: req_superuser = True grzywacz@2337: grzywacz@2064: def __init__(self, request, pagename, username): grzywacz@2337: PageEvent.__init__(self, request) grzywacz@2064: self.pagename = pagename grzywacz@2064: self.username = username grzywacz@2064: grzywacz@2086: grzywacz@2091: class JabberIDSetEvent(Event): grzywacz@2091: """ Sent when user changes her Jabber ID """ grzywacz@2337: grzywacz@2091: def __init__(self, request, jid): grzywacz@2091: Event.__init__(self, request) grzywacz@2091: self.jid = jid grzywacz@2337: grzywacz@2095: class JabberIDUnsetEvent(Event): grzywacz@2095: """ Sent when Jabber ID is no longer used grzywacz@2337: grzywacz@2095: Obviously this will be usually sent along with JabberIDSetEvent, grzywacz@2095: because we require user's jabber id to be unique by default. grzywacz@2337: grzywacz@2095: """ grzywacz@2095: def __init__(self, request, jid): grzywacz@2095: Event.__init__(self, request) grzywacz@2095: self.jid = jid grzywacz@2337: grzywacz@2145: class UserCreatedEvent(Event): grzywacz@2145: """ Sent when a new user has been created """ grzywacz@2337: grzywacz@2464: description = _(u"""A new account has been created""") grzywacz@2152: req_superuser = True grzywacz@2337: grzywacz@2145: def __init__(self, request, user): grzywacz@2145: Event.__init__(self, request) grzywacz@2145: self.user = user grzywacz@2337: grzywacz@2396: class PagePreSaveEvent(Event): grzywacz@2396: """ Event sent when a page is about to be saved grzywacz@2396: grzywacz@2396: This can be used to abort a save, for instance, grzywacz@2396: if handler returns grzywacz@2396: grzywacz@2396: """ grzywacz@2396: def __init__(self, request, page_editor, new_text): grzywacz@2396: Event.__init__(self, request) grzywacz@2396: self.page_editor = page_editor grzywacz@2396: self.new_text = new_text grzywacz@2396: grzywacz@2396: grzywacz@2318: class EventResult: grzywacz@2318: """ This is a base class for messages passed from event handlers """ grzywacz@2318: pass grzywacz@2337: grzywacz@2396: class Abort(EventResult): grzywacz@2396: """ Result returned if handler wants to abort operation that sent the event """ grzywacz@2396: def __init__(self, reason): grzywacz@2396: """ grzywacz@2396: @param reason: human-readable reason of failure grzywacz@2396: """ grzywacz@2396: self.reason = reason grzywacz@2399: grzywacz@2396: grzywacz@2307: def get_handlers(cfg): grzywacz@2058: """Create a list of available event handlers. grzywacz@2337: grzywacz@2058: Each handler is a handle() function defined in an plugin, grzywacz@2058: pretty much like in case of actions. grzywacz@2337: grzywacz@2307: TODO: maybe make it less dumb? ;-) grzywacz@2337: grzywacz@2307: """ grzywacz@2307: event_handlers = [] grzywacz@2058: names = wikiutil.getPlugins("events", cfg) grzywacz@2058: grzywacz@2058: for name in names: grzywacz@2073: try: grzywacz@2073: handler = wikiutil.importPlugin(cfg, "events", name, "handle") grzywacz@2073: except PluginAttributeError: grzywacz@2073: handler = None grzywacz@2337: grzywacz@2058: if handler is not None: grzywacz@2307: event_handlers.append(handler) grzywacz@2337: grzywacz@2307: return event_handlers grzywacz@2058: grzywacz@2086: grzywacz@2058: def send_event(event): grzywacz@2312: """Function called from outside to process an event grzywacz@2337: grzywacz@2312: @return: a list of messages returned by handlers grzywacz@2312: @rtype: list grzywacz@2312: """ grzywacz@2337: grzywacz@2064: # A list of messages generated by event handlers, passed back to caller grzywacz@2064: msg = [] grzywacz@2307: cfg = event.request.cfg grzywacz@2337: grzywacz@2058: # Try to handle the event with each available handler (for now) grzywacz@2307: for handle in cfg.event_handlers: grzywacz@2064: retval = handle(event) grzywacz@2385: grzywacz@2385: assert retval is None or isinstance(retval, EventResult) grzywacz@2385: grzywacz@2318: if retval: grzywacz@2064: msg.append(retval) grzywacz@2337: grzywacz@2076: return msg grzywacz@2152: grzywacz@2152: def get_subscribable_events(): grzywacz@2152: """Create and return a list of user-visible events grzywacz@2337: grzywacz@2152: @return: A list of user-visible events described by dictionaries grzywacz@2152: @rtype: dict grzywacz@2152: """ grzywacz@2152: defs = globals() grzywacz@2337: subscribable_events = {} grzywacz@2337: grzywacz@2307: for ev in defs.values(): grzywacz@2307: if type(ev) is type and issubclass(ev, Event) and ev.__dict__.has_key("description"): grzywacz@2307: subscribable_events[ev.__name__] = {'desc': ev.description, grzywacz@2307: 'superuser': ev.req_superuser} grzywacz@2307: return subscribable_events grzywacz@2464: grzywacz@2464: # Get rid of the dummy getText so that it doesn't get imported with * grzywacz@2464: del _