Mercurial > libervia-backend
comparison sat/stdui/ui_contact_list.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | src/stdui/ui_contact_list.py@0046283a285d |
children | 56f94936df1e |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT standard user interface for managing contacts | |
5 # Copyright (C) 2009-2018 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 sat.core.i18n import _, D_ | |
21 from sat.core.constants import Const as C | |
22 from sat.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.registerCallback(self._addContact, with_data=True) | |
33 self.__update_id = host.registerCallback(self._updateContact, with_data=True) | |
34 self.__confirm_delete_id = host.registerCallback(self._getConfirmRemoveXMLUI, with_data=True) | |
35 | |
36 host.importMenu((D_("Contacts"), D_("Add contact")), self._getAddDialogXMLUI, security_limit=2, help_string=D_("Add contact")) | |
37 host.importMenu((D_("Contacts"), D_("Update contact")), self._getUpdateDialogXMLUI, security_limit=2, help_string=D_("Update contact")) | |
38 host.importMenu((D_("Contacts"), D_("Remove contact")), self._getRemoveDialogXMLUI, security_limit=2, help_string=D_("Remove contact")) | |
39 | |
40 # FIXME: a plugin should not be used here, and current profile's jid host would be better than installation wise host | |
41 if 'MISC-ACCOUNT' in self.host.plugins: | |
42 self.default_host = self.host.plugins['MISC-ACCOUNT'].getNewAccountDomain() | |
43 else: | |
44 self.default_host = 'example.net' | |
45 | |
46 def getContacts(self, profile): | |
47 """Return a sorted list of the contacts for that profile | |
48 | |
49 @param profile: %(doc_profile)s | |
50 @return: list[string] | |
51 """ | |
52 client = self.host.getClient(profile) | |
53 ret = [contact.full() for contact in client.roster.getJids()] | |
54 ret.sort() | |
55 return ret | |
56 | |
57 def getGroups(self, new_groups=None, profile=C.PROF_KEY_NONE): | |
58 """Return a sorted list of the groups for that profile | |
59 | |
60 @param new_group (list): add these groups to the existing ones | |
61 @param profile: %(doc_profile)s | |
62 @return: list[string] | |
63 """ | |
64 client = self.host.getClient(profile) | |
65 ret = client.roster.getGroups() | |
66 ret.sort() | |
67 ret.extend([group for group in new_groups if group not in ret]) | |
68 return ret | |
69 | |
70 def getGroupsOfContact(self, user_jid_s, profile): | |
71 """Return all the groups of the given contact | |
72 | |
73 @param user_jid_s (string) | |
74 @param profile: %(doc_profile)s | |
75 @return: list[string] | |
76 """ | |
77 client = self.host.getClient(profile) | |
78 return client.roster.getItem(jid.JID(user_jid_s)).groups | |
79 | |
80 def getGroupsOfAllContacts(self, profile): | |
81 """Return a mapping between the contacts and their groups | |
82 | |
83 @param profile: %(doc_profile)s | |
84 @return: dict (key: string, value: list[string]): | |
85 - key: the JID userhost | |
86 - value: list of groups | |
87 """ | |
88 client = self.host.getClient(profile) | |
89 return {item.jid.userhost(): item.groups for item in client.roster.getItems()} | |
90 | |
91 def _data2elts(self, data): | |
92 """Convert a contacts data dict to minidom Elements | |
93 | |
94 @param data (dict) | |
95 @return list[Element] | |
96 """ | |
97 elts = [] | |
98 for key in data: | |
99 key_elt = Element('jid') | |
100 key_elt.setAttribute('name', key) | |
101 for value in data[key]: | |
102 value_elt = Element('group') | |
103 value_elt.setAttribute('name', value) | |
104 key_elt.childNodes.append(value_elt) | |
105 elts.append(key_elt) | |
106 return elts | |
107 | |
108 def getDialogXMLUI(self, options, data, profile): | |
109 """Generic method to return the XMLUI dialog for adding or updating a contact | |
110 | |
111 @param options (dict): parameters for the dialog, with the keys: | |
112 - 'id': the menu callback id | |
113 - 'title': deferred localized string | |
114 - 'contact_text': deferred localized string | |
115 @param data (dict) | |
116 @param profile: %(doc_profile)s | |
117 @return dict | |
118 """ | |
119 form_ui = xml_tools.XMLUI("form", title=options['title'], submit_id=options['id']) | |
120 if 'message' in data: | |
121 form_ui.addText(data['message']) | |
122 form_ui.addDivider('dash') | |
123 | |
124 form_ui.addText(options['contact_text']) | |
125 if options['id'] == self.__add_id: | |
126 contact = data.get(xml_tools.formEscape('contact_jid'), '@%s' % self.default_host) | |
127 form_ui.addString('contact_jid', value=contact) | |
128 elif options['id'] == self.__update_id: | |
129 contacts = self.getContacts(profile) | |
130 list_ = form_ui.addList('contact_jid', options=contacts, selected=contacts[0]) | |
131 elts = self._data2elts(self.getGroupsOfAllContacts(profile)) | |
132 list_.setInternalCallback('groups_of_contact', fields=['contact_jid', 'groups_list'], data_elts=elts) | |
133 | |
134 form_ui.addDivider('blank') | |
135 | |
136 form_ui.addText(_("Select in which groups your contact is:")) | |
137 selected_groups = [] | |
138 if 'selected_groups' in data: | |
139 selected_groups = data['selected_groups'] | |
140 elif options['id'] == self.__update_id: | |
141 try: | |
142 selected_groups = self.getGroupsOfContact(contacts[0], profile) | |
143 except IndexError: | |
144 pass | |
145 groups = self.getGroups(selected_groups, profile) | |
146 form_ui.addList('groups_list', options=groups, selected=selected_groups, styles=['multi']) | |
147 | |
148 adv_list = form_ui.changeContainer("advanced_list", columns=3, selectable='no') | |
149 form_ui.addLabel(D_("Add group")) | |
150 form_ui.addString("add_group") | |
151 button = form_ui.addButton('', value=D_('Add')) | |
152 button.setInternalCallback('move', fields=['add_group', 'groups_list']) | |
153 adv_list.end() | |
154 | |
155 form_ui.addDivider('blank') | |
156 return {'xmlui': form_ui.toXml()} | |
157 | |
158 def _getAddDialogXMLUI(self, data, profile): | |
159 """Get the dialog for adding contact | |
160 | |
161 @param data (dict) | |
162 @param profile: %(doc_profile)s | |
163 @return dict | |
164 """ | |
165 options = {'id': self.__add_id, | |
166 'title': D_('Add contact'), | |
167 'contact_text': D_("New contact identifier (JID):"), | |
168 } | |
169 return self.getDialogXMLUI(options, {}, profile) | |
170 | |
171 def _getUpdateDialogXMLUI(self, data, profile): | |
172 """Get the dialog for updating contact | |
173 | |
174 @param data (dict) | |
175 @param profile: %(doc_profile)s | |
176 @return dict | |
177 """ | |
178 if not self.getContacts(profile): | |
179 _dialog = xml_tools.XMLUI('popup', title=D_('Nothing to update')) | |
180 _dialog.addText(_('Your contact list is empty.')) | |
181 return {'xmlui': _dialog.toXml()} | |
182 | |
183 options = {'id': self.__update_id, | |
184 'title': D_('Update contact'), | |
185 'contact_text': D_("Which contact do you want to update?"), | |
186 } | |
187 return self.getDialogXMLUI(options, {}, profile) | |
188 | |
189 def _getRemoveDialogXMLUI(self, data, profile): | |
190 """Get the dialog for removing contact | |
191 | |
192 @param data (dict) | |
193 @param profile: %(doc_profile)s | |
194 @return dict | |
195 """ | |
196 if not self.getContacts(profile): | |
197 _dialog = xml_tools.XMLUI('popup', title=D_('Nothing to delete')) | |
198 _dialog.addText(_('Your contact list is empty.')) | |
199 return {'xmlui': _dialog.toXml()} | |
200 | |
201 form_ui = xml_tools.XMLUI("form", title=D_('Who do you want to remove from your contacts?'), submit_id=self.__confirm_delete_id) | |
202 form_ui.addList('contact_jid', options=self.getContacts(profile)) | |
203 return {'xmlui': form_ui.toXml()} | |
204 | |
205 def _getConfirmRemoveXMLUI(self, data, profile): | |
206 """Get the confirmation dialog for removing contact | |
207 | |
208 @param data (dict) | |
209 @param profile: %(doc_profile)s | |
210 @return dict | |
211 """ | |
212 if C.bool(data.get('cancelled', 'false')): | |
213 return {} | |
214 contact = data[xml_tools.formEscape('contact_jid')] | |
215 def delete_cb(data, profile): | |
216 if not C.bool(data.get('cancelled', 'false')): | |
217 self._deleteContact(jid.JID(contact), profile) | |
218 return {} | |
219 delete_id = self.host.registerCallback(delete_cb, with_data=True, one_shot=True) | |
220 form_ui = xml_tools.XMLUI("form", title=D_("Delete contact"), submit_id=delete_id) | |
221 form_ui.addText(D_("Are you sure you want to remove %s from your contact list?") % contact) | |
222 return {'xmlui': form_ui.toXml()} | |
223 | |
224 def _addContact(self, data, profile): | |
225 """Add the selected contact | |
226 | |
227 @param data (dict) | |
228 @param profile: %(doc_profile)s | |
229 @return dict | |
230 """ | |
231 if C.bool(data.get('cancelled', 'false')): | |
232 return {} | |
233 contact_jid_s = data[xml_tools.formEscape('contact_jid')] | |
234 try: | |
235 contact_jid = jid.JID(contact_jid_s) | |
236 except (RuntimeError, jid.InvalidFormat, AttributeError): | |
237 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.onFormSubmitted) | |
238 data['selected_groups'] = data[xml_tools.formEscape('groups_list')].split('\t') | |
239 options = {'id': self.__add_id, | |
240 'title': D_('Add contact'), | |
241 'contact_text': D_('Please enter a valid JID (like "contact@%s"):') % self.default_host, | |
242 } | |
243 return self.getDialogXMLUI(options, data, profile) | |
244 self.host.addContact(contact_jid, profile_key=profile) | |
245 return self._updateContact(data, profile) # after adding, updating | |
246 | |
247 def _updateContact(self, data, profile): | |
248 """Update the selected contact | |
249 | |
250 @param data (dict) | |
251 @param profile: %(doc_profile)s | |
252 @return dict | |
253 """ | |
254 if C.bool(data.get('cancelled', 'false')): | |
255 return {} | |
256 contact_jid = jid.JID(data[xml_tools.formEscape('contact_jid')]) | |
257 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.onFormSubmitted) | |
258 groups = data[xml_tools.formEscape('groups_list')].split('\t') | |
259 self.host.updateContact(contact_jid, name='', groups=groups, profile_key=profile) | |
260 return {} | |
261 | |
262 def _deleteContact(self, contact_jid, profile): | |
263 """Delete the selected contact | |
264 | |
265 @param contact_jid (JID) | |
266 @param profile: %(doc_profile)s | |
267 @return dict | |
268 """ | |
269 self.host.delContact(contact_jid, profile_key=profile) | |
270 return {} |