plugin XEP-0048: bookmarks (first draft)
This commit is contained in:
parent
c497e19b25
commit
63e668ec01
1 changed files with 280 additions and 0 deletions
280
src/plugins/plugin_xep_0048.py
Normal file
280
src/plugins/plugin_xep_0048.py
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
#!/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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in a new issue