changeset 2665:0b9987a83cc9

Merge devel.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Thu, 09 Aug 2007 02:49:07 +0200
parents 9a17fabb8008 (current diff) 4d4a1a5c0c42 (diff)
children 507ed8d9748a 9e1a05495686
files
diffstat 2 files changed, 153 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/jabberbot/commands.py	Wed Aug 08 20:10:05 2007 +0200
+++ b/jabberbot/commands.py	Thu Aug 09 02:49:07 2007 +0200
@@ -129,14 +129,14 @@
     description = u"perform a wiki search"
     parameter_list = u"{title|text} term"
 
-    def __init__(self, jid, search_type, *args):
+    def __init__(self, jid, search_type, *args, **kwargs):
         BaseDataCommand.__init__(self, jid)
         self.term = ' '.join(args)
         self.search_type = search_type
-        self.presentation = "text" # "text" or "dataforms"
-        self.case = False
+        self.presentation = kwargs.get('presentation', 'text') # "text" or "dataforms"
+        self.case = kwargs.get('case', False)
         self.mtime = None
-        self.regexp = False
+        self.regexp = kwargs.get('regexp', False)
 
 class GetUserLanguage:
     """Request user's language information from wiki"""
--- a/jabberbot/xmppbot.py	Wed Aug 08 20:10:05 2007 +0200
+++ b/jabberbot/xmppbot.py	Thu Aug 09 02:49:07 2007 +0200
@@ -16,6 +16,7 @@
 from pyxmpp.presence import Presence
 from pyxmpp.iq import Iq
 import pyxmpp.jabber.dataforms as forms
+import libxml2
 
 import jabberbot.commands as cmd
 import jabberbot.i18n as i18n
@@ -348,29 +349,45 @@
                 self.contacts[command.jid].language = command.language
 
         elif isinstance(command, cmd.Search):
-            if command.presentation == u"text":
-                if not command.data:
-                    msg = _("There are no pages matching your search criteria!")
-                    self.send_message(command.jid, {'text': msg})
-                    return
+            warnings = []
+            if not command.data:
+                warnings.append(_("There are no pages matching your search criteria!"))
 
-                # This hardcoded limitation relies on (mostly correct) assumption that Jabber
-                # servers have rather tight traffic limits. Sending more than 25 results is likely
-                # to take a second or two - users should not have to wait longer (+search time!).
-                elif len(command.data) > 25:
-                    msg =  _("There are too many results (%(number)s). Limiting to first 25 entries.") % {'number': str(len(command.data))}
-                    self.send_message(command.jid, {'text': msg})
-                    command.data = command.data[:25]
+            # This hardcoded limitation relies on (mostly correct) assumption that Jabber
+            # servers have rather tight traffic limits. Sending more than 25 results is likely
+            # to take a second or two - users should not have to wait longer (+search time!).
+            elif len(command.data) > 25:
+                warnings.append(_("There are too many results (%(number)s). Limiting to first 25 entries.") % {'number': str(len(command.data))})
+                command.data = command.data[:25]
 
-                #intro = _("Following pages match your search:\n%(results)s")
+            results = [{'description': result[0], 'url': result[2]} for result in command.data]
 
-                results = [{'description': result[0], 'url': result[2]} for result in command.data]
+            if command.presentation == u"text":
+                for warning in warnings:
+                    self.send_message(command.jid, {'text': warning})
+
+                if not results:
+                    return
 
                 data = {'text': _('Following pages match your search criteria:'), 'url_list': results}
                 self.send_message(command.jid, data, u"chat")
             else:
-                pass
-                # TODO: implement data forms here
+                form_title = _("Search results").encode("utf-8")
+                form = forms.Form(xmlnode_or_type="result", title=form_title)
+
+                for no, warning in enumerate(warnings):
+                    name = "warning%d" % (no, )
+                    form.add_field(name=name, field_type="fixed", value=warning)
+
+                for no, result in enumerate(results):
+                    name = "result%d" % (no, )
+                    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)
+
+                self.send_form(command.jid, form, _("Search results"))
+
 
     def ask_for_subscription(self, jid):
         """Sends a <presence/> stanza with type="subscribe"
@@ -428,8 +445,26 @@
 
         self.get_stream().send(message)
 
-    def send_form(self, jid, form):
-        pass
+    def send_form(self, jid, form, subject):
+        """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
+        @type jid: unicode
+        @type form: pyxmpp.jabber.dataforms.Form
+        @type subject: unicode
+
+        """
+        if not isinstance(form, forms.Form):
+            raise ValueErrur("The 'form' argument must be of type pyxmpp.jabber.dataforms.Form!")
+
+        _ = 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.add_content(form)
+        self.get_stream().send(message)
 
     def send_search_form(self, jid):
         _ = self.get_text(jid)
@@ -442,18 +477,21 @@
         search_type2 = _("Full-text search")
         search_label = _("Search type")
         search_label2 = _("Search text")
+        case_label = _("Case-sensitive search")
+        regexp_label = _("Treat terms as regular expressions")
         forms_warn = _("If you see this, your client probably doesn't support Data Forms.")
 
         title_search = forms.Option("t", search_type1)
         full_search = forms.Option("f", search_type2)
 
         form = forms.Form(xmlnode_or_type="form", title=form_title, instructions=help_form)
+        form.add_field(name="action", field_type="hidden", value="search")
+        form.add_field(name="case", field_type="boolean", label=case_label)
+        form.add_field(name="regexp", field_type="boolean", label=regexp_label)
         form.add_field(name="search_type", options=[title_search, full_search], field_type="list-single", label=search_label)
         form.add_field(name="search", field_type="text-single", label=search_label2)
 
-        message = Message(to_jid=jid, body=forms_warn, subject=_("Wiki search"))
-        message.add_content(form)
-        self.get_stream().send(message)
+        self.send_form(jid, form, _("Wiki search"))
 
     def is_internal(self, command):
         """Check if a given command is internal
@@ -479,6 +517,90 @@
 
         return False
 
+    def contains_form(self, message):
+        """Checks if passed message stanza contains a submitted form and parses it
+
+        @param message: message stanza
+        @type message: pyxmpp.message.Message
+        @return: xml node with form data if found, or None
+
+        """
+        if not isinstance(message, Message):
+            raise ValueError("The 'message' parameter must be of type pyxmpp.message.Message!")
+
+        payload = message.get_node()
+        form = message.xpath_eval('/ns:message/data:x', {'data': 'jabber:x:data'})
+
+        if form:
+            return form[0]
+        else:
+            return None
+
+    def handle_form(self, jid, form_node):
+        """Handles a submitted data form
+
+        @param jid: jid that submitted the form (full jid)
+        @type jid: pyxmpp.jid.JID
+        @param form_node: a xml node with data form
+        @type form_node: libxml2.xmlNode
+
+        """
+        if not isinstance(form_node, libxml2.xmlNode):
+            raise ValueError("The 'form' parameter must be of type libxml2.xmlNode!")
+
+        if not isinstance(jid, JID):
+            raise ValueError("The 'jid' parameter must be of type jid!")
+
+        _ = self.get_text(jid.bare().as_unicode())
+
+        form = forms.Form(form_node)
+
+        if form.type != u"submit":
+            return
+
+        try:
+            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")
+
+
+    def handle_search_form(self, jid, form):
+        """Handles a search form
+
+        @param jid: jid that submitted the form
+        @type jid: pyxmpp.jid.JID
+        @param form: a form object
+        @type form_node: pyxmpp.jabber.dataforms.Form
+
+        """
+        required_fields = ["case", "regexp", "search_type", "search"]
+        jid_text = jid.bare().as_unicode()
+        _ = self.get_text(jid_text)
+
+        for field in required_fields:
+            if field not in form:
+                data = {'text': _('The form you submitted was invalid!'), 'subject': _('Invalid data')}
+                self.send_message(jid.as_unicode(), data, u"message")
+
+        case_sensitive = form['case'].value
+        regexp_terms = form['regexp'].value
+        if form['search_type'].value == 't':
+            search_type = 'title'
+        else:
+            search_type = 'text'
+
+        command = cmd.Search(jid_text, search_type, form["search"].value, case=form['case'].value,
+                             regexp=form['regexp'].value, presentation='dataforms')
+        self.from_commands.put_nowait(command)
+
     def handle_message(self, message):
         """Handles incoming messages
 
@@ -490,6 +612,11 @@
             msg = "Message from %s." % (message.get_from_jid().as_utf8(), )
             self.log.debug(msg)
 
+        form = self.contains_form(message)
+        if form:
+            self.handle_form(message.get_from_jid(), form)
+            return
+
         text = message.get_body()
         sender = message.get_from_jid()
         if text:
@@ -531,7 +658,7 @@
 
             # Assume that outsiders know what they are doing. Clients that don't support
             # data forms should display a warning passed in message <body>.
-            if jid not in self.contacts or self.contacts[jid].supports_forms(resource):
+            if jid not in self.contacts or self.contacts[jid].supports(resource, u"jabber:x:data"):
                 self.send_search_form(sender)
             else:
                 msg = {'text': _("This command requires a client supporting Data Forms.")}