Mercurial > libervia-backend
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 |