comparison src/stdui/ui_contact_list.py @ 987:3a96920c07b7

core, frontends: unify the roster management UIs in sat/stdui/ui_contact_list.py
author souliane <souliane@mailoo.org>
date Thu, 03 Apr 2014 16:10:44 +0200
parents
children f8a8434dbac7
comparison
equal deleted inserted replaced
986:224cafc67324 987:3a96920c07b7
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # SAT standard user interface for managing contacts
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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 import re
26
27
28 class ContactList(object):
29 """Add, update and remove contacts."""
30
31 def __init__(self, host):
32 self.host = host
33 self.__add_id = host.registerCallback(self._addContact, with_data=True)
34 self.__update_id = host.registerCallback(self._updateContact, with_data=True)
35 self.__confirm_delete_id = host.registerCallback(self._getConfirmRemoveXMLUI, with_data=True)
36
37 host.importMenu((D_("Contacts"), D_("Add contact")), self._getAddDialogXMLUI, security_limit=2, help_string=D_("Add contact"))
38 host.importMenu((D_("Contacts"), D_("Update contact")), self._getUpdateDialogXMLUI, security_limit=2, help_string=D_("Update contact"))
39 host.importMenu((D_("Contacts"), D_("Remove contact")), self._getRemoveDialogXMLUI, security_limit=2, help_string=D_("Remove contact"))
40
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.userhost() for contact in client.roster.getBareJids()]
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, style=['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 contact = data[xml_tools.formEscape('contact_jid')]
213 cb = lambda data, profile: self._deleteContact(jid.JID(contact), profile)
214 delete_id = self.host.registerCallback(cb, with_data=True, one_shot=True)
215 form_ui = xml_tools.XMLUI("form", title=D_("Delete contact"), submit_id=delete_id)
216 form_ui.addText(D_("Are you sure you want to remove %s from your contact list?") % contact)
217 return {'xmlui': form_ui.toXml()}
218
219 def _addContact(self, data, profile):
220 """Add the selected contact
221
222 @param data (dict)
223 @param profile: %(doc_profile)s
224 @return dict
225 """
226 contact_jid_s = data[xml_tools.formEscape('contact_jid')]
227 if not re.match(r'^.+@.+\..+', contact_jid_s, re.IGNORECASE):
228 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.onFormSubmitted)
229 data['selected_groups'] = data[xml_tools.formEscape('groups_list')].split('\t')
230 options = {'id': self.__add_id,
231 'title': D_('Add contact'),
232 'contact_text': D_('Please enter a valid JID (like "contact@%s"):') % self.default_host,
233 }
234 return self.getDialogXMLUI(options, data, profile)
235 contact_jid = jid.JID(contact_jid_s)
236 self.host.addContact(contact_jid, profile_key=profile)
237 return self._updateContact(data, profile) # after adding, updating
238
239 def _updateContact(self, data, profile):
240 """Update the selected contact
241
242 @param data (dict)
243 @param profile: %(doc_profile)s
244 @return dict
245 """
246 contact_jid = jid.JID(data[xml_tools.formEscape('contact_jid')])
247 # TODO: replace '\t' by a constant (see tools.xmlui.XMLUI.onFormSubmitted)
248 groups = data[xml_tools.formEscape('groups_list')].split('\t')
249 self.host.updateContact(contact_jid, name='', groups=groups, profile_key=profile)
250 return {}
251
252 def _deleteContact(self, contact_jid, profile):
253 """Delete the selected contact
254
255 @param contact_jid (JID)
256 @param profile: %(doc_profile)s
257 @return dict
258 """
259 self.host.delContact(contact_jid, profile_key=profile)
260 return {}