Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0048.py @ 982:0e80ee1fe9af
plugin XEP-0048: bookmarks (first draft)
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 07 Apr 2014 16:22:35 +0200 |
parents | |
children | c34e0b2bbf08 |
comparison
equal
deleted
inserted
replaced
981:58a57ce5932a | 982:0e80ee1fe9af |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT plugin for Bookmarks (xep-0048) | |
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 import exceptions | |
22 from sat.memory.persistent import PersistentBinaryDict | |
23 from sat.tools import xml_tools | |
24 from logging import debug, info, warning, error | |
25 from twisted.words.xish import domish | |
26 from twisted.words.protocols.jabber import jid | |
27 | |
28 from twisted.internet import defer | |
29 | |
30 NS_BOOKMARKS = 'storage:bookmarks' | |
31 | |
32 PLUGIN_INFO = { | |
33 "name": "Bookmarks", | |
34 "import_name": "XEP-0048", | |
35 "type": "XEP", | |
36 "protocols": ["XEP-0048"], | |
37 "dependencies": ["XEP-0045"], | |
38 "recommendations": ["XEP-0049"], | |
39 "main": "XEP_0048", | |
40 "handler": "no", | |
41 "description": _("""Implementation of bookmarks""") | |
42 } | |
43 | |
44 | |
45 class XEP_0048(object): | |
46 MUC_TYPE = 'muc' | |
47 URL_TYPE = 'url' | |
48 MUC_KEY = 'jid' | |
49 URL_KEY = 'url' | |
50 MUC_ATTRS = ('autojoin', 'name') | |
51 URL_ATTRS = ('name',) | |
52 | |
53 def __init__(self, host): | |
54 info(_("Bookmarks plugin initialization")) | |
55 self.host = host | |
56 # self.__menu_id = host.registerCallback(self._bookmarksMenu, with_data=True) | |
57 self.__bm_save_id = host.registerCallback(self._bookmarksSaveCb, with_data=True) | |
58 host.importMenu((D_("Communication"), D_("bookmarks")), self._bookmarksMenu, security_limit=0, help_string=D_("Use and manage bookmarks")) | |
59 try: | |
60 self.private_plg = self.host.plugins["XEP-0049"] | |
61 except KeyError: | |
62 self.private_plg = None | |
63 | |
64 @defer.inlineCallbacks | |
65 def profileConnected(self, profile): | |
66 client = self.host.getClient(profile) | |
67 local = client.bookmarks_local = PersistentBinaryDict(NS_BOOKMARKS, profile) | |
68 yield local.load() | |
69 if not local: | |
70 local[XEP_0048.MUC_TYPE] = dict() | |
71 local[XEP_0048.URL_TYPE] = dict() | |
72 private = yield self._getServerBookmarks('private', profile) | |
73 pubsub = client.bookmarks_pubsub = None | |
74 | |
75 for bookmarks in (local, private, pubsub): | |
76 if bookmarks is not None: | |
77 for (room_jid, data) in bookmarks[XEP_0048.MUC_TYPE].items(): | |
78 if data.get('autojoin', 'false') == 'true': | |
79 nick = data.get('nick', client.jid.user) | |
80 self.host.plugins['XEP-0045'].join(room_jid, nick, {}, profile_key=client.profile) | |
81 | |
82 @defer.inlineCallbacks | |
83 def _getServerBookmarks(self, storage_type, profile): | |
84 """Get distants bookmarks | |
85 | |
86 update also the client.bookmarks_[type] key, with None if service is not available | |
87 @param storage_type: storage type, can be: | |
88 - 'private': XEP-0049 storage | |
89 - 'pubsub': XEP-0223 storage | |
90 @param profile: %(doc_profile)s | |
91 @return: data dictionary, or None if feature is not available | |
92 | |
93 @raise: exception.FeatureNotFound | |
94 """ | |
95 client = self.host.getClient(profile) | |
96 if storage_type == 'private': | |
97 try: | |
98 bookmarks_private_xml = yield self.private_plg.privateXMLGet('storage', NS_BOOKMARKS, profile) | |
99 data = client.bookmarks_private = self._bookmarkElt2Dict(bookmarks_private_xml) | |
100 except (exceptions.FeatureNotFound, AttributeError): | |
101 info(_("Private XML storage not available")) | |
102 data = client.bookmarks_private = None | |
103 elif storage_type == 'pubsub': | |
104 raise NotImplementedError | |
105 else: | |
106 raise ValueError("storage_type must be 'private' or 'pubsub'") | |
107 defer.returnValue(data) | |
108 | |
109 @defer.inlineCallbacks | |
110 def _setServerBookmarks(self, storage_type, bookmarks_elt, profile): | |
111 """Save bookmarks on server | |
112 | |
113 @param storage_type: storage type, can be: | |
114 - 'private': XEP-0049 storage | |
115 - 'pubsub': XEP-0223 storage | |
116 @param bookmarks_elt (domish.Element): bookmarks XML | |
117 @param profile: %(doc_profile)s | |
118 """ | |
119 if storage_type == 'private': | |
120 yield self.private_plg.privateXMLStore(bookmarks_elt, profile) | |
121 elif storage_type == 'pubsub': | |
122 raise NotImplementedError | |
123 else: | |
124 raise ValueError("storage_type must be 'private' or 'pubsub'") | |
125 | |
126 def _bookmarkElt2Dict(self, storage_elt): | |
127 """Parse bookmarks to get dictionary | |
128 @param storage_elt (domish.Element): bookmarks storage | |
129 @return (dict): bookmark data (key: bookmark type, value: list) where key can be: | |
130 - XEP_0048.MUC_TYPE | |
131 - XEP_0048.URL_TYPE | |
132 - value (dict): data as for addBookmark | |
133 """ | |
134 conf_data = {} | |
135 url_data = {} | |
136 | |
137 conference_elts = storage_elt.elements(NS_BOOKMARKS, 'conference') | |
138 for conference_elt in conference_elts: | |
139 try: | |
140 room_jid = jid.JID(conference_elt[XEP_0048.MUC_KEY]) | |
141 except KeyError: | |
142 warning ("invalid bookmark found, igoring it:\n%s" % conference_elt.toXml()) | |
143 continue | |
144 | |
145 data = conf_data[room_jid] = {} | |
146 | |
147 for attr in XEP_0048.MUC_ATTRS: | |
148 if conference_elt.hasAttribute(attr): | |
149 data[attr] = conference_elt[attr] | |
150 try: | |
151 data['nick'] = unicode(conference_elt.elements(NS_BOOKMARKS, 'nick').next()) | |
152 except StopIteration: | |
153 pass | |
154 # TODO: manage password (need to be secured, see XEP-0049 §4) | |
155 | |
156 url_elts = storage_elt.elements(NS_BOOKMARKS, 'url') | |
157 for url_elt in url_elts: | |
158 try: | |
159 url = url_elt[XEP_0048.URL_KEY] | |
160 except KeyError: | |
161 warning ("invalid bookmark found, igoring it:\n%s" % url_elt.toXml()) | |
162 continue | |
163 data = url_data[url] = {} | |
164 for attr in XEP_0048.URL_ATTRS: | |
165 if url_elt.hasAttribute(attr): | |
166 data[attr] = url_elt[attr] | |
167 | |
168 return {XEP_0048.MUC_TYPE: conf_data, XEP_0048.URL_TYPE: url_data} | |
169 | |
170 def _dict2BookmarkElt(self, type_, data): | |
171 """Construct a bookmark element from a data dict | |
172 @param data (dict): bookmark data (key: bookmark type, value: list) where key can be: | |
173 - XEP_0048.MUC_TYPE | |
174 - XEP_0048.URL_TYPE | |
175 - value (dict): data as for addBookmark | |
176 @return (domish.Element): bookmark element | |
177 """ | |
178 rooms_data = data.get(XEP_0048.MUC_TYPE, {}) | |
179 urls_data = data.get(XEP_0048.URL_TYPE, {}) | |
180 storage_elt = domish.Element((NS_BOOKMARKS, 'storage')) | |
181 for room_jid in rooms_data: | |
182 conference_elt = storage_elt.addElement('conference') | |
183 conference_elt[XEP_0048.MUC_KEY] = room_jid.full() | |
184 for attr in XEP_0048.MUC_ATTRS: | |
185 try: | |
186 conference_elt[attr] = rooms_data[room_jid][attr] | |
187 except KeyError: | |
188 pass | |
189 try: | |
190 conference_elt.addElement('nick', content=rooms_data[room_jid]['nick']) | |
191 except KeyError: | |
192 pass | |
193 | |
194 for url in urls_data: | |
195 url_elt = storage_elt.addElement('url') | |
196 url_elt[XEP_0048.URL_KEY] = url | |
197 for attr in XEP_0048.URL_ATTRS: | |
198 try: | |
199 url_elt[attr] = url[attr] | |
200 except KeyError: | |
201 pass | |
202 | |
203 return storage_elt | |
204 | |
205 def _bookmarksMenu(self, data, profile): | |
206 """ XMLUI activated by menu: return Gateways UI | |
207 @param profile: %(doc_profile)s | |
208 | |
209 """ | |
210 client = self.host.getClient(profile) | |
211 xmlui = xml_tools.XMLUI(title=_('Bookmarks manager')) | |
212 xmlui.addText(_("add a bookmark")) | |
213 xmlui.changeContainer("pairs") | |
214 xmlui.addLabel(_('Name')) | |
215 xmlui.addString('name') | |
216 xmlui.addLabel(_('jid')) | |
217 xmlui.addString('jid') | |
218 xmlui.addLabel(_('Nickname')) | |
219 xmlui.addString('nick', client.jid.user) | |
220 xmlui.addLabel(_('Autojoin')) | |
221 xmlui.addBool('autojoin') | |
222 xmlui.changeContainer("vertical") | |
223 xmlui.addButton(self.__bm_save_id, _("Save"), ('name', 'jid', 'nick', 'autojoin')) | |
224 return {'xmlui': xmlui.toXml()} | |
225 | |
226 def _bookmarksSaveCb(self, data, profile): | |
227 bm_data = xml_tools.XMLUIResult2DataFormResult(data) | |
228 try: | |
229 location = jid.JID(bm_data.pop('jid')) | |
230 except KeyError: | |
231 raise exceptions.InternalError("Can't find mandatory key") | |
232 d = self.addBookmark(XEP_0048.MUC_TYPE, location, bm_data, profile_key=profile) | |
233 d.addCallback(lambda dummy: {}) | |
234 return d | |
235 | |
236 @defer.inlineCallbacks | |
237 def addBookmark(self, type_, location, data, storage_type="auto", profile_key="@NONE@"): | |
238 """ Store a new bookmark | |
239 @param type_: bookmark type, one of: | |
240 - XEP_0048.MUC_TYPE: Multi-User chat room | |
241 - XEP_0048.URL_TYPE: web page URL | |
242 @param location: dependeding on type_, can be a MUC room jid or an url | |
243 @param data (dict): depending on type_, can contains the following keys: | |
244 - name: human readable name of the bookmark | |
245 - nick: user preferred room nick (default to user part of profile's jid) | |
246 - autojoin: "true" if room must be automatically joined on connection | |
247 - password: unused yet TODO | |
248 @param storage_type: where the bookmark will be stored, can be: | |
249 - "auto": find best available option: pubsub, private, local in that order | |
250 - "pubsub": PubSub private storage (XEP-0223) | |
251 - "private": Private XML storage (XEP-0049) | |
252 - "local": Store in SàT database | |
253 @param profile_key: %(doc_profile_key)s | |
254 | |
255 """ | |
256 assert storage_type in ('auto', 'pubsub', 'private', 'local') | |
257 client = self.host.getClient(profile_key) | |
258 if storage_type == 'auto': | |
259 if client.bookmarks_pubsub is not None: | |
260 storage_type = 'pubsub' | |
261 elif client.bookmarks_private is not None: | |
262 storage_type = 'private' | |
263 else: | |
264 storage_type = 'local' | |
265 warning(_("Bookmarks will be local only")) | |
266 info(_('Type selected for "auto" storage: %s') % storage_type) | |
267 | |
268 if storage_type == 'local': | |
269 client.bookmarks_local[type_][location] = data | |
270 yield client.bookmarks_local.force(type_) | |
271 else: | |
272 bookmarks = yield self._getServerBookmarks(storage_type, client.profile) | |
273 bookmarks[type_][location] = data | |
274 bookmark_elt = self._dict2BookmarkElt(type_, bookmarks) | |
275 yield self._setServerBookmarks(storage_type, bookmark_elt, client.profile) | |
276 | |
277 | |
278 | |
279 | |
280 |