Mercurial > libervia-backend
comparison libervia/backend/stdui/ui_contact_list.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/stdui/ui_contact_list.py@524856bd7b19 |
children |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 | |
4 # SAT standard user interface for managing contacts | |
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
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/>. | |
19 | |
20 from libervia.backend.core.i18n import _, D_ | |
21 from libervia.backend.core.constants import Const as C | |
22 from libervia.backend.tools import xml_tools | |
23 from twisted.words.protocols.jabber import jid | |
24 from xml.dom.minidom import Element | |
25 | |
26 | |
27 class ContactList(object): | |
28 """Add, update and remove contacts.""" | |
29 | |
30 def __init__(self, host): | |
31 self.host = host | |
32 self.__add_id = host.register_callback(self._add_contact, with_data=True) | |
33 self.__update_id = host.register_callback(self._update_contact, with_data=True) | |
34 self.__confirm_delete_id = host.register_callback( | |
35 self._get_confirm_remove_xmlui, with_data=True | |
36 ) | |
37 | |
38 host.import_menu( | |
39 (D_("Contacts"), D_("Add contact")), | |
40 self._get_add_dialog_xmlui, | |
41 security_limit=2, | |
42 help_string=D_("Add contact"), | |
43 ) | |
44 host.import_menu( | |
45 (D_("Contacts"), D_("Update contact")), | |
46 self._get_update_dialog_xmlui, | |
47 security_limit=2, | |
48 help_string=D_("Update contact"), | |
49 ) | |
50 host.import_menu( | |
51 (D_("Contacts"), D_("Remove contact")), | |
52 self._get_remove_dialog_xmlui, | |
53 security_limit=2, | |
54 help_string=D_("Remove contact"), | |
55 ) | |
56 | |
57 # FIXME: a plugin should not be used here, and current profile's jid host would be better than installation wise host | |
58 if "MISC-ACCOUNT" in self.host.plugins: | |
59 self.default_host = self.host.plugins["MISC-ACCOUNT"].account_domain_new_get() | |
60 else: | |
61 self.default_host = "example.net" | |
62 | |
63 def contacts_get(self, profile): | |
64 """Return a sorted list of the contacts for that profile | |
65 | |
66 @param profile: %(doc_profile)s | |
67 @return: list[string] | |
68 """ | |
69 client = self.host.get_client(profile) | |
70 ret = [contact.full() for contact in client.roster.get_jids()] | |
71 ret.sort() | |
72 return ret | |
73 | |
74 def get_groups(self, new_groups=None, profile=C.PROF_KEY_NONE): | |
75 """Return a sorted list of the groups for that profile | |
76 | |
77 @param new_group (list): add these groups to the existing ones | |
78 @param profile: %(doc_profile)s | |
79 @return: list[string] | |
80 """ | |
81 client = self.host.get_client(profile) | |
82 ret = client.roster.get_groups() | |
83 ret.sort() | |
84 ret.extend([group for group in new_groups if group not in ret]) | |
85 return ret | |
86 | |
87 def get_groups_of_contact(self, user_jid_s, profile): | |
88 """Return all the groups of the given contact | |
89 | |
90 @param user_jid_s (string) | |
91 @param profile: %(doc_profile)s | |
92 @return: list[string] | |
93 """ | |
94 client = self.host.get_client(profile) | |
95 return client.roster.get_item(jid.JID(user_jid_s)).groups | |
96 | |
97 def get_groups_of_all_contacts(self, profile): | |
98 """Return a mapping between the contacts and their groups | |
99 | |
100 @param profile: %(doc_profile)s | |
101 @return: dict (key: string, value: list[string]): | |
102 - key: the JID userhost | |
103 - value: list of groups | |
104 """ | |
105 client = self.host.get_client(profile) | |
106 return {item.jid.userhost(): item.groups for item in client.roster.get_items()} | |
107 | |
108 def _data2elts(self, data): | |
109 """Convert a contacts data dict to minidom Elements | |
110 | |
111 @param data (dict) | |
112 @return list[Element] | |
113 """ | |
114 elts = [] | |
115 for key in data: | |
116 key_elt = Element("jid") | |
117 key_elt.setAttribute("name", key) | |
118 for value in data[key]: | |
119 value_elt = Element("group") | |
120 value_elt.setAttribute("name", value) | |
121 key_elt.childNodes.append(value_elt) | |
122 elts.append(key_elt) | |
123 return elts | |
124 | |
125 def get_dialog_xmlui(self, options, data, profile): | |
126 """Generic method to return the XMLUI dialog for adding or updating a contact | |
127 | |
128 @param options (dict): parameters for the dialog, with the keys: | |
129 - 'id': the menu callback id | |
130 - 'title': deferred localized string | |
131 - 'contact_text': deferred localized string | |
132 @param data (dict) | |
133 @param profile: %(doc_profile)s | |
134 @return dict | |
135 """ | |
136 form_ui = xml_tools.XMLUI("form", title=options["title"], submit_id=options["id"]) | |
137 if "message" in data: | |
138 form_ui.addText(data["message"]) | |
139 form_ui.addDivider("dash") | |
140 | |
141 form_ui.addText(options["contact_text"]) | |
142 if options["id"] == self.__add_id: | |
143 contact = data.get( | |
144 xml_tools.form_escape("contact_jid"), "@%s" % self.default_host | |
145 ) | |
146 form_ui.addString("contact_jid", value=contact) | |
147 elif options["id"] == self.__update_id: | |
148 contacts = self.contacts_get(profile) | |
149 list_ = form_ui.addList("contact_jid", options=contacts, selected=contacts[0]) | |
150 elts = self._data2elts(self.get_groups_of_all_contacts(profile)) | |
151 list_.set_internal_callback( | |
152 "groups_of_contact", fields=["contact_jid", "groups_list"], data_elts=elts | |
153 ) | |
154 | |
155 form_ui.addDivider("blank") | |
156 | |
157 form_ui.addText(_("Select in which groups your contact is:")) | |
158 selected_groups = [] | |
159 if "selected_groups" in data: | |
160 selected_groups = data["selected_groups"] | |
161 elif options["id"] == self.__update_id: | |
162 try: | |
163 selected_groups = self.get_groups_of_contact(contacts[0], profile) | |
164 except IndexError: | |
165 pass | |
166 groups = self.get_groups(selected_groups, profile) | |
167 form_ui.addList( | |
168 "groups_list", options=groups, selected=selected_groups, styles=["multi"] | |
169 ) | |
170 | |
171 adv_list = form_ui.change_container("advanced_list", columns=3, selectable="no") | |
172 form_ui.addLabel(D_("Add group")) | |
173 form_ui.addString("add_group") | |
174 button = form_ui.addButton("", value=D_("Add")) | |
175 button.set_internal_callback("move", fields=["add_group", "groups_list"]) | |
176 adv_list.end() | |
177 | |
178 form_ui.addDivider("blank") | |
179 return {"xmlui": form_ui.toXml()} | |
180 | |
181 def _get_add_dialog_xmlui(self, data, profile): | |
182 """Get the dialog for adding contact | |
183 | |
184 @param data (dict) | |
185 @param profile: %(doc_profile)s | |
186 @return dict | |
187 """ | |
188 options = { | |
189 "id": self.__add_id, | |
190 "title": D_("Add contact"), | |
191 "contact_text": D_("New contact identifier (JID):"), | |
192 } | |
193 return self.get_dialog_xmlui(options, {}, profile) | |
194 | |
195 def _get_update_dialog_xmlui(self, data, profile): | |
196 """Get the dialog for updating contact | |
197 | |
198 @param data (dict) | |
199 @param profile: %(doc_profile)s | |
200 @return dict | |
201 """ | |
202 if not self.contacts_get(profile): | |
203 _dialog = xml_tools.XMLUI("popup", title=D_("Nothing to update")) | |
204 _dialog.addText(_("Your contact list is empty.")) | |
205 return {"xmlui": _dialog.toXml()} | |
206 | |
207 options = { | |
208 "id": self.__update_id, | |
209 "title": D_("Update contact"), | |
210 "contact_text": D_("Which contact do you want to update?"), | |
211 } | |
212 return self.get_dialog_xmlui(options, {}, profile) | |
213 | |
214 def _get_remove_dialog_xmlui(self, data, profile): | |
215 """Get the dialog for removing contact | |
216 | |
217 @param data (dict) | |
218 @param profile: %(doc_profile)s | |
219 @return dict | |
220 """ | |
221 if not self.contacts_get(profile): | |
222 _dialog = xml_tools.XMLUI("popup", title=D_("Nothing to delete")) | |
223 _dialog.addText(_("Your contact list is empty.")) | |
224 return {"xmlui": _dialog.toXml()} | |
225 | |
226 form_ui = xml_tools.XMLUI( | |
227 "form", | |
228 title=D_("Who do you want to remove from your contacts?"), | |
229 submit_id=self.__confirm_delete_id, | |
230 ) | |
231 form_ui.addList("contact_jid", options=self.contacts_get(profile)) | |
232 return {"xmlui": form_ui.toXml()} | |
233 | |
234 def _get_confirm_remove_xmlui(self, data, profile): | |
235 """Get the confirmation dialog for removing contact | |
236 | |
237 @param data (dict) | |
238 @param profile: %(doc_profile)s | |
239 @return dict | |
240 """ | |
241 if C.bool(data.get("cancelled", "false")): | |
242 return {} | |
243 contact = data[xml_tools.form_escape("contact_jid")] | |
244 | |
245 def delete_cb(data, profile): | |
246 if not C.bool(data.get("cancelled", "false")): | |
247 self._delete_contact(jid.JID(contact), profile) | |
248 return {} | |
249 | |
250 delete_id = self.host.register_callback(delete_cb, with_data=True, one_shot=True) | |
251 form_ui = xml_tools.XMLUI("form", title=D_("Delete contact"), submit_id=delete_id) | |
252 form_ui.addText( | |
253 D_("Are you sure you want to remove %s from your contact list?") % contact | |
254 ) | |
255 return {"xmlui": form_ui.toXml()} | |
256 | |
257 def _add_contact(self, data, profile): | |
258 """Add the selected contact | |
259 | |
260 @param data (dict) | |
261 @param profile: %(doc_profile)s | |
262 @return dict | |
263 """ | |
264 if C.bool(data.get("cancelled", "false")): | |
265 return {} | |
266 contact_jid_s = data[xml_tools.form_escape("contact_jid")] | |
267 try: | |
268 contact_jid = jid.JID(contact_jid_s) | |
269 except (RuntimeError, jid.InvalidFormat, AttributeError): | |
270 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.on_form_submitted) | |
271 data["selected_groups"] = data[xml_tools.form_escape("groups_list")].split( | |
272 "\t" | |
273 ) | |
274 options = { | |
275 "id": self.__add_id, | |
276 "title": D_("Add contact"), | |
277 "contact_text": D_('Please enter a valid JID (like "contact@%s"):') | |
278 % self.default_host, | |
279 } | |
280 return self.get_dialog_xmlui(options, data, profile) | |
281 self.host.contact_add(contact_jid, profile_key=profile) | |
282 return self._update_contact(data, profile) # after adding, updating | |
283 | |
284 def _update_contact(self, data, profile): | |
285 """Update the selected contact | |
286 | |
287 @param data (dict) | |
288 @param profile: %(doc_profile)s | |
289 @return dict | |
290 """ | |
291 client = self.host.get_client(profile) | |
292 if C.bool(data.get("cancelled", "false")): | |
293 return {} | |
294 contact_jid = jid.JID(data[xml_tools.form_escape("contact_jid")]) | |
295 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.on_form_submitted) | |
296 groups = data[xml_tools.form_escape("groups_list")].split("\t") | |
297 self.host.contact_update(client, contact_jid, name="", groups=groups) | |
298 return {} | |
299 | |
300 def _delete_contact(self, contact_jid, profile): | |
301 """Delete the selected contact | |
302 | |
303 @param contact_jid (JID) | |
304 @param profile: %(doc_profile)s | |
305 @return dict | |
306 """ | |
307 self.host.contact_del(contact_jid, profile_key=profile) | |
308 return {} |