comparison sat/plugins/plugin_xep_0055.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children 378188abe941
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _, D_
21 from sat.core.log import getLogger 21 from sat.core.log import getLogger
22
22 log = getLogger(__name__) 23 log = getLogger(__name__)
23 24
24 from twisted.words.protocols.jabber.xmlstream import IQ 25 from twisted.words.protocols.jabber.xmlstream import IQ
25 from twisted.words.protocols.jabber import jid 26 from twisted.words.protocols.jabber import jid
26 from twisted.internet import defer 27 from twisted.internet import defer
28 from sat.core.constants import Const as C 29 from sat.core.constants import Const as C
29 from sat.core.exceptions import DataError 30 from sat.core.exceptions import DataError
30 from sat.tools import xml_tools 31 from sat.tools import xml_tools
31 32
32 from wokkel import disco, iwokkel 33 from wokkel import disco, iwokkel
34
33 try: 35 try:
34 from twisted.words.protocols.xmlstream import XMPPHandler 36 from twisted.words.protocols.xmlstream import XMPPHandler
35 except ImportError: 37 except ImportError:
36 from wokkel.subprotocols import XMPPHandler 38 from wokkel.subprotocols import XMPPHandler
37 from zope.interface import implements 39 from zope.interface import implements
38 40
39 41
40 NS_SEARCH = 'jabber:iq:search' 42 NS_SEARCH = "jabber:iq:search"
41 43
42 PLUGIN_INFO = { 44 PLUGIN_INFO = {
43 C.PI_NAME: "Jabber Search", 45 C.PI_NAME: "Jabber Search",
44 C.PI_IMPORT_NAME: "XEP-0055", 46 C.PI_IMPORT_NAME: "XEP-0055",
45 C.PI_TYPE: "XEP", 47 C.PI_TYPE: "XEP",
46 C.PI_PROTOCOLS: ["XEP-0055"], 48 C.PI_PROTOCOLS: ["XEP-0055"],
47 C.PI_DEPENDENCIES: [], 49 C.PI_DEPENDENCIES: [],
48 C.PI_RECOMMENDATIONS: ["XEP-0059"], 50 C.PI_RECOMMENDATIONS: ["XEP-0059"],
49 C.PI_MAIN: "XEP_0055", 51 C.PI_MAIN: "XEP_0055",
50 C.PI_HANDLER: "no", 52 C.PI_HANDLER: "no",
51 C.PI_DESCRIPTION: _("""Implementation of Jabber Search""") 53 C.PI_DESCRIPTION: _("""Implementation of Jabber Search"""),
52 } 54 }
53 55
54 # config file parameters 56 # config file parameters
55 CONFIG_SECTION = "plugin search" 57 CONFIG_SECTION = "plugin search"
56 CONFIG_SERVICE_LIST = "service_list" 58 CONFIG_SERVICE_LIST = "service_list"
57 59
58 DEFAULT_SERVICE_LIST = ["salut.libervia.org"] 60 DEFAULT_SERVICE_LIST = ["salut.libervia.org"]
59 61
60 FIELD_SINGLE = "field_single" # single text field for the simple search 62 FIELD_SINGLE = "field_single" # single text field for the simple search
61 FIELD_CURRENT_SERVICE = "current_service_jid" # read-only text field for the advanced search 63 FIELD_CURRENT_SERVICE = (
64 "current_service_jid"
65 ) # read-only text field for the advanced search
66
62 67
63 class XEP_0055(object): 68 class XEP_0055(object):
64
65 def __init__(self, host): 69 def __init__(self, host):
66 log.info(_("Jabber search plugin initialization")) 70 log.info(_("Jabber search plugin initialization"))
67 self.host = host 71 self.host = host
68 72
69 # default search services (config file + hard-coded lists) 73 # default search services (config file + hard-coded lists)
70 self.services = [jid.JID(entry) for entry in host.memory.getConfig(CONFIG_SECTION, CONFIG_SERVICE_LIST, DEFAULT_SERVICE_LIST)] 74 self.services = [
71 75 jid.JID(entry)
72 host.bridge.addMethod("searchGetFieldsUI", ".plugin", in_sign='ss', out_sign='s', 76 for entry in host.memory.getConfig(
73 method=self._getFieldsUI, 77 CONFIG_SECTION, CONFIG_SERVICE_LIST, DEFAULT_SERVICE_LIST
74 async=True) 78 )
75 host.bridge.addMethod("searchRequest", ".plugin", in_sign='sa{ss}s', out_sign='s', 79 ]
76 method=self._searchRequest, 80
77 async=True) 81 host.bridge.addMethod(
82 "searchGetFieldsUI",
83 ".plugin",
84 in_sign="ss",
85 out_sign="s",
86 method=self._getFieldsUI,
87 async=True,
88 )
89 host.bridge.addMethod(
90 "searchRequest",
91 ".plugin",
92 in_sign="sa{ss}s",
93 out_sign="s",
94 method=self._searchRequest,
95 async=True,
96 )
78 97
79 self.__search_menu_id = host.registerCallback(self._getMainUI, with_data=True) 98 self.__search_menu_id = host.registerCallback(self._getMainUI, with_data=True)
80 host.importMenu((D_("Contacts"), D_("Search directory")), self._getMainUI, security_limit=1, help_string=D_("Search user directory")) 99 host.importMenu(
100 (D_("Contacts"), D_("Search directory")),
101 self._getMainUI,
102 security_limit=1,
103 help_string=D_("Search user directory"),
104 )
81 105
82 def _getHostServices(self, profile): 106 def _getHostServices(self, profile):
83 """Return the jabber search services associated to the user host. 107 """Return the jabber search services associated to the user host.
84 108
85 @param profile (unicode): %(doc_profile)s 109 @param profile (unicode): %(doc_profile)s
87 """ 111 """
88 client = self.host.getClient(profile) 112 client = self.host.getClient(profile)
89 d = self.host.findFeaturesSet(client, [NS_SEARCH]) 113 d = self.host.findFeaturesSet(client, [NS_SEARCH])
90 return d.addCallback(lambda set_: list(set_)) 114 return d.addCallback(lambda set_: list(set_))
91 115
92
93 ## Main search UI (menu item callback) ## 116 ## Main search UI (menu item callback) ##
94
95 117
96 def _getMainUI(self, raw_data, profile): 118 def _getMainUI(self, raw_data, profile):
97 """Get the XMLUI for selecting a service and searching the directory. 119 """Get the XMLUI for selecting a service and searching the directory.
98 120
99 @param raw_data (dict): data received from the frontend 121 @param raw_data (dict): data received from the frontend
113 @return: a deferred XMLUI string representation 135 @return: a deferred XMLUI string representation
114 """ 136 """
115 # extend services offered by user's server with the default services 137 # extend services offered by user's server with the default services
116 services.extend([service for service in self.services if service not in services]) 138 services.extend([service for service in self.services if service not in services])
117 data = xml_tools.XMLUIResult2DataFormResult(raw_data) 139 data = xml_tools.XMLUIResult2DataFormResult(raw_data)
118 main_ui = xml_tools.XMLUI(C.XMLUI_WINDOW, container="tabs", title=_("Search users"), submit_id=self.__search_menu_id) 140 main_ui = xml_tools.XMLUI(
141 C.XMLUI_WINDOW,
142 container="tabs",
143 title=_("Search users"),
144 submit_id=self.__search_menu_id,
145 )
119 146
120 d = self._addSimpleSearchUI(services, main_ui, data, profile) 147 d = self._addSimpleSearchUI(services, main_ui, data, profile)
121 d.addCallback(lambda dummy: self._addAdvancedSearchUI(services, main_ui, data, profile)) 148 d.addCallback(
122 return d.addCallback(lambda dummy: {'xmlui': main_ui.toXml()}) 149 lambda dummy: self._addAdvancedSearchUI(services, main_ui, data, profile)
150 )
151 return d.addCallback(lambda dummy: {"xmlui": main_ui.toXml()})
123 152
124 def _addSimpleSearchUI(self, services, main_ui, data, profile): 153 def _addSimpleSearchUI(self, services, main_ui, data, profile):
125 """Add to the main UI a tab for the simple search. 154 """Add to the main UI a tab for the simple search.
126 155
127 Display a single input field and search on the main service (it actually does one search per search field and then compile the results). 156 Display a single input field and search on the main service (it actually does one search per search field and then compile the results).
131 @param data (dict): form data without SAT_FORM_PREFIX 160 @param data (dict): form data without SAT_FORM_PREFIX
132 @param profile (unicode): %(doc_profile)s 161 @param profile (unicode): %(doc_profile)s
133 162
134 @return: a dummy Deferred 163 @return: a dummy Deferred
135 """ 164 """
136 service_jid = services[0] # TODO: search on all the given services, not only the first one 165 service_jid = services[
137 166 0
138 form = data_form.Form('form', formNamespace=NS_SEARCH) 167 ] # TODO: search on all the given services, not only the first one
139 form.addField(data_form.Field('text-single', FIELD_SINGLE, label=_('Search for'), value=data.get(FIELD_SINGLE, ''))) 168
140 169 form = data_form.Form("form", formNamespace=NS_SEARCH)
141 sub_cont = main_ui.main_container.addTab("simple_search", label=_("Simple search"), container=xml_tools.VerticalContainer) 170 form.addField(
171 data_form.Field(
172 "text-single",
173 FIELD_SINGLE,
174 label=_("Search for"),
175 value=data.get(FIELD_SINGLE, ""),
176 )
177 )
178
179 sub_cont = main_ui.main_container.addTab(
180 "simple_search",
181 label=_("Simple search"),
182 container=xml_tools.VerticalContainer,
183 )
142 main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui))) 184 main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui)))
143 xml_tools.dataForm2Widgets(main_ui, form) 185 xml_tools.dataForm2Widgets(main_ui, form)
144 186
145 # FIXME: add colspan attribute to divider? (we are in a PairsContainer) 187 # FIXME: add colspan attribute to divider? (we are in a PairsContainer)
146 main_ui.addDivider('blank') 188 main_ui.addDivider("blank")
147 main_ui.addDivider('blank') # here we added a blank line before the button 189 main_ui.addDivider("blank") # here we added a blank line before the button
148 main_ui.addDivider('blank') 190 main_ui.addDivider("blank")
149 main_ui.addButton(self.__search_menu_id, _("Search"), (FIELD_SINGLE,)) 191 main_ui.addButton(self.__search_menu_id, _("Search"), (FIELD_SINGLE,))
150 main_ui.addDivider('blank') 192 main_ui.addDivider("blank")
151 main_ui.addDivider('blank') # a blank line again after the button 193 main_ui.addDivider("blank") # a blank line again after the button
152 194
153 simple_data = {key: value for key, value in data.iteritems() if key in (FIELD_SINGLE,)} 195 simple_data = {
196 key: value for key, value in data.iteritems() if key in (FIELD_SINGLE,)
197 }
154 if simple_data: 198 if simple_data:
155 log.debug("Simple search with %s on %s" % (simple_data, service_jid)) 199 log.debug("Simple search with %s on %s" % (simple_data, service_jid))
156 sub_cont.parent.setSelected(True) 200 sub_cont.parent.setSelected(True)
157 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui))) 201 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
158 main_ui.addDivider('dash') 202 main_ui.addDivider("dash")
159 d = self.searchRequest(service_jid, simple_data, profile) 203 d = self.searchRequest(service_jid, simple_data, profile)
160 d.addCallbacks(lambda elt: self._displaySearchResult(main_ui, elt), 204 d.addCallbacks(
161 lambda failure: main_ui.addText(failure.getErrorMessage())) 205 lambda elt: self._displaySearchResult(main_ui, elt),
206 lambda failure: main_ui.addText(failure.getErrorMessage()),
207 )
162 return d 208 return d
163 209
164 return defer.succeed(None) 210 return defer.succeed(None)
165 211
166 def _addAdvancedSearchUI(self, services, main_ui, data, profile): 212 def _addAdvancedSearchUI(self, services, main_ui, data, profile):
173 @param data (dict): form data without SAT_FORM_PREFIX 219 @param data (dict): form data without SAT_FORM_PREFIX
174 @param profile (unicode): %(doc_profile)s 220 @param profile (unicode): %(doc_profile)s
175 221
176 @return: a dummy Deferred 222 @return: a dummy Deferred
177 """ 223 """
178 sub_cont = main_ui.main_container.addTab("advanced_search", label=_("Advanced search"), container=xml_tools.VerticalContainer) 224 sub_cont = main_ui.main_container.addTab(
179 service_selection_fields = ['service_jid', 'service_jid_extra'] 225 "advanced_search",
180 226 label=_("Advanced search"),
181 if 'service_jid_extra' in data: 227 container=xml_tools.VerticalContainer,
228 )
229 service_selection_fields = ["service_jid", "service_jid_extra"]
230
231 if "service_jid_extra" in data:
182 # refresh button has been pushed, select the tab 232 # refresh button has been pushed, select the tab
183 sub_cont.parent.setSelected(True) 233 sub_cont.parent.setSelected(True)
184 # get the selected service 234 # get the selected service
185 service_jid_s = data.get('service_jid_extra', '') 235 service_jid_s = data.get("service_jid_extra", "")
186 if not service_jid_s: 236 if not service_jid_s:
187 service_jid_s = data.get('service_jid', unicode(services[0])) 237 service_jid_s = data.get("service_jid", unicode(services[0]))
188 log.debug("Refreshing search fields for %s" % service_jid_s) 238 log.debug("Refreshing search fields for %s" % service_jid_s)
189 else: 239 else:
190 service_jid_s = data.get(FIELD_CURRENT_SERVICE, unicode(services[0])) 240 service_jid_s = data.get(FIELD_CURRENT_SERVICE, unicode(services[0]))
191 services_s = [unicode(service) for service in services] 241 services_s = [unicode(service) for service in services]
192 if service_jid_s not in services_s: 242 if service_jid_s not in services_s:
193 services_s.append(service_jid_s) 243 services_s.append(service_jid_s)
194 244
195 main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui))) 245 main_ui.changeContainer(sub_cont.append(xml_tools.PairsContainer(main_ui)))
196 main_ui.addLabel(_("Search on")) 246 main_ui.addLabel(_("Search on"))
197 main_ui.addList('service_jid', options=services_s, selected=service_jid_s) 247 main_ui.addList("service_jid", options=services_s, selected=service_jid_s)
198 main_ui.addLabel(_("Other service")) 248 main_ui.addLabel(_("Other service"))
199 main_ui.addString(name='service_jid_extra') 249 main_ui.addString(name="service_jid_extra")
200 250
201 # FIXME: add colspan attribute to divider? (we are in a PairsContainer) 251 # FIXME: add colspan attribute to divider? (we are in a PairsContainer)
202 main_ui.addDivider('blank') 252 main_ui.addDivider("blank")
203 main_ui.addDivider('blank') # here we added a blank line before the button 253 main_ui.addDivider("blank") # here we added a blank line before the button
204 main_ui.addDivider('blank') 254 main_ui.addDivider("blank")
205 main_ui.addButton(self.__search_menu_id, _("Refresh fields"), service_selection_fields) 255 main_ui.addButton(
206 main_ui.addDivider('blank') 256 self.__search_menu_id, _("Refresh fields"), service_selection_fields
207 main_ui.addDivider('blank') # a blank line again after the button 257 )
258 main_ui.addDivider("blank")
259 main_ui.addDivider("blank") # a blank line again after the button
208 main_ui.addLabel(_("Displaying the search form for")) 260 main_ui.addLabel(_("Displaying the search form for"))
209 main_ui.addString(name=FIELD_CURRENT_SERVICE, value=service_jid_s, read_only=True) 261 main_ui.addString(name=FIELD_CURRENT_SERVICE, value=service_jid_s, read_only=True)
210 main_ui.addDivider('dash') 262 main_ui.addDivider("dash")
211 main_ui.addDivider('dash') 263 main_ui.addDivider("dash")
212 264
213 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui))) 265 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
214 service_jid = jid.JID(service_jid_s) 266 service_jid = jid.JID(service_jid_s)
215 d = self.getFieldsUI(service_jid, profile) 267 d = self.getFieldsUI(service_jid, profile)
216 d.addCallbacks(self._addAdvancedForm, lambda failure: main_ui.addText(failure.getErrorMessage()), 268 d.addCallbacks(
217 [service_jid, main_ui, sub_cont, data, profile]) 269 self._addAdvancedForm,
270 lambda failure: main_ui.addText(failure.getErrorMessage()),
271 [service_jid, main_ui, sub_cont, data, profile],
272 )
218 return d 273 return d
219 274
220 def _addAdvancedForm(self, form_elt, service_jid, main_ui, sub_cont, data, profile): 275 def _addAdvancedForm(self, form_elt, service_jid, main_ui, sub_cont, data, profile):
221 """Add the search form and the search results (if there is some to display). 276 """Add the search form and the search results (if there is some to display).
222 277
241 name = widget.getAttribute("name") 296 name = widget.getAttribute("name")
242 if adv_data.get(name): 297 if adv_data.get(name):
243 widget.setAttribute("value", adv_data[name]) 298 widget.setAttribute("value", adv_data[name])
244 299
245 # FIXME: add colspan attribute to divider? (we are in a PairsContainer) 300 # FIXME: add colspan attribute to divider? (we are in a PairsContainer)
246 main_ui.addDivider('blank') 301 main_ui.addDivider("blank")
247 main_ui.addDivider('blank') # here we added a blank line before the button 302 main_ui.addDivider("blank") # here we added a blank line before the button
248 main_ui.addDivider('blank') 303 main_ui.addDivider("blank")
249 main_ui.addButton(self.__search_menu_id, _("Search"), adv_fields + [FIELD_CURRENT_SERVICE]) 304 main_ui.addButton(
250 main_ui.addDivider('blank') 305 self.__search_menu_id, _("Search"), adv_fields + [FIELD_CURRENT_SERVICE]
251 main_ui.addDivider('blank') # a blank line again after the button 306 )
307 main_ui.addDivider("blank")
308 main_ui.addDivider("blank") # a blank line again after the button
252 309
253 if adv_data: # display the search results 310 if adv_data: # display the search results
254 log.debug("Advanced search with %s on %s" % (adv_data, service_jid)) 311 log.debug("Advanced search with %s on %s" % (adv_data, service_jid))
255 sub_cont.parent.setSelected(True) 312 sub_cont.parent.setSelected(True)
256 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui))) 313 main_ui.changeContainer(sub_cont.append(xml_tools.VerticalContainer(main_ui)))
257 main_ui.addDivider('dash') 314 main_ui.addDivider("dash")
258 d = self.searchRequest(service_jid, adv_data, profile) 315 d = self.searchRequest(service_jid, adv_data, profile)
259 d.addCallbacks(lambda elt: self._displaySearchResult(main_ui, elt), 316 d.addCallbacks(
260 lambda failure: main_ui.addText(failure.getErrorMessage())) 317 lambda elt: self._displaySearchResult(main_ui, elt),
318 lambda failure: main_ui.addText(failure.getErrorMessage()),
319 )
261 return d 320 return d
262 321
263 return defer.succeed(None) 322 return defer.succeed(None)
264
265 323
266 def _displaySearchResult(self, main_ui, elt): 324 def _displaySearchResult(self, main_ui, elt):
267 """Display the search results. 325 """Display the search results.
268 326
269 @param main_ui (XMLUI): the main XMLUI instance 327 @param main_ui (XMLUI): the main XMLUI instance
275 values = {} 333 values = {}
276 for i in range(len(xmlui_data)): 334 for i in range(len(xmlui_data)):
277 header = headers.keys()[i % len(headers)] 335 header = headers.keys()[i % len(headers)]
278 widget_type, widget_args, widget_kwargs = xmlui_data[i] 336 widget_type, widget_args, widget_kwargs = xmlui_data[i]
279 value = widget_args[0] 337 value = widget_args[0]
280 values.setdefault(header, []).append(jid.JID(value) if header == "jid" else value) 338 values.setdefault(header, []).append(
339 jid.JID(value) if header == "jid" else value
340 )
281 main_ui.addJidsList(jids=values["jid"], name=D_(u"Search results")) 341 main_ui.addJidsList(jids=values["jid"], name=D_(u"Search results"))
282 # TODO: also display the values other than JID 342 # TODO: also display the values other than JID
283 else: 343 else:
284 xml_tools.XMLUIData2AdvancedList(main_ui, headers, xmlui_data) 344 xml_tools.XMLUIData2AdvancedList(main_ui, headers, xmlui_data)
285 else: 345 else:
286 main_ui.addText(D_("The search gave no result")) 346 main_ui.addText(D_("The search gave no result"))
287 347
288
289 ## Retrieve the search fields ## 348 ## Retrieve the search fields ##
290
291 349
292 def _getFieldsUI(self, to_jid_s, profile_key): 350 def _getFieldsUI(self, to_jid_s, profile_key):
293 """Ask a service to send us the list of the form fields it manages. 351 """Ask a service to send us the list of the form fields it manages.
294 352
295 @param to_jid_s (unicode): XEP-0055 compliant search entity 353 @param to_jid_s (unicode): XEP-0055 compliant search entity
306 @param to_jid (jid.JID): XEP-0055 compliant search entity 364 @param to_jid (jid.JID): XEP-0055 compliant search entity
307 @param profile_key (unicode): %(doc_profile_key)s 365 @param profile_key (unicode): %(doc_profile_key)s
308 @return: a deferred domish.Element 366 @return: a deferred domish.Element
309 """ 367 """
310 client = self.host.getClient(profile_key) 368 client = self.host.getClient(profile_key)
311 fields_request = IQ(client.xmlstream, 'get') 369 fields_request = IQ(client.xmlstream, "get")
312 fields_request["from"] = client.jid.full() 370 fields_request["from"] = client.jid.full()
313 fields_request["to"] = to_jid.full() 371 fields_request["to"] = to_jid.full()
314 fields_request.addElement('query', NS_SEARCH) 372 fields_request.addElement("query", NS_SEARCH)
315 d = fields_request.send(to_jid.full()) 373 d = fields_request.send(to_jid.full())
316 d.addCallbacks(self._getFieldsUICb, self._getFieldsUIEb) 374 d.addCallbacks(self._getFieldsUICb, self._getFieldsUIEb)
317 return d 375 return d
318 376
319 def _getFieldsUICb(self, answer): 377 def _getFieldsUICb(self, answer):
321 379
322 @param answer (domish.Element): search query element 380 @param answer (domish.Element): search query element
323 @return: domish.Element 381 @return: domish.Element
324 """ 382 """
325 try: 383 try:
326 query_elts = answer.elements('jabber:iq:search', 'query').next() 384 query_elts = answer.elements("jabber:iq:search", "query").next()
327 except StopIteration: 385 except StopIteration:
328 log.info(_("No query element found")) 386 log.info(_("No query element found"))
329 raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC 387 raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
330 try: 388 try:
331 form_elt = query_elts.elements(data_form.NS_X_DATA, 'x').next() 389 form_elt = query_elts.elements(data_form.NS_X_DATA, "x").next()
332 except StopIteration: 390 except StopIteration:
333 log.info(_("No data form found")) 391 log.info(_("No data form found"))
334 raise NotImplementedError("Only search through data form is implemented so far") 392 raise NotImplementedError(
393 "Only search through data form is implemented so far"
394 )
335 return form_elt 395 return form_elt
336 396
337 def _getFieldsUIEb(self, failure): 397 def _getFieldsUIEb(self, failure):
338 """Errback to self.getFieldsUI. 398 """Errback to self.getFieldsUI.
339 399
341 @raise: the unchanged defer.failure.Failure 401 @raise: the unchanged defer.failure.Failure
342 """ 402 """
343 log.info(_("Fields request failure: %s") % unicode(failure.getErrorMessage())) 403 log.info(_("Fields request failure: %s") % unicode(failure.getErrorMessage()))
344 raise failure 404 raise failure
345 405
346
347 ## Do the search ## 406 ## Do the search ##
348
349 407
350 def _searchRequest(self, to_jid_s, search_data, profile_key): 408 def _searchRequest(self, to_jid_s, search_data, profile_key):
351 """Actually do a search, according to filled data. 409 """Actually do a search, according to filled data.
352 410
353 @param to_jid_s (unicode): XEP-0055 compliant search entity 411 @param to_jid_s (unicode): XEP-0055 compliant search entity
368 @return: a deferred domish.Element 426 @return: a deferred domish.Element
369 """ 427 """
370 if FIELD_SINGLE in search_data: 428 if FIELD_SINGLE in search_data:
371 value = search_data[FIELD_SINGLE] 429 value = search_data[FIELD_SINGLE]
372 d = self.getFieldsUI(to_jid, profile_key) 430 d = self.getFieldsUI(to_jid, profile_key)
373 d.addCallback(lambda elt: self.searchRequestMulti(to_jid, value, elt, profile_key)) 431 d.addCallback(
432 lambda elt: self.searchRequestMulti(to_jid, value, elt, profile_key)
433 )
374 return d 434 return d
375 435
376 client = self.host.getClient(profile_key) 436 client = self.host.getClient(profile_key)
377 search_request = IQ(client.xmlstream, 'set') 437 search_request = IQ(client.xmlstream, "set")
378 search_request["from"] = client.jid.full() 438 search_request["from"] = client.jid.full()
379 search_request["to"] = to_jid.full() 439 search_request["to"] = to_jid.full()
380 query_elt = search_request.addElement('query', NS_SEARCH) 440 query_elt = search_request.addElement("query", NS_SEARCH)
381 x_form = data_form.Form('submit', formNamespace=NS_SEARCH) 441 x_form = data_form.Form("submit", formNamespace=NS_SEARCH)
382 x_form.makeFields(search_data) 442 x_form.makeFields(search_data)
383 query_elt.addChild(x_form.toElement()) 443 query_elt.addChild(x_form.toElement())
384 # TODO: XEP-0059 could be used here (with the needed new method attributes) 444 # TODO: XEP-0059 could be used here (with the needed new method attributes)
385 d = search_request.send(to_jid.full()) 445 d = search_request.send(to_jid.full())
386 d.addCallbacks(self._searchOk, self._searchErr) 446 d.addCallbacks(self._searchOk, self._searchErr)
404 def cb(result): # return the results compiled in one domish element 464 def cb(result): # return the results compiled in one domish element
405 result_elt = None 465 result_elt = None
406 for success, form_elt in result: 466 for success, form_elt in result:
407 if not success: 467 if not success:
408 continue 468 continue
409 if result_elt is None: # the result element is built over the first answer 469 if (
470 result_elt is None
471 ): # the result element is built over the first answer
410 result_elt = form_elt 472 result_elt = form_elt
411 continue 473 continue
412 for item_elt in form_elt.elements('jabber:x:data', 'item'): 474 for item_elt in form_elt.elements("jabber:x:data", "item"):
413 result_elt.addChild(item_elt) 475 result_elt.addChild(item_elt)
414 if result_elt is None: 476 if result_elt is None:
415 raise defer.failure.Failure(DataError(_("The search could not be performed"))) 477 raise defer.failure.Failure(
478 DataError(_("The search could not be performed"))
479 )
416 return result_elt 480 return result_elt
417 481
418 return defer.DeferredList(d_list).addCallback(cb) 482 return defer.DeferredList(d_list).addCallback(cb)
419 483
420 def _searchOk(self, answer): 484 def _searchOk(self, answer):
422 486
423 @param answer (domish.Element): search query element 487 @param answer (domish.Element): search query element
424 @return: domish.Element 488 @return: domish.Element
425 """ 489 """
426 try: 490 try:
427 query_elts = answer.elements('jabber:iq:search', 'query').next() 491 query_elts = answer.elements("jabber:iq:search", "query").next()
428 except StopIteration: 492 except StopIteration:
429 log.info(_("No query element found")) 493 log.info(_("No query element found"))
430 raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC 494 raise DataError # FIXME: StanzaError is probably more appropriate, check the RFC
431 try: 495 try:
432 form_elt = query_elts.elements(data_form.NS_X_DATA, 'x').next() 496 form_elt = query_elts.elements(data_form.NS_X_DATA, "x").next()
433 except StopIteration: 497 except StopIteration:
434 log.info(_("No data form found")) 498 log.info(_("No data form found"))
435 raise NotImplementedError("Only search through data form is implemented so far") 499 raise NotImplementedError(
500 "Only search through data form is implemented so far"
501 )
436 return form_elt 502 return form_elt
437 503
438 def _searchErr(self, failure): 504 def _searchErr(self, failure):
439 """Errback to self.searchRequest. 505 """Errback to self.searchRequest.
440 506
451 def __init__(self, plugin_parent, profile): 517 def __init__(self, plugin_parent, profile):
452 self.plugin_parent = plugin_parent 518 self.plugin_parent = plugin_parent
453 self.host = plugin_parent.host 519 self.host = plugin_parent.host
454 self.profile = profile 520 self.profile = profile
455 521
456 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 522 def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
457 return [disco.DiscoFeature(NS_SEARCH)] 523 return [disco.DiscoFeature(NS_SEARCH)]
458 524
459 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 525 def getDiscoItems(self, requestor, target, nodeIdentifier=""):
460 return [] 526 return []
461