Mercurial > libervia-backend
view 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 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # SAT plugin for Bookmarks (xep-0048) # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from sat.core.i18n import _, D_ from sat.core import exceptions from sat.memory.persistent import PersistentBinaryDict from sat.tools import xml_tools from logging import debug, info, warning, error from twisted.words.xish import domish from twisted.words.protocols.jabber import jid from twisted.internet import defer NS_BOOKMARKS = 'storage:bookmarks' PLUGIN_INFO = { "name": "Bookmarks", "import_name": "XEP-0048", "type": "XEP", "protocols": ["XEP-0048"], "dependencies": ["XEP-0045"], "recommendations": ["XEP-0049"], "main": "XEP_0048", "handler": "no", "description": _("""Implementation of bookmarks""") } class XEP_0048(object): MUC_TYPE = 'muc' URL_TYPE = 'url' MUC_KEY = 'jid' URL_KEY = 'url' MUC_ATTRS = ('autojoin', 'name') URL_ATTRS = ('name',) def __init__(self, host): info(_("Bookmarks plugin initialization")) self.host = host # self.__menu_id = host.registerCallback(self._bookmarksMenu, with_data=True) self.__bm_save_id = host.registerCallback(self._bookmarksSaveCb, with_data=True) host.importMenu((D_("Communication"), D_("bookmarks")), self._bookmarksMenu, security_limit=0, help_string=D_("Use and manage bookmarks")) try: self.private_plg = self.host.plugins["XEP-0049"] except KeyError: self.private_plg = None @defer.inlineCallbacks def profileConnected(self, profile): client = self.host.getClient(profile) local = client.bookmarks_local = PersistentBinaryDict(NS_BOOKMARKS, profile) yield local.load() if not local: local[XEP_0048.MUC_TYPE] = dict() local[XEP_0048.URL_TYPE] = dict() private = yield self._getServerBookmarks('private', profile) pubsub = client.bookmarks_pubsub = None for bookmarks in (local, private, pubsub): if bookmarks is not None: for (room_jid, data) in bookmarks[XEP_0048.MUC_TYPE].items(): if data.get('autojoin', 'false') == 'true': nick = data.get('nick', client.jid.user) self.host.plugins['XEP-0045'].join(room_jid, nick, {}, profile_key=client.profile) @defer.inlineCallbacks def _getServerBookmarks(self, storage_type, profile): """Get distants bookmarks update also the client.bookmarks_[type] key, with None if service is not available @param storage_type: storage type, can be: - 'private': XEP-0049 storage - 'pubsub': XEP-0223 storage @param profile: %(doc_profile)s @return: data dictionary, or None if feature is not available @raise: exception.FeatureNotFound """ client = self.host.getClient(profile) if storage_type == 'private': try: bookmarks_private_xml = yield self.private_plg.privateXMLGet('storage', NS_BOOKMARKS, profile) data = client.bookmarks_private = self._bookmarkElt2Dict(bookmarks_private_xml) except (exceptions.FeatureNotFound, AttributeError): info(_("Private XML storage not available")) data = client.bookmarks_private = None elif storage_type == 'pubsub': raise NotImplementedError else: raise ValueError("storage_type must be 'private' or 'pubsub'") defer.returnValue(data) @defer.inlineCallbacks def _setServerBookmarks(self, storage_type, bookmarks_elt, profile): """Save bookmarks on server @param storage_type: storage type, can be: - 'private': XEP-0049 storage - 'pubsub': XEP-0223 storage @param bookmarks_elt (domish.Element): bookmarks XML @param profile: %(doc_profile)s """ if storage_type == 'private': yield self.private_plg.privateXMLStore(bookmarks_elt, profile) elif storage_type == 'pubsub': raise NotImplementedError else: raise ValueError("storage_type must be 'private' or 'pubsub'") def _bookmarkElt2Dict(self, storage_elt): """Parse bookmarks to get dictionary @param storage_elt (domish.Element): bookmarks storage @return (dict): bookmark data (key: bookmark type, value: list) where key can be: - XEP_0048.MUC_TYPE - XEP_0048.URL_TYPE - value (dict): data as for addBookmark """ conf_data = {} url_data = {} conference_elts = storage_elt.elements(NS_BOOKMARKS, 'conference') for conference_elt in conference_elts: try: room_jid = jid.JID(conference_elt[XEP_0048.MUC_KEY]) except KeyError: warning ("invalid bookmark found, igoring it:\n%s" % conference_elt.toXml()) continue data = conf_data[room_jid] = {} for attr in XEP_0048.MUC_ATTRS: if conference_elt.hasAttribute(attr): data[attr] = conference_elt[attr] try: data['nick'] = unicode(conference_elt.elements(NS_BOOKMARKS, 'nick').next()) except StopIteration: pass # TODO: manage password (need to be secured, see XEP-0049 §4) url_elts = storage_elt.elements(NS_BOOKMARKS, 'url') for url_elt in url_elts: try: url = url_elt[XEP_0048.URL_KEY] except KeyError: warning ("invalid bookmark found, igoring it:\n%s" % url_elt.toXml()) continue data = url_data[url] = {} for attr in XEP_0048.URL_ATTRS: if url_elt.hasAttribute(attr): data[attr] = url_elt[attr] return {XEP_0048.MUC_TYPE: conf_data, XEP_0048.URL_TYPE: url_data} def _dict2BookmarkElt(self, type_, data): """Construct a bookmark element from a data dict @param data (dict): bookmark data (key: bookmark type, value: list) where key can be: - XEP_0048.MUC_TYPE - XEP_0048.URL_TYPE - value (dict): data as for addBookmark @return (domish.Element): bookmark element """ rooms_data = data.get(XEP_0048.MUC_TYPE, {}) urls_data = data.get(XEP_0048.URL_TYPE, {}) storage_elt = domish.Element((NS_BOOKMARKS, 'storage')) for room_jid in rooms_data: conference_elt = storage_elt.addElement('conference') conference_elt[XEP_0048.MUC_KEY] = room_jid.full() for attr in XEP_0048.MUC_ATTRS: try: conference_elt[attr] = rooms_data[room_jid][attr] except KeyError: pass try: conference_elt.addElement('nick', content=rooms_data[room_jid]['nick']) except KeyError: pass for url in urls_data: url_elt = storage_elt.addElement('url') url_elt[XEP_0048.URL_KEY] = url for attr in XEP_0048.URL_ATTRS: try: url_elt[attr] = url[attr] except KeyError: pass return storage_elt def _bookmarksMenu(self, data, profile): """ XMLUI activated by menu: return Gateways UI @param profile: %(doc_profile)s """ client = self.host.getClient(profile) xmlui = xml_tools.XMLUI(title=_('Bookmarks manager')) xmlui.addText(_("add a bookmark")) xmlui.changeContainer("pairs") xmlui.addLabel(_('Name')) xmlui.addString('name') xmlui.addLabel(_('jid')) xmlui.addString('jid') xmlui.addLabel(_('Nickname')) xmlui.addString('nick', client.jid.user) xmlui.addLabel(_('Autojoin')) xmlui.addBool('autojoin') xmlui.changeContainer("vertical") xmlui.addButton(self.__bm_save_id, _("Save"), ('name', 'jid', 'nick', 'autojoin')) return {'xmlui': xmlui.toXml()} def _bookmarksSaveCb(self, data, profile): bm_data = xml_tools.XMLUIResult2DataFormResult(data) try: location = jid.JID(bm_data.pop('jid')) except KeyError: raise exceptions.InternalError("Can't find mandatory key") d = self.addBookmark(XEP_0048.MUC_TYPE, location, bm_data, profile_key=profile) d.addCallback(lambda dummy: {}) return d @defer.inlineCallbacks def addBookmark(self, type_, location, data, storage_type="auto", profile_key="@NONE@"): """ Store a new bookmark @param type_: bookmark type, one of: - XEP_0048.MUC_TYPE: Multi-User chat room - XEP_0048.URL_TYPE: web page URL @param location: dependeding on type_, can be a MUC room jid or an url @param data (dict): depending on type_, can contains the following keys: - name: human readable name of the bookmark - nick: user preferred room nick (default to user part of profile's jid) - autojoin: "true" if room must be automatically joined on connection - password: unused yet TODO @param storage_type: where the bookmark will be stored, can be: - "auto": find best available option: pubsub, private, local in that order - "pubsub": PubSub private storage (XEP-0223) - "private": Private XML storage (XEP-0049) - "local": Store in SàT database @param profile_key: %(doc_profile_key)s """ assert storage_type in ('auto', 'pubsub', 'private', 'local') client = self.host.getClient(profile_key) if storage_type == 'auto': if client.bookmarks_pubsub is not None: storage_type = 'pubsub' elif client.bookmarks_private is not None: storage_type = 'private' else: storage_type = 'local' warning(_("Bookmarks will be local only")) info(_('Type selected for "auto" storage: %s') % storage_type) if storage_type == 'local': client.bookmarks_local[type_][location] = data yield client.bookmarks_local.force(type_) else: bookmarks = yield self._getServerBookmarks(storage_type, client.profile) bookmarks[type_][location] = data bookmark_elt = self._dict2BookmarkElt(type_, bookmarks) yield self._setServerBookmarks(storage_type, bookmark_elt, client.profile)