changeset 2699:98f7c5c64e91

Merge main
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Sun, 19 Aug 2007 17:01:35 +0200
parents 701a36397035 (current diff) 0a6f74a01646 (diff)
children a0b85757e5d0
files
diffstat 10 files changed, 750 insertions(+), 236 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/PageEditor.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/MoinMoin/PageEditor.py	Sun Aug 19 17:01:35 2007 +0200
@@ -63,6 +63,8 @@
     # exceptions for .saveText()
     class SaveError(error.Error):
         pass
+    class RevertError(SaveError):
+        pass
     class AccessDenied(SaveError):
         pass
     class Immutable(AccessDenied):
@@ -627,6 +629,41 @@
             else:
                 return False, _('Could not rename page because of file system error: %s.') % unicode(err)
 
+
+    def revertPage(self, revision):
+        """ Reverts page to the given revision
+
+        @param revision: revision to revert to
+        @type revision: int
+
+        """
+        _ = self.request.getText
+
+        if not self.request.user.may.revert(self.page_name):
+            raise self.RevertError(_('You are not allowed to revert this page!'))
+        elif revision is None:
+            raise self.RevertError(_('You were viewing the current revision of this page when you called the revert action. '
+                    'If you want to revert to an older revision, first view that older revision and '
+                    'then call revert to this (older) revision again.'))
+        else:
+            revstr = '%08d' % revision
+            pg = Page(self.request, self.page_name, rev=revision)
+            msg = self.saveText(pg.get_raw_body(), 0, extra=revstr, action="SAVE/REVERT", notify=False)
+
+            # Remove cache entry (if exists)
+            from MoinMoin import caching
+            pg = Page(self.request, self.page_name)
+            key = self.request.form.get('key', ['text_html'])[0]
+            caching.CacheEntry(self.request, pg, key, scope='item').remove()
+            caching.CacheEntry(self.request, pg, "pagelinks", scope='item').remove()
+
+            # Notify observers
+            from MoinMoin.events import PageRevertedEvent, send_event
+            e = PageRevertedEvent(self.request, self.page_name, revision, revstr)
+            send_event(e)
+
+            return msg
+
     def deletePage(self, comment=None):
         """ Delete the current version of the page (making a backup before deletion
             and keeping the backups, logs and attachments).
--- a/MoinMoin/action/revert.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/MoinMoin/action/revert.py	Sun Aug 19 17:01:35 2007 +0200
@@ -11,41 +11,13 @@
 def execute(pagename, request):
     """ restore another revision of a page as a new current revision """
     from MoinMoin.PageEditor import PageEditor
-    _ = request.getText
-    msg = None
     rev = request.rev
-    pg = Page(request, pagename, rev=rev)
-
-    if not request.user.may.revert(pagename):
-        msg = _('You are not allowed to revert this page!')
-    elif rev is None:
-        msg = _('You were viewing the current revision of this page when you called the revert action. '
-                'If you want to revert to an older revision, first view that older revision and '
-                'then call revert to this (older) revision again.')
-    else:
-        newpg = PageEditor(request, pagename)
+    pg = PageEditor(request, pagename)
 
-        revstr = '%08d' % rev
-        try:
-            msg = newpg.saveText(pg.get_raw_body(), 0, extra=revstr, action="SAVE/REVERT", notify=False)
-            pg = newpg
-        except newpg.SaveError, msg:
-            msg = unicode(msg)
-        request.reset()
-
-        key = request.form.get('key', ['text_html'])[0]
+    try:
+        msg = pg.revertPage(rev)
+    except PageEditor.RevertError, error:
+        msg = unicode(error)
 
-        # Remove cache entry (if exists)
-        pg = Page(request, pagename)
-        from MoinMoin import caching
-        caching.CacheEntry(request, pg, key, scope='item').remove()
-        caching.CacheEntry(request, pg, "pagelinks", scope='item').remove()
-
-        # Notify observers
-        from MoinMoin.events import PageRevertedEvent, send_event
-        e = PageRevertedEvent(request, pagename, request.rev, revstr)
-        send_event(e)
-
-    if request.action != "xmlrpc":
-        pg.send_page(msg=msg)
-
+    request.reset()
+    pg.send_page(msg=msg)
--- a/MoinMoin/events/jabbernotify.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/MoinMoin/events/jabbernotify.py	Sun Aug 19 17:01:35 2007 +0200
@@ -88,8 +88,10 @@
                   {'url': pagelink, 'description': _("Page link")}]
 
         jids = [usr.jid for usr in subscribers[lang]]
+        data['url_list'] = links
+        data['action'] = "file_attached"
 
-        if send_notification(request, jids, data['body'], data['subject'], links, "file_attached"):
+        if send_notification(request, jids, data):
             names.update(recipients)
 
     return notification.Success(names)
@@ -124,8 +126,12 @@
     old_name = event.old_page.page_name
 
     subscribers = page.getSubscribers(request, return_users=1)
+
+    # Change request's page so that we filter subscribers of the OLD page
+    request.page = event.old_page
     notification.filter_subscriber_list(event, subscribers, True)
-    return page_change("page_renamed", request, page, subscribers, oldname=old_name)
+    request.page = page
+    return page_change("page_renamed", request, page, subscribers, old_name=old_name)
 
 
 def handle_user_created(event):
@@ -165,7 +171,13 @@
             msg = notification.page_change_message(change_type, request, page, lang, **kwargs)
             page_url = request.getQualifiedURL(page.url(request, relative=False))
             url = {'url': page_url, 'description': _("Changed page")}
-            result = send_notification(request, jids, msg, _("Page changed"), [url], "page_changed")
+            data = {'action': change_type, 'subject': _('Page changed'),
+                            'url_list': [url], 'text': msg['text'], 'diff': msg.get('diff', ''),
+                            'comment': msg.get('comment', ''), 'editor': msg['editor'],
+                            'old_name': msg.get('old_name', ''), 'page_name': msg.get('page_name', ''),
+                            'revision': msg.get('revision', '')}
+
+            result = send_notification(request, jids, data)
 
             if result:
                 recipients.update(names)
@@ -173,7 +185,7 @@
         if recipients:
             return notification.Success(recipients)
 
-def send_notification(request, jids, message, subject="", url_list=[], action=""):
+def send_notification(request, jids, notification):
     """ Send notifications for a single language.
 
     @param jids: an iterable of Jabber IDs to send the message to
@@ -186,12 +198,14 @@
     _ = request.getText
     server = request.cfg.notification_server
 
-    if type(url_list) != list:
+    if type(notification['url_list']) != list:
         raise ValueError("url_list must be of type list!")
 
+    if type(notification) != dict:
+        raise ValueError("notification must be of type dict!")
+
     try:
-        cmd_data = {'text': message, 'subject': subject, 'url_list': url_list, 'action': action}
-        server.send_notification(request.cfg.secret, jids, cmd_data)
+        server.send_notification(request.cfg.secret, jids, notification)
         return True
     except xmlrpclib.Error, err:
         ev.logger.error(_("XML RPC error: %s"), str(err))
--- a/MoinMoin/events/notification.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/MoinMoin/events/notification.py	Sun Aug 19 17:01:35 2007 +0200
@@ -60,12 +60,12 @@
 def page_change_message(msgtype, request, page, lang, **kwargs):
     """Prepare a notification text for a page change of given type
 
-    @param msgtype: a type of message to send (page_changed, attachment_added, ...)
+    @param msgtype: a type of message to send (page_changed, page_renamed, ...)
     @type msgtype: str or unicode
     @param **kwargs: a dictionary of additional parameters, which depend on msgtype
 
-    @return: a formatted, ready to send message
-    @rtype: unicode
+    @return: dictionary containing data about the changed page
+    @rtype: dict
 
     """
     from MoinMoin.action.AttachFile import getAttachUrl
@@ -73,6 +73,7 @@
     _ = request.getText
     page._ = lambda s, formatted=True, r=request, l=lang: r.getText(s, formatted=formatted, lang=l)
     querystr = {}
+    changes = {'page_name': page.page_name, 'revision': str(page.getRevList()[0])}
 
     if msgtype == "page_changed":
         revisions = kwargs['revisions']
@@ -84,7 +85,7 @@
     pagelink = page_link(request, page, querystr)
 
     if msgtype == "page_changed":
-        msg_body = _("Dear Wiki user,\n\n"
+        changes['text'] = _("Dear Wiki user,\n\n"
         'You have subscribed to a wiki page or wiki category on "%(sitename)s" for change notification.\n\n'
         'The "%(pagename)s" page has been changed by %(editor)s:\n\n', formatted=False) % {
             'pagename': page.page_name,
@@ -94,45 +95,45 @@
 
         # append a diff (or append full page text if there is no diff)
         if len(revisions) < 2:
-            messageBody = msg_body + \
-                _("New page:\n", formatted=False) + \
-                page.get_raw_body()
+            changes['diff'] = _("New page:\n", formatted=False) + page.get_raw_body()
         else:
             lines = wikiutil.pagediff(request, page.page_name, revisions[1],
                                       page.page_name, revisions[0])
             if lines:
-                msg_body = msg_body + "%s\n%s\n" % (("-" * 78), '\n'.join(lines))
+                changes['diff'] = '\n'.join(lines)
             else:
-                msg_body = msg_body + _("No differences found!\n", formatted=False)
+                changes['diff'] = _("No differences found!\n", formatted=False)
 
     elif msgtype == "page_deleted":
-        msg_body = _("Dear wiki user,\n\n"
+        changes['text'] = _("Dear wiki user,\n\n"
             'You have subscribed to a wiki page "%(sitename)s" for change notification.\n\n'
-            'The page "%(pagename)" has been deleted by %(editor)s:\n\n', formatted=False) % {
+            'The page "%(pagename)s" has been deleted by %(editor)s:\n\n', formatted=False) % {
                 'pagename': page.page_name,
                 'editor': page.uid_override or user.getUserIdentification(request),
                 'sitename': page.cfg.sitename or request.getBaseURL(),
         }
 
     elif msgtype == "page_renamed":
-        msg_body = _("Dear wiki user,\n\n"
+        changes['text'] = _("Dear wiki user,\n\n"
             'You have subscribed to a wiki page "%(sitename)s" for change notification.\n\n'
-            'The page "%(pagename)" has been renamed from %(oldname)s by %(editor)s:\n',
+            'The page "%(pagename)s" has been renamed from "%(oldname)s" by %(editor)s:\n',
             formatted=False) % {
                 'editor': page.uid_override or user.getUserIdentification(request),
                 'pagename': page.page_name,
                 'sitename': page.cfg.sitename or request.getBaseURL(),
                 'oldname': kwargs['old_name']
         }
+
+        changes['old_name'] = kwargs['old_name']
+
     else:
         raise UnknownChangeType()
 
+    changes['editor'] = page.uid_override or user.getUserIdentification(request)
     if 'comment' in kwargs and kwargs['comment']:
-        msg_body = msg_body + \
-            _("The comment on the change is:\n%(comment)s",
-              formatted=False) % {'comment': kwargs['comment']}
+        changes['comment'] = kwargs['comment']
 
-    return msg_body
+    return changes
 
 def user_created_message(request, sitename, username, email):
     """Formats a message used to notify about accounts being created
@@ -155,17 +156,18 @@
     """Formats a message used to notify about new attachments
 
     @param _: a gettext function
-    @return: a dict containing message body and subject
+    @return: a dict with notification data
 
     """
     page = Page(request, page_name)
+    data = {}
 
-    subject = _("New attachment added to page %(pagename)s on %(sitename)s") % {
+    data['subject'] = _("New attachment added to page %(pagename)s on %(sitename)s") % {
                 'pagename': page_name,
                 'sitename': request.cfg.sitename or request.getBaseURL(),
                 }
 
-    body = _("Dear Wiki user,\n\n"
+    data['text'] = _("Dear Wiki user,\n\n"
     'You have subscribed to a wiki page "%(page_name)s" for change notification. '
     "An attachment has been added to that page by %(editor)s. "
     "Following detailed information is available:\n\n"
@@ -177,7 +179,12 @@
         'attach_size': attach_size,
     }
 
-    return {'body': body, 'subject': subject}
+    data['editor'] = user.getUserIdentification(request)
+    data['page_name'] = page_name
+    data['attach_size'] = attach_size
+    data['attach_name'] = attach_name
+
+    return data
 
 
 # XXX: clean up this method to take a notification type instead of bool for_jabber
--- a/MoinMoin/xmlrpc/__init__.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/MoinMoin/xmlrpc/__init__.py	Sun Aug 19 17:01:35 2007 +0200
@@ -570,13 +570,16 @@
         if not self.request.user.may.write(pagename):
             return xmlrpclib.Fault(1, "You are not allowed to edit this page")
 
-        from MoinMoin.action import revert
-        self.request.rev = int(self._instr(revision))
-        msg = revert.execute(pagename, self.request)
-        if msg:
-            return xmlrpclib.Fault(1, "Revert failed: %s" % (msg, ))
-        else:
-            return xmlrpclib.Boolean(1)
+        from MoinMoin.PageEditor import PageEditor
+        rev = int(self._instr(revision))
+        editor = PageEditor(self.request, pagename)
+
+        try:
+            editor.revertPage(rev)
+        except PageEditor.SaveError, error:
+            return xmlrpclib.Fault(1, "Revert failed: %s" % (str(error), ))
+
+        return xmlrpclib.Boolean(1)
 
     def xmlrpc_searchPages(self, query_string):
         """ Searches pages for query_string.
--- a/docs/CHANGES.jabber	Sun Aug 19 16:57:38 2007 +0200
+++ b/docs/CHANGES.jabber	Sun Aug 19 17:01:35 2007 +0200
@@ -5,83 +5,77 @@
 using until now. Otherwise you might miss some important upgrading and
 configuration hints.
 
-Overview:
-===============================================
-   MoinMoin 1.7 includes a brand new notification system based on
-   a separate process running a Jabber/XMPP notification bot. See
-   http://www.jabber.org and http://www.xmpp.org for more information
-   on this protocol.
-
-   The bot can be used to send notifications about various events
-   occuring in your Wiki, or to work with the Wiki interactively.
-
-   As it's a separate process, it doesn't block waiting for all
-   notifications to be sent, to this solution should be suitable for
-   large sites that have many users subscribed to particular changes.
-
-Features:
-===============================================
-  * Notification sent when pages are changed in various ways (content
-    change, page rename, deletion, page copy), users being created
-    (visible to super user only!), attachments being added and users
-    subscribing to pages...
-
-  * Users can choose which events they're interested in being notified
-    about. This applied both to (old) email and jabber notifications.
-
-  * Interactive Jabber bot allows to perform various simple operations
-    on a Wiki from within your IM client (possibly in response to
-    received notification). This includes getting raw and html-formatted
-    page contents, querying detailed page information (last author, 
-    revision, date of the last change...), getting a list of pages,
-    performing searches and reverts.
-
-    The bot uses Data Forms (XEP-004) and Out of Band Data (XEP-066)
-    extensions if they're supported by the client to further extend
-    available communication options
-    
-Getting help:
-===============================================
-  There's a sample wikiconfig in MOINDIR/wiki/config/more_examples
+Version 1.7.current:
+  New Features:
+    * The first version that supports jabber notifications.
+      * Overview:
+	MoinMoin 1.7 includes a brand new notification system based on
+	a separate process running a Jabber/XMPP notification bot. See
+	http://www.jabber.org and http://www.xmpp.org for more information
+	on this protocol.
 
-  You can read more about the notification bot on following pages:
-    * http://moinmo.in/JabberSupport
-    * http://moinmo.in/MoinMoinTodo/Release_1.7/HelpOnNotification
-
-Known main issues with jabber bot:
-===============================================
-  * You need a development version of pyxmpp, 1.0 won't work. You can
-    get it directly from svn repository with:
-
-    svn checkout http://pyxmpp.jajcus.net/svn/pyxmpp/trunk pyxmpp
-
-    Add the resulting `pyxmpp` directory to your PYTHONPATH or perform
-    a "full installation" as described on http://pyxmpp.jajcus.net/:
-
-    To build the package just invoke:
-    python setup.py build
-
-    To install it:
-    python setup.py install
+	The bot can be used to send notifications about various events
+	occuring in your Wiki, or to work with the Wiki interactively.
 
-    If you had some older version of PyXMPP it is better to uninstall it 
-    (delete pyxmpp subdirectory os your site-packages directory) before 
-    installing this one or things may not work correctly.
-
-    You may also try:
-    make
-
-    and:
-    make install
+	As it's a separate process, it doesn't block waiting for all
+	notifications to be sent, to this solution should be suitable for
+	large sites that have many users subscribed to particular changes.
 
-    instead.
+      * Features:
+	* Notification sent when pages are changed in various ways (content
+	  change, page rename, deletion, page copy), users being created
+	  (visible to super user only!), attachments being added and users
+	  subscribing to pages...
 
-  * Jabber servers usually have rather tight data rate limits, so if
-    your site generates a lot of traffic, the notification bot may become
-    unstable and/or unusable. If such condition occurs, you should
-    consider running your own Jabber/XMPP server with relaxed limits.
+	* Users can choose which events they're interested in being notified
+	  about. This applied both to (old) email and jabber notifications.
 
-Changes
-===============================================
-  Version 1.7.current:
-    The first version that supports jabber notifications.
+	* Interactive Jabber bot allows to perform various simple operations
+	  on a Wiki from within your IM client (possibly in response to
+	  received notification). This includes getting raw and html-formatted
+	  page contents, querying detailed page information (last author, 
+	  revision, date of the last change...), getting a list of pages,
+	  performing searches and reverts.
+
+	  The bot uses Data Forms (XEP-004) and Out of Band Data (XEP-066)
+	  extensions if they're supported by the client to further extend
+	  available communication options
+    
+       * Getting help:
+	 * There's a sample wikiconfig in MOINDIR/wiki/config/more_examples
+
+	 * You can read more about the notification bot on following pages:
+	     * http://moinmo.in/JabberSupport
+	     * http://moinmo.in/MoinMoinTodo/Release_1.7/HelpOnNotification
+
+       * Known main issues with jabber bot:
+	 * You need a development version of pyxmpp, 1.0 won't work. You can
+	   get it directly from svn repository with:
+
+	   svn checkout http://pyxmpp.jajcus.net/svn/pyxmpp/trunk pyxmpp
+
+	   Add the resulting `pyxmpp` directory to your PYTHONPATH or perform
+	   a "full installation" as described on http://pyxmpp.jajcus.net/:
+
+	   To build the package just invoke:
+	   python setup.py build
+
+	   To install it:
+	   python setup.py install
+
+	   If you had some older version of PyXMPP it is better to uninstall it 
+	   (delete pyxmpp subdirectory os your site-packages directory) before 
+	   installing this one or things may not work correctly.
+
+	   You may also try:
+	   make
+
+	   and:
+	   make install
+
+	   instead.
+
+	 * Jabber servers usually have rather tight data rate limits, so if
+	   your site generates a lot of traffic, the notification bot may become
+	   unstable and/or unusable. If such condition occurs, you should
+	   consider running your own Jabber/XMPP server with relaxed limits.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jabberbot/_tests/test_xmlrpcbot.py	Sun Aug 19 17:01:35 2007 +0200
@@ -0,0 +1,34 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - XMLRPC bot tests
+
+    @copyright: 2007 by Karol Nowak <grywacz@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+from Queue import Queue
+
+try:
+    import pyxmpp
+except ImportError:
+    py.test.skip("Skipping jabber bot tests - pyxmpp is not installed")
+
+import jabberbot.xmlrpcbot as xmlrpcbot
+from jabberbot.config import BotConfig
+
+
+class TestXMLRPCBotAPIs:
+    def setup_class(self):
+        self.queue_in = Queue()
+        self.queue_out = Queue()
+        self.bot = xmlrpcbot.XMLRPCClient(BotConfig, self.queue_in, self.queue_out)
+
+    def testReportError(self):
+        print "report_error() should put a command in the output queue"
+        self.bot.report_error(["dude@example.com"], "Error %(err)s!", data={'err': 'bar!'})
+        self.queue_out.get(False)
+
+    def testWanrNoCredentials(self):
+        print "warn_no_credentials() should put a command in the output queue"
+        self.bot.warn_no_credentials(["dude@example.com"])
+        self.queue_out.get(False)
+
--- a/jabberbot/commands.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/jabberbot/commands.py	Sun Aug 19 17:01:35 2007 +0200
@@ -83,14 +83,18 @@
     # Parameter list in a human-readable format
     parameter_list = u""
 
-    def __init__(self, jid):
+    def __init__(self, jid, presentation=u"text"):
         """A constructor
 
         @param jid: Jabber ID to send the reply to
+        @param presentation: how to display results; "text" or "dataforms"
         @type jid: unicode
+        @type presentation: unicode
+
         """
         self.jid = jid
         self.data = None
+        self.presentation = presentation
 
 class GetPage(BaseDataCommand):
 
@@ -123,8 +127,8 @@
     description = u"show detailed information about a page"
     parameter_list = u"pagename"
 
-    def __init__(self, jid, pagename):
-        BaseDataCommand.__init__(self, jid)
+    def __init__(self, jid, pagename, presentation=u"text"):
+        BaseDataCommand.__init__(self, jid, presentation)
         self.pagename = pagename
 
 class Search(BaseDataCommand):
--- a/jabberbot/xmlrpcbot.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/jabberbot/xmlrpcbot.py	Sun Aug 19 17:01:35 2007 +0200
@@ -134,6 +134,14 @@
             self.do_revert(command)
 
     def report_error(self, jid, text, data={}):
+        """Reports an internal error
+
+        @param jid: Jabber ID that should be informed about the error condition
+        @param text: description of the error
+        @param data: dictionary used to substitute strings in translated message
+        @type data: dict
+
+        """
         # Dummy function, so that the string appears in a .po file
         _ = lambda x: x
 
--- a/jabberbot/xmppbot.py	Sun Aug 19 16:57:38 2007 +0200
+++ b/jabberbot/xmppbot.py	Sun Aug 19 17:01:35 2007 +0200
@@ -66,7 +66,7 @@
         @param priority: priority of the given resource
 
         """
-        self.resources[resource] = {'show': show, 'priority': priority, supports: []}
+        self.resources[resource] = {'show': show, 'priority': priority, 'supports': []}
         self.last_online = None
 
     def set_supports(self, resource, extension):
@@ -80,7 +80,7 @@
         priority among currently connected.
 
         """
-        if resource:
+        if resource and resource in self.resources:
             return extension in self.resources[resource]['supports']
         else:
             resource = self.max_prio_resource()
@@ -156,7 +156,7 @@
     def __str__(self):
         retval = "%s (%s) has %d queued messages"
         res = ", ".join([name + " is " + res['show'] for name, res in self.resources.items()])
-        return retval % (self.jid.as_utf8(), res, len(self.messages))
+        return retval % (self.jid.as_unicode(), res, len(self.messages))
 
 class XMPPBot(Client, Thread):
     """A simple XMPP bot"""
@@ -255,9 +255,9 @@
         """
         language = "en"
         if isinstance(jid, str) or isinstance(jid, unicode):
-            jid = JID(jid).bare().as_utf8()
+            jid = JID(jid).bare().as_unicode()
         else:
-            jid = jid.bare().as_utf8()
+            jid = jid.bare().as_unicode()
 
         if jid in self.contacts:
             language = self.contacts[jid].language
@@ -289,22 +289,22 @@
         # Handle normal notifications
         if isinstance(command, cmd.NotificationCommand):
             cmd_data = command.notification
+            original_text = cmd_data.get('text', '')
+            original_subject = cmd_data.get('subject', '')
 
             for recipient in command.jids:
                 jid = JID(recipient)
-                jid_text = jid.bare().as_utf8()
-
-                text = cmd_data['text']
-                subject = cmd_data.get('subject', '')
-                msg_data = command.notification
+                jid_text = jid.bare().as_unicode()
 
                 if isinstance(command, cmd.NotificationCommandI18n):
                     # Translate&interpolate the message with data
                     gettext_func = self.get_text(jid_text)
                     text, subject = command.translate(gettext_func)
-                    msg_data = {'text': text, 'subject': subject,
-                                'url_list': cmd_data.get('url_list', []),
-                                'action': cmd_data.get('action', '')}
+                    cmd_data['text'] = text
+                    cmd_data['subject'] = subject
+                else:
+                    cmd_data['text'] = original_text
+                    cmd_data['subject'] = original_subject
 
                 # Check if contact is DoNotDisturb.
                 # If so, queue the message for delayed delivery.
@@ -314,14 +314,19 @@
                         contact.messages.append(command)
                         return
 
-                    if contact.supports(jid.resource, u"jabber:x:data"):
-                        action = msg_data.get('action', '')
-                        if action == "page_changed":
-                            self.send_change_form(jid, msg_data)
-                        else:
-                            self.send_message(jid, msg_data, command.msg_type)
+                action = cmd_data.get('action', '')
+                if action == u'page_changed':
+                    self.handle_changed_action(cmd_data, jid, contact)
+                elif action == u'page_deleted':
+                    self.handle_deleted_action(cmd_data, jid, contact)
+                elif action == u'file_attached':
+                    self.handle_attached_action(cmd_data, jid, contact)
+                elif action == u'page_renamed':
+                    self.handle_renamed_action(cmd_data, jid, contact)
+                elif action == u'user_created':
+                    self.handle_user_created_action(cmd_data, jid, contact)
                 else:
-                    self.send_message(jid, msg_data, command.msg_type)
+                    self.send_message(jid, cmd_data, command.msg_type)
 
             return
 
@@ -349,32 +354,7 @@
             self.send_message(command.jid, {'text': msg % (pagelist, )})
 
         elif isinstance(command, cmd.GetPageInfo):
-            intro = _("""Following detailed information on page "%(pagename)s" \
-is available:""")
-
-            if command.data['author'].startswith("Self:"):
-                author = command.data['author'][5:]
-            else:
-                author = command.data['author']
-
-            datestr = str(command.data['lastModified'])
-            date = u"%(year)s-%(month)s-%(day)s at %(time)s" % {
-                        'year': datestr[:4],
-                        'month': datestr[4:6],
-                        'day': datestr[6:8],
-                        'time': datestr[9:17],
-            }
-
-            msg = _("""Last author: %(author)s
-Last modification: %(modification)s
-Current version: %(version)s""") % {
-             'author': author,
-             'modification': date,
-             'version': command.data['version'],
-            }
-
-            self.send_message(command.jid, {'text': intro % {'pagename': command.pagename}})
-            self.send_message(command.jid, {'text': msg})
+            self.handle_page_info(command)
 
         elif isinstance(command, cmd.GetUserLanguage):
             if command.jid in self.contacts:
@@ -405,26 +385,106 @@
                 self.send_message(command.jid, data, u"chat")
             else:
                 form_title = _("Search results").encode("utf-8")
+                help_form = _("Submit this form to perform a wiki search").encode("utf-8")
+                form = forms.Form(xmlnode_or_type="result", title=form_title, instructions=help_form)
 
-                warnings = []
+                action_label = _("What to do next")
+                do_nothing = forms.Option("n", _("Do nothing"))
+                search_again = forms.Option("s", _("Search again"))
+
                 for no, warning in enumerate(warnings):
-                    field = forms.Field(name="warning", field_type="fixed", value=warning)
-                    warnings.append(forms.Item([field]))
-
-                reported = [forms.Field(name="url", field_type="text-single"), forms.Field(name="description", field_type="text-single")]
-                if warnings:
-                    reported.append(forms.Field(name="warning", field_type="fixed"))
-
-                form = forms.Form(xmlnode_or_type="result", title=form_title, reported_fields=reported)
+                    form.add_field(name="warning", field_type="fixed", value=warning)
 
                 for no, result in enumerate(results):
-                    url = forms.Field(name="url", value=result["url"], field_type="text-single")
-                    description = forms.Field(name="description", value=result["description"], field_type="text-single")
-                    item = forms.Item([url, description])
-                    form.add_item(item)
+                    field_name = "url%d" % (no, )
+                    form.add_field(name=field_name, value=unicode(result["url"]), label=result["description"].encode("utf-8"), field_type="text-single")
+
+                # Selection of a following action
+                form.add_field(name="options", field_type="list-single", options=[do_nothing, search_again], label=action_label)
 
                 self.send_form(command.jid, form, _("Search results"))
 
+    def handle_changed_action(self, cmd_data, jid, contact):
+        """Handles a notification command with 'page_changed' action
+
+        @param cmd_data: notification command data
+        @param jid: jid to send the notification to
+        @param contact: a roster contact
+        @type cmd_data: dict
+        @type jid: pyxmpp.jid.JID
+        @type contact: Contact
+
+        """
+        if contact and contact.supports(jid.resource, u"jabber:x:data"):
+            self.send_change_form(jid.as_unicode(), cmd_data)
+            return
+        else:
+            self.send_change_text(jid.as_unicode(), cmd_data)
+
+    def handle_deleted_action(self, cmd_data, jid, contact):
+        """Handles a notification cmd_data with 'page_deleted' action
+
+        @param cmd_data: notification cmd_data
+        @param jid: jid to send the notification to
+        @param contact: a roster contact
+        @type cmd_data: dict
+        @type jid: pyxmpp.jid.JID
+        @type contact: Contact
+
+        """
+        if contact and contact.supports(jid.resource, u"jabber:x:data"):
+            self.send_deleted_form(jid.as_unicode(), cmd_data)
+            return
+        else:
+            self.send_deleted_text(jid.as_unicode(), cmd_data)
+
+
+    def handle_attached_action(self, cmd_data, jid, contact):
+        """Handles a notification cmd_data with 'file_attached' action
+
+        @param cmd_data: notification cmd_data
+        @param jid: jid to send the notification to
+        @param contact: a roster contact
+        @type cmd_data: dict
+        @type jid: pyxmpp.jid.JID
+        @type contact: Contact
+
+        """
+        if contact and contact.supports(jid.resource, u"jabber:x:data"):
+            self.send_attached_form(jid.as_unicode(), cmd_data)
+            return
+        else:
+            self.send_attached_text(jid.as_unicode(), cmd_data)
+
+    def handle_renamed_action(self, cmd_data, jid, contact):
+        """Handles a notification cmd_data with 'page_renamed' action
+
+        @param cmd_data: notification cmd_data
+        @param jid: jid to send the notification to
+        @param contact: a roster contact
+        @type cmd_data: dict
+        @type jid: pyxmpp.jid.JID
+        @type contact: Contact
+
+        """
+        if contact and contact.supports(jid.resource, u"jabber:x:data"):
+            self.send_renamed_form(jid.as_unicode(), cmd_data)
+            return
+        else:
+            self.send_renamed_text(jid.as_unicode(), cmd_data)
+
+    def handle_user_created_action(self, cmd_data, jid, contact):
+        """Handles a notification cmd_data with 'user_created' action
+
+        @param cmd_data: notification cmd_data
+        @param jid: jid to send the notification to
+        @param contact: a roster contact
+        @type cmd_data: dict
+        @type jid: pyxmpp.jid.JID
+        @type contact: Contact
+
+        """
+        raise NotImplemented()
 
     def ask_for_subscription(self, jid):
         """Sends a <presence/> stanza with type="subscribe"
@@ -463,7 +523,7 @@
         jid = JID(jid_text)
 
         if data.has_key('url_list') and data['url_list']:
-            jid_bare = jid.bare().as_utf8()
+            jid_bare = jid.bare().as_unicode()
             contact = self.contacts.get(jid_bare, None)
             if contact and contact.supports(jid.resource, u'jabber:x:oob'):
                 use_oob = True
@@ -482,15 +542,17 @@
 
         self.get_stream().send(message)
 
-    def send_form(self, jid, form, subject):
+    def send_form(self, jid, form, subject, url_list=[]):
         """Send a data form
 
         @param jid: jid to send the form to (full)
         @param form: the form to send
         @param subject: subject of the message
+        @param url_list: list of urls to use with OOB
         @type jid: unicode
         @type form: pyxmpp.jabber.dataforms.Form
         @type subject: unicode
+        @type url_list: list
 
         """
         if not isinstance(form, forms.Form):
@@ -498,9 +560,12 @@
 
         _ = self.get_text(JID(jid).bare().as_unicode())
 
-        warning = _("If you see this, your client probably doesn't support Data Forms.")
-        message = Message(to_jid=jid, body=warning, subject=subject)
+        message = Message(to_jid=jid, subject=subject)
         message.add_content(form)
+
+        if url_list:
+            oob.add_urls(message, url_list)
+
         self.get_stream().send(message)
 
     def send_search_form(self, jid):
@@ -531,29 +596,388 @@
         self.send_form(jid, form, _("Wiki search"))
 
     def send_change_form(self, jid, msg_data):
-        """Sends a page change notification using Data Forms"""
+        """Sends a page change notification using Data Forms
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
         _ = self.get_text(jid)
 
         form_title = _("Page changed notification").encode("utf-8")
         instructions = _("Submit this form with a specified action to continue.").encode("utf-8")
-        url_label = _("URL")
-        pagename_label = _("Page name")
         action_label = _("What to do next")
 
         action1 = _("Do nothing")
         action2 = _("Revert change")
         action3 = _("View page info")
+        action4 = _("Perform a search")
 
         do_nothing = forms.Option("n", action1)
         revert = forms.Option("r", action2)
         view_info = forms.Option("v", action3)
+        search = forms.Option("s", action4)
 
         form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=instructions)
-        form.add_field(name="action", field_type="hidden", value="change_notify_action")
-        form.add_field(name="notification", field_type="fixed", value=msg_data['text'])
-        form.add_field(name="options", field_type="list-single", options=[do_nothing, revert, view_info], label=action_label)
+        form.add_field(name='revision', field_type='hidden', value=msg_data['revision'])
+        form.add_field(name='page_name', field_type='hidden', value=msg_data['page_name'])
+        form.add_field(name='editor', field_type='text-single', value=msg_data['editor'], label=_("Editor"))
+        form.add_field(name='comment', field_type='text-single', value=msg_data.get('comment', ''), label=_("Comment"))
 
-        self.send_form(jid, form, _("Page change notification"))
+        # Add lines of text as separate values, as recommended in XEP
+        diff_lines = msg_data['diff'].split('\n')
+        form.add_field(name="diff", field_type="text-multi", values=diff_lines, label=("Diff"))
+
+        full_jid = JID(jid)
+        bare_jid = full_jid.bare().as_unicode()
+        resource = full_jid.resource
+
+        # Add URLs as OOB data if it's supported and as separate fields otherwise
+        if bare_jid in self.contacts and self.contacts[bare_jid].supports(resource, u'jabber:x:oob'):
+            url_list = msg_data['url_list']
+        else:
+            url_list = []
+
+            for number, url in enumerate(msg_data['url_list']):
+                field_name = "url%d" % (number, )
+                form.add_field(name=field_name, field_type="text-single", value=url["url"], label=url["description"])
+
+        # Selection of a following action
+        form.add_field(name="options", field_type="list-single", options=[do_nothing, revert, view_info, search], label=action_label)
+
+        self.send_form(jid, form, _("Page change notification"), url_list)
+
+    def send_change_text(self, jid, msg_data):
+        """Sends a simple, text page change notification
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+        separator = '-' * 78
+        urls_text = '\n'.join(["%s - %s" % (url["description"], url["url"]) for url in msg_data['url_list']])
+        message = _("%(preamble)s\nComment: %(comment)s\n%(separator)s\n%(diff)s\n%(separator)s\n%(links)s") % {
+                    'preamble': msg_data['text'],
+                    'separator': separator,
+                    'diff': msg_data['diff'],
+                    'comment': msg_data.get('comment', _('no comment')),
+                    'links': urls_text,
+                  }
+
+        data = {'text': message, 'subject': msg_data.get('subject', '')}
+        self.send_message(jid, data, u"message")
+
+    def send_deleted_form(self, jid, msg_data):
+        """Sends a page deleted notification using Data Forms
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+
+        form_title = _("Page deletion notification").encode("utf-8")
+        instructions = _("Submit this form with a specified action to continue.").encode("utf-8")
+        action_label = _("What to do next")
+
+        action1 = _("Do nothing")
+        action2 = _("Perform a search")
+
+        do_nothing = forms.Option("n", action1)
+        search = forms.Option("s", action2)
+
+        form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=instructions)
+        form.add_field(name='editor', field_type='text-single', value=msg_data['editor'], label=_("Editor"))
+        form.add_field(name='comment', field_type='text-single', value=msg_data.get('comment', ''), label=_("Comment"))
+
+        full_jid = JID(jid)
+        bare_jid = full_jid.bare().as_unicode()
+        resource = full_jid.resource
+
+        # Add URLs as OOB data if it's supported and as separate fields otherwise
+        if bare_jid in self.contacts and self.contacts[bare_jid].supports(resource, u'jabber:x:oob'):
+            url_list = msg_data['url_list']
+        else:
+            url_list = []
+
+            for number, url in enumerate(msg_data['url_list']):
+                field_name = "url%d" % (number, )
+                form.add_field(name=field_name, field_type="text-single", value=url["url"], label=url["description"])
+
+        # Selection of a following action
+        form.add_field(name="options", field_type="list-single", options=[do_nothing, search], label=action_label)
+
+        self.send_form(jid, form, _("Page deletion notification"), url_list)
+
+    def send_deleted_text(self, jid, msg_data):
+        """Sends a simple, text page deletion notification
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+        separator = '-' * 78
+        urls_text = '\n'.join(["%s - %s" % (url["description"], url["url"]) for url in msg_data['url_list']])
+        message = _("%(preamble)s\nComment: %(comment)s\n%(separator)s\n%(links)s") % {
+                    'preamble': msg_data['text'],
+                    'separator': separator,
+                    'comment': msg_data.get('comment', _('no comment')),
+                    'links': urls_text,
+                  }
+
+        data = {'text': message, 'subject': msg_data.get('subject', '')}
+        self.send_message(jid, data, u"message")
+
+    def send_attached_form(self, jid, msg_data):
+        """Sends a new attachment notification using Data Forms
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+
+        form_title = _("File attached notification").encode("utf-8")
+        instructions = _("Submit this form with a specified action to continue.").encode("utf-8")
+        action_label = _("What to do next")
+
+        action1 = _("Do nothing")
+        action2 = _("View page info")
+        action3 = _("Perform a search")
+
+        do_nothing = forms.Option("n", action1)
+        view_info = forms.Option("v", action2)
+        search = forms.Option("s", action3)
+
+        form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=instructions)
+        form.add_field(name='page_name', field_type='hidden', value=msg_data['page_name'])
+        form.add_field(name='editor', field_type='text-single', value=msg_data['editor'], label=_("Editor"))
+        form.add_field(name='page', field_type='text-single', value=msg_data['page_name'], label=_("Page name"))
+        form.add_field(name='name', field_type='text-single', value=msg_data['attach_name'], label=_("File name"))
+        form.add_field(name='size', field_type='text-single', value=msg_data['attach_size'], label=_("File size"))
+
+        full_jid = JID(jid)
+        bare_jid = full_jid.bare().as_unicode()
+        resource = full_jid.resource
+
+        # Add URLs as OOB data if it's supported and as separate fields otherwise
+        if bare_jid in self.contacts and self.contacts[bare_jid].supports(resource, u'jabber:x:oob'):
+            url_list = msg_data['url_list']
+        else:
+            url_list = []
+
+            for number, url in enumerate(msg_data['url_list']):
+                field_name = "url%d" % (number, )
+                form.add_field(name=field_name, field_type="text-single", value=url["url"], label=url["description"])
+
+        # Selection of a following action
+        form.add_field(name="options", field_type="list-single", options=[do_nothing, view_info, search], label=action_label)
+
+        self.send_form(jid, form, _("File attached notification"), url_list)
+
+    def send_attached_text(self, jid, msg_data):
+        """Sends a simple, text page deletion notification
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+        separator = '-' * 78
+        urls_text = '\n'.join(["%s - %s" % (url["description"], url["url"]) for url in msg_data['url_list']])
+        message = _("%(preamble)s\n%(separator)s\n%(links)s") % {
+                    'preamble': msg_data['text'],
+                    'separator': separator,
+                    'links': urls_text,
+                  }
+
+        data = {'text': message, 'subject': msg_data['subject']}
+        self.send_message(jid, data, u"message")
+
+    def send_renamed_form(self, jid, msg_data):
+        """Sends a page rename notification using Data Forms
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+
+        form_title = _("Page rename notification").encode("utf-8")
+        instructions = _("Submit this form with a specified action to continue.").encode("utf-8")
+        action_label = _("What to do next")
+
+        action1 = _("Do nothing")
+        action2 = _("Revert change")
+        action3 = _("View page info")
+        action4 = _("Perform a search")
+
+        do_nothing = forms.Option("n", action1)
+        revert = forms.Option("r", action2)
+        view_info = forms.Option("v", action3)
+        search = forms.Option("s", action4)
+
+        form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=instructions)
+        form.add_field(name='revision', field_type='hidden', value=msg_data['revision'])
+        form.add_field(name='page_name', field_type='hidden', value=msg_data['page_name'])
+        form.add_field(name='editor', field_type='text-single', value=msg_data['editor'], label=_("Editor"))
+        form.add_field(name='comment', field_type='text-single', value=msg_data.get('comment', ''), label=_("Comment"))
+        form.add_field(name='old', field_type='text-single', value=msg_data['old_name'], label=_("Old name"))
+        form.add_field(name='new', field_type='text-single', value=msg_data['page_name'], label=_("New name"))
+
+        full_jid = JID(jid)
+        bare_jid = full_jid.bare().as_unicode()
+        resource = full_jid.resource
+
+        # Add URLs as OOB data if it's supported and as separate fields otherwise
+        if bare_jid in self.contacts and self.contacts[bare_jid].supports(resource, u'jabber:x:oob'):
+            url_list = msg_data['url_list']
+        else:
+            url_list = []
+
+            for number, url in enumerate(msg_data['url_list']):
+                field_name = "url%d" % (number, )
+                form.add_field(name=field_name, field_type="text-single", value=url["url"], label=url["description"])
+
+        # Selection of a following action
+        form.add_field(name="options", field_type="list-single", options=[do_nothing, revert, view_info, search], label=action_label)
+
+        self.send_form(jid, form, _("Page rename notification"), url_list)
+
+    def send_renamed_text(self, jid, msg_data):
+        """Sends a simple, text page rename notification
+
+        @param jid: a Jabber ID to send the notification to
+        @type jid: unicode
+        @param msg_data: dictionary with notification data
+        @type msg_data: dict
+
+        """
+        _ = self.get_text(jid)
+        separator = '-' * 78
+        urls_text = '\n'.join(["%s - %s" % (url["description"], url["url"]) for url in msg_data['url_list']])
+        message = _("%(preamble)s\nComment: %(comment)s\n%(separator)s\n%(links)s") % {
+                    'preamble': msg_data['text'],
+                    'separator': separator,
+                    'comment': msg_data.get('comment', _('no comment')),
+                    'links': urls_text,
+                  }
+
+        data = {'text': message, 'subject': msg_data['subject']}
+        self.send_message(jid, data, u"message")
+
+    def handle_page_info(self, command):
+        """Handles GetPageInfo commands
+
+        @param command: a command instance
+        @type command: jabberbot.commands.GetPageInfo
+
+        """
+        # Process command data first so it can be directly usable
+        if command.data['author'].startswith("Self:"):
+            command.data['author'] = command.data['author'][5:]
+
+        datestr = str(command.data['lastModified'])
+        command.data['lastModified'] = u"%(year)s-%(month)s-%(day)s at %(time)s" % {
+                    'year': datestr[:4],
+                    'month': datestr[4:6],
+                    'day': datestr[6:8],
+                    'time': datestr[9:17],
+        }
+
+        if command.presentation == u"text":
+            self.send_pageinfo_text(command)
+        elif command.presentation == u"dataforms":
+            self.send_pageinfo_form(command)
+
+        else:
+            raise ValueError("presentation value '%s' is not supported!" % (command.presentation, ))
+
+    def send_pageinfo_text(self, command):
+        """Sends detailed page info with plain text
+
+        @param command: command with detailed data
+        @type command: jabberbot.command.GetPageInfo
+
+        """
+        _ = self.get_text(command.jid)
+
+        intro = _("""Following detailed information on page "%(pagename)s" \
+is available:""")
+
+        msg = _("""Last author: %(author)s
+Last modification: %(modification)s
+Current version: %(version)s""") % {
+         'author': command.data['author'],
+         'modification': command.data['lastModified'],
+         'version': command.data['version'],
+        }
+
+        self.send_message(command.jid, {'text': intro % {'pagename': command.pagename}})
+        self.send_message(command.jid, {'text': msg})
+
+    def send_pageinfo_form(self, command):
+        """Sends page info using Data Forms
+
+
+        """
+        _ = self.get_text(command.jid)
+        data = command.data
+
+        form_title = _("Detailed page information").encode("utf-8")
+        instructions = _("Submit this form with a specified action to continue.").encode("utf-8")
+        action_label = _("What to do next")
+
+        action1 = _("Do nothing")
+        action2 = _("Get page contents")
+        action3 = _("Get page contents (HTML)")
+        action4 = _("Perform a search")
+
+        do_nothing = forms.Option("n", action1)
+        get_content = forms.Option("c", action2)
+        get_content_html = forms.Option("h", action3)
+        search = forms.Option("s", action4)
+
+        form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=instructions)
+        form.add_field(name='pagename', field_type='text-single', value=command.pagename, label=_("Page name"))
+        form.add_field(name="changed", field_type='text-single', value=data['lastModified'], label=_("Last changed"))
+        form.add_field(name='editor', field_type='text-single', value=data['author'], label=_("Last editor"))
+        form.add_field(name='version', field_type='text-single', value=data['version'], label=_("Current version"))
+
+#        full_jid = JID(jid)
+#        bare_jid = full_jid.bare().as_unicode()
+#        resource = full_jid.resource
+
+        # Add URLs as OOB data if it's supported and as separate fields otherwise
+#        if bare_jid in self.contacts and self.contacts[bare_jid].supports(resource, u'jabber:x:oob'):
+#            url_list = msg_data['url_list']
+#        else:
+#            url_list = []
+#
+#            for number, url in enumerate(msg_data['url_list']):
+#                field_name = "url%d" % (number, )
+#                form.add_field(name=field_name, field_type="text-single", value=url["url"], label=url["description"])
+
+        # Selection of a following action
+        form.add_field(name="options", field_type="list-single", options=[do_nothing, get_content, get_content_html, search], label=action_label)
+
+        self.send_form(command.jid, form, _("Detailed page information"))
 
     def is_internal(self, command):
         """Check if a given command is internal
@@ -620,18 +1044,34 @@
         if form.type != u"submit":
             return
 
-        try:
+        if "action" in form:
             action = form["action"].value
-        except KeyError:
-            data = {'text': _('The form you submitted was invalid!'), 'subject': _('Invalid data')}
-            self.send_message(jid.as_unicode(), data, u"message")
-            return
+            if action == u"search":
+                self.handle_search_form(jid, form)
+            else:
+                data = {'text': _('The form you submitted was invalid!'), 'subject': _('Invalid data')}
+                self.send_message(jid.as_unicode(), data, u"message")
+        elif "options" in form:
+            option = form["options"].value
 
-        if action == u"search":
-            self.handle_search_form(jid, form)
-        else:
-            data = {'text': _('The form you submitted was invalid!'), 'subject': _('Invalid data')}
-            self.send_message(jid.as_unicode(), data, u"message")
+            # View page info
+            if option == "v":
+                command = cmd.GetPageInfo(jid.as_unicode(), form["page_name"].value, presentation="dataforms")
+                self.from_commands.put_nowait(command)
+
+            # Perform an another search
+            elif option == "s":
+                self.handle_internal_command(jid, ["searchform"])
+
+            # Revert a change
+            elif option == "r":
+                revision = int(form["revision"].value)
+
+                # We can't really revert creation of a page, right?
+                if revision == 1:
+                    return
+
+                self.handle_xmlrpc_command(jid, ["revertpage", form["page_name"].value, "%d" % (revision - 1, )])
 
 
     def handle_search_form(self, jid, form):
@@ -671,7 +1111,7 @@
 
         """
         if self.config.verbose:
-            msg = "Message from %s." % (message.get_from_jid().as_utf8(), )
+            msg = "Message from %s." % (message.get_from_jid().as_unicode(), )
             self.log.debug(msg)
 
         form = self.contains_form(message)
@@ -715,7 +1155,7 @@
             else:
                 return self.help_on(sender, command[1])
         elif command[0] == "searchform":
-            jid = sender.bare().as_utf8()
+            jid = sender.bare().as_unicode()
             resource = sender.resource
 
             # Assume that outsiders know what they are doing. Clients that don't support
@@ -789,7 +1229,7 @@
         command_class = self.xmlrpc_commands[command[0]]
 
         # Add sender's JID to the argument list
-        command.insert(1, sender.as_utf8())
+        command.insert(1, sender.as_unicode())
 
         try:
             instance = command_class.__new__(command_class)
@@ -825,7 +1265,7 @@
         self.log.debug("Handling unavailable presence.")
 
         jid = stanza.get_from_jid()
-        bare_jid = jid.bare().as_utf8()
+        bare_jid = jid.bare().as_unicode()
 
         # If we get presence, this contact should already be known
         if bare_jid in self.contacts:
@@ -871,7 +1311,7 @@
 
         priority = presence.get_priority()
         jid = presence.get_from_jid()
-        bare_jid = jid.bare().as_utf8()
+        bare_jid = jid.bare().as_unicode()
 
         if bare_jid in self.contacts:
             contact = self.contacts[bare_jid]
@@ -883,8 +1323,9 @@
             # Unknown resource, add it to the list
             else:
                 contact.add_resource(jid.resource, show, priority)
+
                 # Discover capabilities of the newly connected client
-                contact.service_discovery(jid)
+                self.service_discovery(jid)
 
             if self.config.verbose:
                 self.log.debug(contact)
@@ -941,12 +1382,12 @@
         supports = payload.xpathEval('//*[@var="jabber:x:data"]')
         if supports:
             jid = stanza.get_from_jid()
-            self.contacts[jid.bare().as_utf8()].set_supports(jid.resource, u"jabber:x:data")
+            self.contacts[jid.bare().as_unicode()].set_supports(jid.resource, u"jabber:x:data")
 
         supports = payload.xpathEval('//*[@var="jabber:x:oob"]')
         if supports:
             jid = stanza.get_from_jid()
-            self.contacts[jid.bare().as_utf8()].set_supports(jid.resource, u"jabber:x:oob")
+            self.contacts[jid.bare().as_unicode()].set_supports(jid.resource, u"jabber:x:oob")
 
 
     def send_queued_messages(self, contact, ignore_dnd=False):