changeset 2163:93e8ba421a88

merge jabber branch
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Mon, 18 Jun 2007 21:43:28 +0200
parents b97040fcd8e1 (current diff) e6dd69d3ac69 (diff)
children 7fed5edaa2a5
files
diffstat 10 files changed, 237 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/config/multiconfig.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/config/multiconfig.py	Mon Jun 18 21:43:28 2007 +0200
@@ -376,6 +376,7 @@
 
     navi_bar = [u'RecentChanges', u'FindPage', u'HelpContents', ]
     nonexist_qm = False
+    notification_bot_uri = None
 
     page_credits = [
         '<a href="http://moinmoin.wikiwikiweb.de/">MoinMoin Powered</a>',
@@ -435,6 +436,7 @@
     siteid = 'default'
     stylesheets = [] # list of tuples (media, csshref) to insert after theme css, before user css
     subscribed_pages_default = [] # preload user subscribed pages with this page list
+    subscribed_events_default = [] # preload user subscribed events with this list
     superuser = [] # list of unicode user names that have super powers :)
     supplementation_page = False
     supplementation_page_name = u'Discussion'
@@ -661,7 +663,12 @@
         self.mail_enabled = (self.mail_smarthost is not None or self.mail_sendmail is not None) and self.mail_from
         
         # check if jabber bot is available and set flag:
-        self.jabber_enabled = self.bot_host is not None
+        self.jabber_enabled = self.notification_bot_uri is not None
+        
+        # if we are to use the jabber bot, instantiate a server object for future use
+        if self.jabber_enabled:
+            from xmlrpclib import Server
+            self.notification_server = Server(self.notification_bot_uri, )
 
         # Cache variables for the properties below
         self._iwid = self._iwid_full = self._meta_dict = None
--- a/MoinMoin/events/__init__.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/events/__init__.py	Mon Jun 18 21:43:28 2007 +0200
@@ -17,11 +17,14 @@
 # A list of available event handlers
 _event_handlers = None
 
+# A list of event types that user can subscribe to
+_subscribable_events = None
+
 # Create a list of extension actions from the package directory
 modules = pysupport.getPackageModules(__file__)
 
 
-class Event:
+class Event(object):
     """A class handling information common to all events."""
     def __init__(self, request):
         self.request = request
@@ -34,6 +37,10 @@
 
         
 class PageChangedEvent(PageEvent):
+    
+    description = u"""Page has been modified (edit, creation, deletion)"""
+    req_superuser = False
+    
     def __init__(self, request, page, comment, trivial):
         PageEvent.__init__(self, request)
         self.page = page
@@ -46,6 +53,10 @@
 
 
 class PageDeletedEvent(PageEvent):
+    
+    description = u"""Page has been deleted"""
+    req_superuser = False
+    
     def __init__(self, request, page, comment):
         PageEvent.__init__(self, request)
         self.request = request
@@ -54,6 +65,10 @@
 
 
 class FileAttachedEvent(PageEvent):
+    
+    description = u"""A new attachment has been added"""
+    req_superuser = False
+    
     def __init__(self, request, pagename, attachment_name, size):
         PageEvent.__init__(self, request)
         self.request = request
@@ -63,6 +78,10 @@
 
 
 class PageRevertedEvent(PageEvent):
+    
+    description = u"""A page has been reverted to a previous state"""
+    req_superuser = False
+    
     def __init__(self, request, pagename, previous, current):
         PageEvent.__init__(self, request)
         self.pagename = pagename
@@ -71,6 +90,10 @@
 
 
 class SubscribedToPageEvent(PageEvent):
+    
+    description = u"""An user has subscribed to a page"""
+    req_superuser = True
+    
     def __init__(self, request, pagename, username):
         PageEvent.__init__(self, request)    
         self.pagename = pagename
@@ -79,6 +102,7 @@
 
 class JabberIDSetEvent(Event):
     """ Sent when user changes her Jabber ID """
+    
     def __init__(self, request, jid):
         Event.__init__(self, request)
         self.jid = jid
@@ -88,11 +112,22 @@
     
     Obviously this will be usually sent along with JabberIDSetEvent,
     because we require user's jabber id to be unique by default.
+    
     """
     def __init__(self, request, jid):
         Event.__init__(self, request)
         self.jid = jid
         
+class UserCreatedEvent(Event):
+    """ Sent when a new user has been created """
+    
+    description = u"""A new account has been created"""
+    req_superuser = True
+    
+    def __init__(self, request, user):
+        Event.__init__(self, request)
+        self.user = user
+        
 def _register_handlers(cfg):
     """Create a list of available event handlers.
     
@@ -133,3 +168,22 @@
             msg.append(retval)
             
     return msg
+
+def get_subscribable_events():
+    """Create and return a list of user-visible events
+    
+    @return: A list of user-visible events described by dictionaries
+    @rtype: dict
+    """
+    global _subscribable_events
+    defs = globals()
+    
+    if not _subscribable_events:
+        _subscribable_events = {}
+        
+        for ev in defs.values():
+            if type(ev) is type and issubclass(ev, Event) and ev.__dict__.has_key("description"):
+                _subscribable_events[ev.__name__] = {'desc': ev.description,
+                                                     'superuser': ev.req_superuser}
+    
+    return _subscribable_events
--- a/MoinMoin/events/emailnotify.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/events/emailnotify.py	Mon Jun 18 21:43:28 2007 +0200
@@ -13,7 +13,7 @@
 from MoinMoin.Page import Page
 from MoinMoin.mail import sendmail
 from MoinMoin.events import *
-from MoinMoin.events.notification_common import page_changed_notification
+from MoinMoin.events.messages import page_change_message
 
 
 def sendNotification(request, page, comment, emails, email_lang, revisions, trivial):
@@ -28,7 +28,8 @@
     @return: sendmail result
     """
     _ = request.getText
-    mailBody = page_changed_notification(request, page, comment, email_lang, revisions, trivial)
+    mailBody = page_change_message("page_changed", request, page, email_lang, 
+                                   comment=comment, revisions=revisions)
 
     return sendmail.sendmail(request, emails,
         _('[%(sitename)s] %(trivial)sUpdate of "%(pagename)s" by %(username)s', formatted=False) % {
--- a/MoinMoin/events/jabbernotify.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/events/jabbernotify.py	Mon Jun 18 21:43:28 2007 +0200
@@ -16,23 +16,14 @@
 import MoinMoin.events as ev
 from MoinMoin.events.messages import page_change_message
 
-# XML RPC Server object used to communicate with notification bot
-server = None
-
 
 def handle(event):
-    global server
-
     cfg = event.request.cfg
 
     # Check for desired event type and if notification bot is configured
     if not cfg.jabber_enabled:
         return
     
-    # Create an XML RPC server object only if it doesn't exist
-    if server is None:
-        server = xmlrpclib.Server("http://" + cfg.bot_host)
-    
     if isinstance(event, ev.PageChangedEvent):
         return handle_page_changed(event)
     elif isinstance(event, ev.JabberIDSetEvent) or isinstance(event, ev.JabberIDUnsetEvent):
@@ -41,16 +32,19 @@
         return handle_file_attached(event)
     elif isinstance(event, ev.PageDeletedEvent):
         return handle_page_deleted(event)
+    elif isinstance(event, ev.UserCreatedEvent):
+        return handle_user_created(event)
     
 
 def handle_jid_changed(event):
     """ Handles events sent when user's JID changes """
     
     request = event.request
+    server = request.cfg.notification_server
     _ = request.getText
     
     try:
-        if isinstance(event, JabberIDSetEvent):
+        if isinstance(event, ev.JabberIDSetEvent):
             server.addJIDToRoster(request.cfg.secret, event.jid)
         else:
             server.removeJIDFromRoster(request.cfg.secret, event.jid)        
@@ -63,13 +57,36 @@
         return (0, _("Notifications not sent"))
 
 
+def _filter_subscriber_list(event, subscribers):
+    """Filter a list of page subscribers to honor event subscriptions
+    
+    @param subscribers: list of subscribers (dict of lists, language is the key)
+    @type subscribers: dict
+    
+    """
+    event_name = event.__class__.__name__
+    
+    # Filter the list by removing users who don't want to receive
+    # notifications about this type of event
+    for lang in subscribers.keys():
+        userlist = []
+        
+        for usr in subscribers[lang]:
+            if event_name in usr.subscribed_events:
+                userlist.append(usr)
+                
+        subscribers[lang] = userlist
+
 def handle_file_attached(event):
     """Handles event sent when a file is attached to a page"""
     
     request = event.request
-    page = Page(request, event.pagename) 
+    server = request.cfg.notification_server
+    page = Page(request, event.pagename)
     
     subscribers = page.getSubscribers(request, return_users=1)
+    _filter_subscriber_list(event, subscribers)
+    
     page_change("attachment_added", request, page, subscribers, attach_name=event.attachment_name, attach_size=event.size)
 
         
@@ -77,9 +94,12 @@
     """ Handles events related to page changes """
     
     request = event.request
+    server = request.cfg.notification_server
     page = event.page
     
-    subscribers = page.getSubscribers(request, return_users=1, trivial=event.trivial)
+    subscribers = page.getSubscribers(request, return_users=1, trivial=event.trivial)    
+    _filter_subscriber_list(event, subscribers)
+
     page_change("page_changed", request, page, subscribers, revisions=page.getRevList(), comment=event.comment)
     
 
@@ -87,10 +107,37 @@
     """Handles event sent when a page is deleted"""
     
     request = event.request
+    server = request.cfg.notification_server
     page = event.page
     
     subscribers = page.getSubscribers(request, return_users=1)
+    _filter_subscriber_list(event, subscribers)
+    
     page_change("page_deleted", request, page, subscribers)
+
+
+def handle_user_created(event):
+    """Handles an event sent when a new user is being created"""
+    
+    user_ids = getUserList(event.request)
+    jids = []
+    event_name = event.__class__.__name__
+    msg = u"""Dear Superuser, a new user has just been created. Details follow:
+    User name: %s
+    Email address: %s
+    """
+
+    email = event.user.email or u"NOT SET"
+    
+    for id in user_ids:
+        usr = User(event.request, id=id)
+        
+        # Currently send this only to super users
+        # TODO: make it possible to disable this notification
+        if usr.isSuperUser() and usr.jid and event_name in usr.subscribed_events:
+            jids.append(usr.jid)
+            
+    send_notification(event.request, jids, msg % (event.user.name, email))
     
 
 def page_change(type, request, page, subscribers, **kwargs):
@@ -101,7 +148,7 @@
         # send notifications to all subscribers
         results = [_('Status of sending notifications:')]
         for lang in subscribers:
-            jids = [u.jid for u in subscribers[lang]]
+            jids = [u.jid for u in subscribers[lang] if u.jid]
             names = [u.name for u in subscribers[lang]]
             msg = page_change_message(type, request, page, lang, **kwargs)
             jabberok, status = send_notification(request, jids, msg)
@@ -126,8 +173,10 @@
     @param trivial: the change is marked as trivial
     """
     _ = request.getText
+    server = request.cfg.notification_server
     
     for jid in jids:
+                
         # FIXME: stops sending notifications on first error
         try:
             server.send_notification(request.cfg.secret, jid, message)
--- a/MoinMoin/user.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/user.py	Mon Jun 18 21:43:28 2007 +0200
@@ -315,6 +315,7 @@
         self.datetime_fmt = ""
         self.quicklinks = self._cfg.quicklinks_default
         self.subscribed_pages = self._cfg.subscribed_pages_default
+        self.subscribed_events = self._cfg.subscribed_events_default
         self.theme_name = self._cfg.theme_default
         self.editor_default = self._cfg.editor_default
         self.editor_ui = self._cfg.editor_ui
@@ -422,7 +423,7 @@
                         val = decodeDict(val)
                     # for compatibility reading old files, keep these explicit
                     # we will store them with [] appended
-                    elif key in ['quicklinks', 'subscribed_pages']:
+                    elif key in ['quicklinks', 'subscribed_pages', 'subscribed_events']:
                         val = decodeList(val)
                     user_data[key] = val
             except ValueError:
--- a/MoinMoin/userform.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/userform.py	Mon Jun 18 21:43:28 2007 +0200
@@ -9,7 +9,7 @@
 
 import time
 from MoinMoin import user, util, wikiutil
-from MoinMoin.events import send_event, JabberIDSetEvent, JabberIDUnsetEvent
+import MoinMoin.events as events
 from MoinMoin.widget import html
 
 _debug = 0
@@ -134,6 +134,10 @@
 
         # save data
         theuser.save()
+        
+        user_created = events.UserCreatedEvent(self.request, theuser)
+        events.send_event(user_created)
+        
         if form.has_key('create_and_mail'):
             theuser.mailAccountData()
 
@@ -233,7 +237,7 @@
                     
         if not 'jid' in theuser.auth_attribs:
             # try to get the jid
-            jid = wikiutil.clean_input(form.get('jid', [theuser.jid])[0]).strip()
+            jid = wikiutil.clean_input(form.get('jid', "")).strip()
             
             jid_changed = theuser.jid != jid
             previous_jid = theuser.jid           
@@ -245,10 +249,10 @@
                     return _("This jabber id already belongs to somebody else.")
             
             if jid_changed:
-                set_event = JabberIDSetEvent(self.request, theuser.jid)
-                unset_event = JabberIDUnsetEvent(self.request, previous_jid)
-                send_event(set_event)
-                send_event(unset_event)
+                set_event = events.JabberIDSetEvent(self.request, theuser.jid)
+                unset_event = events.JabberIDUnsetEvent(self.request, previous_jid)
+                events.send_event(unset_event)
+                events.send_event(set_event)
 
         if not 'aliasname' in theuser.auth_attribs:
             # aliasname
@@ -326,6 +330,10 @@
 
         # subscription for page change notification
         theuser.subscribed_pages = self._decode_pagelist('subscribed_pages')
+        
+        # subscription to various events
+        available = events.get_subscribable_events()
+        theuser.subscribed_events = [ev for ev in form.get('events')]
 
         # save data
         theuser.save()
@@ -462,6 +470,24 @@
             options.append((theme, theme))
 
         return util.web.makeSelection('theme_name', options, cur_theme)
+    
+    def _event_select(self):
+        """ Create event subscription list. """
+        
+        event_list = events.get_subscribable_events()
+        selected = self.request.user.subscribed_events
+        super = self.request.user.isSuperUser()
+        
+        # Create a list of (value, name) tuples for display in <select>
+        # Only include super-user visible events if current user has these rights.
+        # It's cosmetic - the check for super-user rights should be performed
+        # in event handling code as well!
+        allowed = []
+        for key in event_list.keys():
+            if not event_list[key]['superuser'] or super:
+                allowed.append((key, event_list[key]['desc']))
+        
+        return util.web.makeMultiSelection('events', allowed, selectedvals=selected)
 
     def _editor_default_select(self):
         """ Create editor selection. """
@@ -595,6 +621,10 @@
                 html.TEXTAREA(name="quicklinks", rows="6", cols="50")
                     .append('\n'.join(self.request.user.getQuickLinks())),
             ], valign="top")
+            
+            # FIXME: this depends on Jabber ATM, but may not do so in the future
+            if self.cfg.jabber_enabled:
+                self.make_row(_('Subscribed events'), [self._event_select()])
 
             # subscribed pages
             if self.cfg.mail_enabled:
--- a/MoinMoin/util/web.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/MoinMoin/util/web.py	Mon Jun 18 21:43:28 2007 +0200
@@ -44,6 +44,24 @@
 
     return result
 
+def makeMultiSelection(name, values, selectedvals=None, size=5):
+    """Make a HTML multiple <select> element with named `name` from a value list.
+    
+    The list can either be a list of strings, or a list of (value, label) tuples.
+    `selectedvals` is a list of values that should be pre-selected.
+    
+    """
+    from MoinMoin.widget import html
+    result = html.SELECT(name=name, size="%d" % int(size), multiple=True)
+    for val in values:
+        if not isinstance(val, type(())):
+            val = (val, val)
+        result.append(html.OPTION(
+            value=val[0], selected=(val[0] in selectedvals))
+            .append(html.Text(val[1]))
+        )
+    
+    return result
 
 class Color:
     """ RGB-Triple that automatically converts from and to
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jabberbot/config.py	Mon Jun 18 21:43:28 2007 +0200
@@ -0,0 +1,45 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - jabber bot configuration file
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+class BotConfig:
+    # Node name (username on the jabber server) to be used
+    xmpp_node = u"moinbot"
+
+    # Server to be used
+    xmpp_server = u"jabber.example.com"
+    
+    # Port to connect to or None, if default
+    xmpp_port = None
+
+    # Resource name (entity@server/resource) to be used
+    xmpp_resource = u"wiki"
+
+    # Password used to connect to the xmpp server
+    xmpp_password = u""
+    
+    # Status message that the bot should set
+    xmpp_status = u"Ready to serve!"
+        
+    # Set to True if bot should be verbose about actions it 
+    # is performing. Useful for debuging.
+    verbose = True
+
+    # Interface to listen on for XML RPC traffic
+    xmlrpc_host = u"localhost"
+    
+    # Port to listen on for XML RPC traffic
+    xmlrpc_port = 8000
+    
+    # Url where wiki is located (for reverse XML RPC traffic)
+    wiki_url = u"http://localhost:8080/"
+    
+    # A secret shared with Wiki , must be the same in both
+    # configs for communication to work.
+    #
+    # CHANGE IT TO A LONG RANDOM STRING, OR YOU WILL HAVE A SECURITY ISSUE!
+    secret = u"8yFAS(E-,.c-93adj'uff;3AW#(UDJ,.df3OA($HG"
--- a/jabberbot/xmppbot.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/jabberbot/xmppbot.py	Mon Jun 18 21:43:28 2007 +0200
@@ -173,6 +173,11 @@
         @param ignore_dnd: if command results in user interaction, should DnD be ignored?
         
         """
+        
+        if not command.jid:
+            self.log("Received a command with empty jid, looks like a bug!")
+            return
+        
         # Handle normal notifications
         if isinstance(command, cmd.NotificationCommand):
             jid = JID(node_or_jid=command.jid)
@@ -240,8 +245,9 @@
         """Sends a message
         
         @param jid: JID to send the message to
-        @param text: message's body
+        @param text: message's body: 
         @param type: message type, as defined in RFC
+        @type jid: pyxmpp.jid.JID
         
         """
         message = Message(to_jid=jid, body=text, stanza_type=msg_type)
--- a/wiki/config/more_samples/jabber_wikiconfig.py	Mon Jun 18 21:21:06 2007 +0200
+++ b/wiki/config/more_samples/jabber_wikiconfig.py	Mon Jun 18 21:43:28 2007 +0200
@@ -167,7 +167,7 @@
     # Notification bot options ------------------------------------------
     
     # Host and port on which the notification bot runs
-    bot_host = u"localhost:8000"
+    notification_bot_uri = u"http://localhost:8000"
     
     # A secret shared with notification bot, must be the same in both
     # configs for communication to work.