plugin XEP-0055: add "simple" and "advanced" modes to Jabber search:
- simple search queries the service offered by the user's server, asks for a single input and compile the results of the queries done on each search field
- advanced allows to select the search service and to perform a "normal" search (the user can fill in all the fields that are implemented by the service)
This commit is contained in:
parent
95c7e2b809
commit
a7535449b4
1 changed files with 336 additions and 79 deletions
|
|
@ -20,14 +20,24 @@
|
|||
from sat.core.i18n import _, D_
|
||||
from sat.core.log import getLogger
|
||||
log = getLogger(__name__)
|
||||
|
||||
from twisted.words.protocols.jabber.xmlstream import IQ
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.internet import defer
|
||||
from wokkel import data_form
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core.exceptions import DataError
|
||||
from sat.memory.memory import Sessions
|
||||
from sat.tools import xml_tools
|
||||
|
||||
from wokkel import disco, iwokkel
|
||||
try:
|
||||
from twisted.words.protocols.xmlstream import XMPPHandler
|
||||
except ImportError:
|
||||
from wokkel.subprotocols import XMPPHandler
|
||||
from zope.interface import implements
|
||||
|
||||
|
||||
NS_SEARCH = 'jabber:iq:search'
|
||||
|
||||
PLUGIN_INFO = {
|
||||
|
|
@ -42,142 +52,389 @@ PLUGIN_INFO = {
|
|||
"description": _("""Implementation of Jabber Search""")
|
||||
}
|
||||
|
||||
# config file parameters
|
||||
CONFIG_SECTION = "plugin search"
|
||||
CONFIG_SERVICE_LIST = "service_list"
|
||||
|
||||
DEFAULT_SERVICE_LIST = ["salut.libervia.org"]
|
||||
|
||||
FIELD_SINGLE = "field_single" # single text field for the simple search
|
||||
FIELD_CURRENT_SERVICE = "current_service_jid" # read-only text field for the advanced search
|
||||
|
||||
class XEP_0055(object):
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_("Jabber search plugin initialization"))
|
||||
self.host = host
|
||||
host.bridge.addMethod("getSearchUI", ".plugin", in_sign='ss', out_sign='s',
|
||||
method=self._getSearchUI,
|
||||
|
||||
# default search services (config file + hard-coded lists)
|
||||
self.services = [jid.JID(entry) for entry in host.memory.getConfig(CONFIG_SECTION, CONFIG_SERVICE_LIST, DEFAULT_SERVICE_LIST)]
|
||||
|
||||
host.bridge.addMethod("searchGetFieldsUI", ".plugin", in_sign='ss', out_sign='s',
|
||||
method=self._getFieldsUI,
|
||||
async=True)
|
||||
host.bridge.addMethod("searchRequest", ".plugin", in_sign='sa{ss}s', out_sign='s',
|
||||
method=self._searchRequest,
|
||||
async=True)
|
||||
self._sessions = Sessions()
|
||||
self.__menu_cb_id = host.registerCallback(self._menuCb, with_data=True)
|
||||
self.__search_request_id = host.registerCallback(self._xmluiSearchRequest, with_data=True)
|
||||
host.importMenu((D_("Communication"), D_("Search directory")), self._searchMenu, security_limit=1, help_string=D_("Search use directory"))
|
||||
|
||||
def _menuCb(self, data, profile):
|
||||
entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX+'jid'])
|
||||
d = self.getSearchUI(entity, profile)
|
||||
def gotXMLUI(xmlui):
|
||||
session_id, session_data = self._sessions.newSession(profile=profile)
|
||||
session_data['jid'] = entity
|
||||
xmlui.session_id = session_id # we need to keep track of the session
|
||||
xmlui.submit_id = self.__search_request_id
|
||||
return {'xmlui': xmlui.toXml()}
|
||||
d.addCallback(gotXMLUI)
|
||||
return d
|
||||
self.__search_menu_id = host.registerCallback(self._getMainUI, with_data=True)
|
||||
host.importMenu((D_("Communication"), D_("Search directory")), self._getMainUI, security_limit=1, help_string=D_("Search use directory"))
|
||||
|
||||
|
||||
def _searchMenu(self, menu_data, profile):
|
||||
""" First XMLUI activated by menu: ask for target jid
|
||||
@param profile: %(doc_profile)s
|
||||
## Main search UI (menu item callback) ##
|
||||
|
||||
|
||||
def _getMainUI(self, raw_data, profile):
|
||||
"""Get the XMLUI for selecting a service and searching the directory.
|
||||
|
||||
@param raw_data (dict): data received from the frontend
|
||||
@param profile (unicode): %(doc_profile)s
|
||||
@return: a deferred XMLUI string representation
|
||||
"""
|
||||
form_ui = xml_tools.XMLUI("form", title=_("Search directory"), submit_id=self.__menu_cb_id)
|
||||
form_ui.addText(_("Please enter the search jid"), 'instructions')
|
||||
form_ui.changeContainer("pairs")
|
||||
form_ui.addLabel("jid")
|
||||
# form_ui.addString("jid", value="users.jabberfr.org") # TODO: replace users.jabberfr.org by any XEP-0055 compatible service discovered on current server
|
||||
form_ui.addString("jid", value="salut.libervia.org") # TODO: replace salut.libervia.org by any XEP-0055 compatible service discovered on current server
|
||||
return {'xmlui': form_ui.toXml()}
|
||||
# check if the user's server offers some search services
|
||||
d = self.host.findFeaturesSet([NS_SEARCH], profile_key=profile)
|
||||
return d.addCallback(lambda services: self.getMainUI(list(services), raw_data, profile))
|
||||
|
||||
def _getSearchUI(self, to_jid_s, profile_key):
|
||||
d = self.getSearchUI(jid.JID(to_jid_s), profile_key)
|
||||
d.addCallback(lambda xmlui: xmlui.toXml())
|
||||
def getMainUI(self, services, raw_data, profile):
|
||||
"""Get the XMLUI for selecting a service and searching the directory.
|
||||
|
||||
@param services (list[jid.JID]): search services offered by the user server
|
||||
@param raw_data (dict): data received from the frontend
|
||||
@param profile (unicode): %(doc_profile)s
|
||||
@return: a deferred XMLUI string representation
|
||||
"""
|
||||
# extend services offered by user's server with the default services
|
||||
services.extend([service for service in self.services if service not in services])
|
||||
data = xml_tools.XMLUIResult2DataFormResult(raw_data)
|
||||
main_ui = xml_tools.XMLUI(C.XMLUI_WINDOW, container="tabs", title=_("Search users"), submit_id=self.__search_menu_id)
|
||||
|
||||
d = self._addSimpleSearchUI(services, main_ui, data, profile)
|
||||
d.addCallback(lambda dummy: self._addAdvancedSearchUI(services, main_ui, data, profile))
|
||||
return d.addCallback(lambda dummy: {'xmlui': main_ui.toXml()})
|
||||
|
||||
def _addSimpleSearchUI(self, services, main_ui, data, profile):
|
||||
"""Add to the main UI a tab for the simple search.
|
||||
|
||||
Display a single input field and search on the main service (it actually does one search per search field and then compile the results).
|
||||
|
||||
@param services (list[jid.JID]): search services offered by the user server
|
||||
@param main_ui (XMLUI): the main XMLUI instance
|
||||
@param data (dict): form data without SAT_FORM_PREFIX
|
||||
@param profile (unicode): %(doc_profile)s
|
||||
|
||||
@return: a dummy Deferred
|
||||
"""
|
||||
service_jid = services[0] # TODO: search on all the given services, not only the first one
|
||||
|
||||
form = data_form.Form('form', formNamespace=NS_SEARCH)
|
||||
form.addField(data_form.Field('text-single', FIELD_SINGLE, label=_('Search for'), value=data.get(FIELD_SINGLE, '')))
|
||||
|
||||
sub_cont = main_ui.main_container.addTab("simple_search", label=_("Simple search"), container=xml_tools.VerticalContainer)
|
||||
main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui)))
|
||||
xml_tools.dataForm2Widgets(main_ui, form)
|
||||
|
||||
# FIXME: add colspan attribute to divider? (we are in a PairsContainer)
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # here we added a blank line before the button
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addButton(self.__search_menu_id, _("Search"), (FIELD_SINGLE,))
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # a blank line again after the button
|
||||
|
||||
simple_data = {key: value for key, value in data.iteritems() if key in (FIELD_SINGLE,)}
|
||||
if simple_data:
|
||||
log.debug("Simple search with %s on %s" % (simple_data, service_jid))
|
||||
sub_cont.parent.setSelected(True)
|
||||
main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
|
||||
main_ui.addDivider('dash')
|
||||
d = self.searchRequest(service_jid, simple_data, profile)
|
||||
d.addCallbacks(lambda elt: self._displaySearchResult(main_ui, elt),
|
||||
lambda failure: main_ui.addText(failure.getErrorMessage()))
|
||||
return d
|
||||
|
||||
return defer.succeed(None)
|
||||
|
||||
def _addAdvancedSearchUI(self, services, main_ui, data, profile):
|
||||
"""Add to the main UI a tab for the advanced search.
|
||||
|
||||
Display a service selector and allow to search on all the fields that are implemented by the selected service.
|
||||
|
||||
@param services (list[jid.JID]): search services offered by the user server
|
||||
@param main_ui (XMLUI): the main XMLUI instance
|
||||
@param data (dict): form data without SAT_FORM_PREFIX
|
||||
@param profile (unicode): %(doc_profile)s
|
||||
|
||||
@return: a dummy Deferred
|
||||
"""
|
||||
sub_cont = main_ui.main_container.addTab("advanced_search", label=_("Advanced search"), container=xml_tools.VerticalContainer)
|
||||
service_selection_fields = ['service_jid', 'service_jid_extra']
|
||||
|
||||
if 'service_jid_extra' in data:
|
||||
# refresh button has been pushed, select the tab
|
||||
sub_cont.parent.setSelected(True)
|
||||
# get the selected service
|
||||
service_jid_s = data.get('service_jid_extra', '')
|
||||
if not service_jid_s:
|
||||
service_jid_s = data.get('service_jid', unicode(services[0]))
|
||||
log.debug("Refreshing search fields for %s" % service_jid_s)
|
||||
else:
|
||||
service_jid_s = data.get(FIELD_CURRENT_SERVICE, unicode(services[0]))
|
||||
services_s = [unicode(service) for service in services]
|
||||
if service_jid_s not in services_s:
|
||||
services_s.append(service_jid_s)
|
||||
|
||||
main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui)))
|
||||
main_ui.addLabel(_("Search on"))
|
||||
main_ui.addList('service_jid', options=services_s, selected=service_jid_s)
|
||||
main_ui.addLabel(_("Other service"))
|
||||
main_ui.addString(name='service_jid_extra')
|
||||
|
||||
# FIXME: add colspan attribute to divider? (we are in a PairsContainer)
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # here we added a blank line before the button
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addButton(self.__search_menu_id, _("Refresh fields"), service_selection_fields)
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # a blank line again after the button
|
||||
main_ui.addLabel(_("Displaying the search form for"))
|
||||
main_ui.addString(name=FIELD_CURRENT_SERVICE, value=service_jid_s, read_only=True)
|
||||
main_ui.addDivider('dash')
|
||||
main_ui.addDivider('dash')
|
||||
|
||||
main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
|
||||
service_jid = jid.JID(service_jid_s)
|
||||
d = self.getFieldsUI(service_jid, profile)
|
||||
d.addCallbacks(self._addAdvancedForm, lambda failure: main_ui.addText(failure.getErrorMessage()),
|
||||
[service_jid, main_ui, sub_cont, data, profile])
|
||||
return d
|
||||
|
||||
def getSearchUI(self, to_jid, profile_key):
|
||||
""" Ask for a search interface
|
||||
@param to_jid: XEP-0055 compliant search entity
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
@return: XMLUI search interface """
|
||||
def _addAdvancedForm(self, form_elt, service_jid, main_ui, sub_cont, data, profile):
|
||||
"""Add the search form and the search results (if there is some to display).
|
||||
|
||||
@param form_elt (domish.Element): form element listing the fields
|
||||
@param service_jid (jid.JID): current search service
|
||||
@param main_ui (XMLUI): the main XMLUI instance
|
||||
@param sub_cont (Container): the container of the current tab
|
||||
@param data (dict): form data without SAT_FORM_PREFIX
|
||||
@param profile (unicode): %(doc_profile)s
|
||||
|
||||
@return: a dummy Deferred
|
||||
"""
|
||||
field_list = data_form.Form.fromElement(form_elt).fieldList
|
||||
adv_fields = [field.var for field in field_list if field.var]
|
||||
adv_data = {key: value for key, value in data.iteritems() if key in adv_fields}
|
||||
|
||||
xml_tools.dataForm2Widgets(main_ui, data_form.Form.fromElement(form_elt))
|
||||
|
||||
# refill the submitted values
|
||||
# FIXME: wokkel's data_form.Form.fromElement doesn't parse the values, so we do it directly in XMLUI for now
|
||||
for widget in main_ui.current_container.elem.childNodes:
|
||||
name = widget.getAttribute("name")
|
||||
if adv_data.get(name):
|
||||
widget.setAttribute("value", adv_data[name])
|
||||
|
||||
# FIXME: add colspan attribute to divider? (we are in a PairsContainer)
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # here we added a blank line before the button
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addButton(self.__search_menu_id, _("Search"), adv_fields + [FIELD_CURRENT_SERVICE])
|
||||
main_ui.addDivider('blank')
|
||||
main_ui.addDivider('blank') # a blank line again after the button
|
||||
|
||||
if adv_data: # display the search results
|
||||
log.debug("Advanced search with %s on %s" % (adv_data, service_jid))
|
||||
sub_cont.parent.setSelected(True)
|
||||
main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
|
||||
main_ui.addDivider('dash')
|
||||
d = self.searchRequest(service_jid, adv_data, profile)
|
||||
d.addCallbacks(lambda elt: self._displaySearchResult(main_ui, elt),
|
||||
lambda failure: main_ui.addText(failure.getErrorMessage()))
|
||||
return d
|
||||
|
||||
return defer.succeed(None)
|
||||
|
||||
|
||||
def _displaySearchResult(self, main_ui, elt):
|
||||
"""Display the search results.
|
||||
|
||||
@param main_ui (XMLUI): the main XMLUI instance
|
||||
@param elt (domish.Element): form result element
|
||||
"""
|
||||
if [child for child in elt.children if child.name == "item"]:
|
||||
xml_tools.dataFormResult2AdvancedList(main_ui, elt)
|
||||
else:
|
||||
main_ui.addText(D_("The search gave no result"))
|
||||
|
||||
## Retrieve the search fields ##
|
||||
|
||||
|
||||
def _getFieldsUI(self, to_jid_s, profile_key):
|
||||
"""Ask a service to send us the list of the form fields it manages.
|
||||
|
||||
@param to_jid_s (unicode): XEP-0055 compliant search entity
|
||||
@param profile_key (unicode): %(doc_profile_key)s
|
||||
@return: a deferred XMLUI instance
|
||||
"""
|
||||
d = self.getFieldsUI(jid.JID(to_jid_s), profile_key)
|
||||
d.addCallback(lambda form: xml_tools.dataFormResult2XMLUI(form).toXml())
|
||||
return d
|
||||
|
||||
def getFieldsUI(self, to_jid, profile_key):
|
||||
"""Ask a service to send us the list of the form fields it manages.
|
||||
|
||||
@param to_jid (jid.JID): XEP-0055 compliant search entity
|
||||
@param profile_key (unicode): %(doc_profile_key)s
|
||||
@return: a deferred domish.Element
|
||||
"""
|
||||
client = self.host.getClient(profile_key)
|
||||
fields_request = IQ(client.xmlstream, 'get')
|
||||
fields_request["from"] = client.jid.full()
|
||||
fields_request["to"] = to_jid.full()
|
||||
fields_request.addElement('query', NS_SEARCH)
|
||||
d = fields_request.send(to_jid.full())
|
||||
d.addCallbacks(self._fieldsOk, self._fieldsErr, callbackArgs=[client.profile], errbackArgs=[client.profile])
|
||||
d.addCallbacks(self._getFieldsUICb, self._getFieldsUIEb)
|
||||
return d
|
||||
|
||||
def _fieldsOk(self, answer, profile):
|
||||
"""got fields available"""
|
||||
def _getFieldsUICb(self, answer):
|
||||
"""Callback for self.getFieldsUI.
|
||||
|
||||
@param answer (domish.Element): search query element
|
||||
@return: domish.Element
|
||||
"""
|
||||
try:
|
||||
query_elts = answer.elements('jabber:iq:search', 'query').next()
|
||||
except StopIteration:
|
||||
log.info(_("No query element found"))
|
||||
raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
|
||||
raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
|
||||
try:
|
||||
form_elt = query_elts.elements(data_form.NS_X_DATA, 'x').next()
|
||||
except StopIteration:
|
||||
log.info(_("No data form found"))
|
||||
raise NotImplementedError("Only search through data form is implemented so far")
|
||||
parsed_form = data_form.Form.fromElement(form_elt)
|
||||
return xml_tools.dataForm2XMLUI(parsed_form, "")
|
||||
return form_elt
|
||||
|
||||
def _fieldsErr(self, failure, profile):
|
||||
""" Called when something is wrong with fields request """
|
||||
log.info(_("Fields request failure: %s") % unicode(failure.value))
|
||||
return failure
|
||||
def _getFieldsUIEb(self, failure):
|
||||
"""Errback to self.getFieldsUI.
|
||||
|
||||
def _xmluiSearchRequest(self, raw_data, profile):
|
||||
try:
|
||||
session_data = self._sessions.profileGet(raw_data["session_id"], profile)
|
||||
except KeyError:
|
||||
log.warning ("session id doesn't exist, session has probably expired")
|
||||
# TODO: send error dialog
|
||||
return defer.succeed({})
|
||||
@param failure (defer.failure.Failure): twisted failure
|
||||
@raise: the unchanged defer.failure.Failure
|
||||
"""
|
||||
log.info(_("Fields request failure: %s") % unicode(failure.getErrorMessage()))
|
||||
raise failure
|
||||
|
||||
data = xml_tools.XMLUIResult2DataFormResult(raw_data)
|
||||
entity =session_data['jid']
|
||||
d = self.searchRequest(entity, data, profile)
|
||||
d.addCallback(lambda xmlui: {'xmlui':xmlui.toXml()})
|
||||
del self._sessions[raw_data["session_id"]]
|
||||
|
||||
## Do the search ##
|
||||
|
||||
|
||||
def _searchRequest(self, to_jid_s, search_data, profile_key):
|
||||
"""Actually do a search, according to filled data.
|
||||
|
||||
@param to_jid_s (unicode): XEP-0055 compliant search entity
|
||||
@param search_data (dict): filled data, corresponding to the form obtained in getFieldsUI
|
||||
@param profile_key (unicode): %(doc_profile_key)s
|
||||
@return: a deferred XMLUI string representation
|
||||
"""
|
||||
d = self.searchRequest(jid.JID(to_jid_s), search_data, profile_key)
|
||||
d.addCallback(lambda form: xml_tools.dataFormResult2XMLUI(form).toXml())
|
||||
return d
|
||||
|
||||
def _searchRequest(self, to_jid_s, search_dict, profile_key):
|
||||
d = self.searchRequest(jid.JID(to_jid_s), search_dict, profile_key)
|
||||
d.addCallback(lambda xmlui: xmlui.toXml())
|
||||
return d
|
||||
def searchRequest(self, to_jid, search_data, profile_key):
|
||||
"""Actually do a search, according to filled data.
|
||||
|
||||
@param to_jid (jid.JID): XEP-0055 compliant search entity
|
||||
@param search_data (dict): filled data, corresponding to the form obtained in getFieldsUI
|
||||
@param profile_key (unicode): %(doc_profile_key)s
|
||||
@return: a deferred domish.Element
|
||||
"""
|
||||
if FIELD_SINGLE in search_data:
|
||||
value = search_data[FIELD_SINGLE]
|
||||
d = self.getFieldsUI(to_jid, profile_key)
|
||||
d.addCallback(lambda elt: self.searchRequestMulti(to_jid, value, elt, profile_key))
|
||||
return d
|
||||
|
||||
def searchRequest(self, to_jid, search_dict, profile_key):
|
||||
""" Actually do a search, according to filled data
|
||||
@param to_jid: XEP-0055 compliant search entity
|
||||
@param search_dict: filled data, corresponding to the form obtained in getSearchUI
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
@return: XMLUI search result """
|
||||
client = self.host.getClient(profile_key)
|
||||
search_request = IQ(client.xmlstream, 'set')
|
||||
search_request["from"] = client.jid.full()
|
||||
search_request["to"] = to_jid.full()
|
||||
query_elt = search_request.addElement('query', NS_SEARCH)
|
||||
x_form = data_form.Form('submit', formNamespace = NS_SEARCH)
|
||||
x_form.makeFields(search_dict)
|
||||
x_form = data_form.Form('submit', formNamespace=NS_SEARCH)
|
||||
x_form.makeFields(search_data)
|
||||
query_elt.addChild(x_form.toElement())
|
||||
# TODO: XEP-0059 could be used here (with the needed new method attributes)
|
||||
d = search_request.send(to_jid.full())
|
||||
d.addCallbacks(self._searchOk, self._searchErr, callbackArgs=[client.profile], errbackArgs=[client.profile])
|
||||
d.addCallbacks(self._searchOk, self._searchErr)
|
||||
return d
|
||||
|
||||
def _searchOk(self, answer, profile):
|
||||
"""got search available"""
|
||||
def searchRequestMulti(self, to_jid, value, form_elt, profile_key):
|
||||
"""Search for a value simultaneously in all fields, returns the results compilation.
|
||||
|
||||
@param to_jid (jid.JID): XEP-0055 compliant search entity
|
||||
@param value (unicode): value to search
|
||||
@param form_elt (domish.Element): form element listing the fields
|
||||
@param profile_key (unicode): %(doc_profile_key)s
|
||||
@return: a deferred domish.Element
|
||||
"""
|
||||
form = data_form.Form.fromElement(form_elt)
|
||||
d_list = []
|
||||
for field in [field.var for field in form.fieldList if field.var]:
|
||||
d_list.append(self.searchRequest(to_jid, {field: value}, profile_key))
|
||||
break
|
||||
|
||||
def cb(result): # return the results compiled in one domish element
|
||||
result_elt = None
|
||||
for success, form_elt in result:
|
||||
if not success:
|
||||
continue
|
||||
if result_elt is None: # the result element is built over the first answer
|
||||
result_elt = form_elt
|
||||
continue
|
||||
for item_elt in form_elt.elements('jabber:x:data', 'item'):
|
||||
result_elt.addChild(item_elt)
|
||||
if result_elt is None:
|
||||
raise defer.failure.Failure(DataError(_("The search could not be performed")))
|
||||
return result_elt
|
||||
|
||||
return defer.DeferredList(d_list).addCallback(cb)
|
||||
|
||||
def _searchOk(self, answer):
|
||||
"""Callback for self.searchRequest.
|
||||
|
||||
@param answer (domish.Element): search query element
|
||||
@return: domish.Element
|
||||
"""
|
||||
try:
|
||||
query_elts = answer.elements('jabber:iq:search', 'query').next()
|
||||
except StopIteration:
|
||||
log.info(_("No query element found"))
|
||||
raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
|
||||
raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
|
||||
try:
|
||||
form_elt = query_elts.elements(data_form.NS_X_DATA, 'x').next()
|
||||
except StopIteration:
|
||||
log.info(_("No data form found"))
|
||||
raise NotImplementedError("Only search through data form is implemented so far")
|
||||
return xml_tools.dataFormResult2XMLUI(form_elt)
|
||||
return form_elt
|
||||
|
||||
def _searchErr(self, failure):
|
||||
"""Errback to self.searchRequest.
|
||||
|
||||
@param failure (defer.failure.Failure): twisted failure
|
||||
@raise: the unchanged defer.failure.Failure
|
||||
"""
|
||||
log.info(_("Search request failure: %s") % unicode(failure.getErrorMessage()))
|
||||
raise failure
|
||||
|
||||
|
||||
class XEP_0059_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
||||
def __init__(self, plugin_parent, profile):
|
||||
self.plugin_parent = plugin_parent
|
||||
self.host = plugin_parent.host
|
||||
self.profile = profile
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
return [disco.DiscoFeature(NS_SEARCH)]
|
||||
|
||||
def getDiscoItems(self, requestor, target, nodeIdentifier=''):
|
||||
return []
|
||||
|
||||
def _searchErr(self, failure, profile):
|
||||
""" Called when something is wrong with search request """
|
||||
log.info(_("Search request failure: %s") % unicode(failure.value))
|
||||
return failure
|
||||
|
|
|
|||
Loading…
Reference in a new issue