core, quick frontend, plugin xep-0054, bridge: use of memory's entities data for vcard:
- bridge: new bridge method getEntityData and signal entityDataUpdated - core: entityDataUpdated signal sent on new string data - quick frontend: fixed avatars/vcard infos, fixed _replace in quick_contact_list - plugin xep-0054: dropped updatedValue signal, use entities data instead
This commit is contained in:
parent
27b2eed139
commit
0f6de78a3b
8 changed files with 76 additions and 80 deletions
|
|
@ -99,6 +99,9 @@ class DBusBridgeFrontend(BridgeFrontend):
|
|||
def getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
|
||||
return self.db_core_iface.getContactsFromGroup(group, profile_key)
|
||||
|
||||
def getEntityData(self, jid, keys, profile):
|
||||
return self.db_core_iface.getEntityData(jid, keys, profile)
|
||||
|
||||
def getHistory(self, from_jid, to_jid, limit, between=True, callback=None, errback=None):
|
||||
return self.db_core_iface.getHistory(from_jid, to_jid, limit, between, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:]))
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class QuickApp():
|
|||
self.bridge.register("subscribe", self.subscribe)
|
||||
self.bridge.register("paramUpdate", self.paramUpdate)
|
||||
self.bridge.register("contactDeleted", self.contactDeleted)
|
||||
self.bridge.register("updatedValue", self.updatedValue)
|
||||
self.bridge.register("entityDataUpdated", self.entityDataUpdated)
|
||||
self.bridge.register("askConfirmation", self.askConfirmation)
|
||||
self.bridge.register("actionResult", self.actionResult)
|
||||
self.bridge.register("actionResultExt", self.actionResult)
|
||||
|
|
@ -165,6 +165,10 @@ class QuickApp():
|
|||
priority = presences[contact][res][1]
|
||||
statuses = presences[contact][res][2]
|
||||
self.presenceUpdate(jabber_id, show, priority, statuses, profile)
|
||||
data = self.bridge.getEntityData(contact, ['avatar','nick'], profile)
|
||||
for key in ('avatar', 'nick'):
|
||||
if key in data:
|
||||
self.entityDataUpdated(contact, key, data[key], profile)
|
||||
|
||||
#The waiting subscription requests
|
||||
waitingSub = self.bridge.getWaitingSub(profile)
|
||||
|
|
@ -476,21 +480,19 @@ class QuickApp():
|
|||
except KeyError:
|
||||
pass
|
||||
|
||||
def updatedValue(self, name, data, profile):
|
||||
#FIXME: to be removed
|
||||
def entityDataUpdated(self, jid_str, key, value, profile):
|
||||
if not self.check_profile(profile):
|
||||
return
|
||||
if name == "card_nick":
|
||||
target = JID(data['jid'])
|
||||
if target in self.contact_list:
|
||||
#self.CM.update(target, 'nick', unicode(data['nick']))
|
||||
self.contact_list._replace(target)
|
||||
elif name == "card_avatar":
|
||||
target = JID(data['jid'])
|
||||
if target in self.contact_list:
|
||||
filename = self.bridge.getAvatarFile(data['avatar'])
|
||||
#self.CM.update(target, 'avatar', filename)
|
||||
self.contact_list._replace(target)
|
||||
jid = JID(jid_str)
|
||||
if key == "nick":
|
||||
if jid in self.contact_list:
|
||||
self.contact_list.setCache(jid, 'nick', value)
|
||||
self.contact_list._replace(jid)
|
||||
elif key == "avatar":
|
||||
if jid in self.contact_list:
|
||||
filename = self.bridge.getAvatarFile(value)
|
||||
self.contact_list.setCache(jid, 'avatar', filename)
|
||||
self.contact_list._replace(jid)
|
||||
|
||||
def askConfirmation(self, type, id, data):
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class QuickContactList():
|
|||
raise NotImplementedError
|
||||
|
||||
def _replace(self, jid, groups=None, attributes=None):
|
||||
if 'name' in attributes:
|
||||
if attributes and 'name' in attributes:
|
||||
self.setCache(jid, 'name', attributes['name'])
|
||||
self.replace(jid, groups, attributes)
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class QuickContactList():
|
|||
def remove(self, jid):
|
||||
"""remove a contact from the list"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def add(self, jid, param_groups=None):
|
||||
"""add a contact to the list"""
|
||||
raise NotImplementedError
|
||||
|
|
|
|||
|
|
@ -139,6 +139,11 @@ class DbusObject(dbus.service.Object):
|
|||
def disconnected(self, profile):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
signature='ssss')
|
||||
def entityDataUpdated(self, jid, name, value, profile):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
signature='ssss')
|
||||
def newAlert(self, message, title, alert_type, profile):
|
||||
|
|
@ -169,11 +174,6 @@ class DbusObject(dbus.service.Object):
|
|||
def subscribe(self, sub_type, entity_jid, profile):
|
||||
pass
|
||||
|
||||
@dbus.service.signal(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
signature='sa{ss}s')
|
||||
def updatedValue(self, name, value, profile):
|
||||
pass
|
||||
|
||||
|
||||
### methods ###
|
||||
|
||||
|
|
@ -261,6 +261,12 @@ class DbusObject(dbus.service.Object):
|
|||
def getContactsFromGroup(self, group, profile_key="@DEFAULT@"):
|
||||
return self._callback("getContactsFromGroup", unicode(group), unicode(profile_key))
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='sass', out_signature='a{ss}',
|
||||
async_callbacks=None)
|
||||
def getEntityData(self, jid, keys, profile):
|
||||
return self._callback("getEntityData", unicode(jid), keys, unicode(profile))
|
||||
|
||||
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
|
||||
in_signature='ssib', out_signature='a(dsss)',
|
||||
async_callbacks=('callback', 'errback'))
|
||||
|
|
@ -518,6 +524,9 @@ class DBusBridge(Bridge):
|
|||
def disconnected(self, profile):
|
||||
self.dbus_bridge.disconnected(profile)
|
||||
|
||||
def entityDataUpdated(self, jid, name, value, profile):
|
||||
self.dbus_bridge.entityDataUpdated(jid, name, value, profile)
|
||||
|
||||
def newAlert(self, message, title, alert_type, profile):
|
||||
self.dbus_bridge.newAlert(message, title, alert_type, profile)
|
||||
|
||||
|
|
@ -536,9 +545,6 @@ class DBusBridge(Bridge):
|
|||
def subscribe(self, sub_type, entity_jid, profile):
|
||||
self.dbus_bridge.subscribe(sub_type, entity_jid, profile)
|
||||
|
||||
def updatedValue(self, name, value, profile):
|
||||
self.dbus_bridge.updatedValue(name, value, profile)
|
||||
|
||||
|
||||
def register(self, name, callback):
|
||||
debug("registering DBus bridge method [%s]", name)
|
||||
|
|
|
|||
|
|
@ -136,14 +136,15 @@ doc_param_0=answer_type: Same as for [actionResult] but with the following addit
|
|||
doc_param_1=id: Id of the action
|
||||
doc_param_2=data: answer_type specific data
|
||||
|
||||
[updatedValue]
|
||||
[entityDataUpdated]
|
||||
type=signal
|
||||
category=core
|
||||
sig_in=sa{ss}s
|
||||
doc=A value has been updated
|
||||
doc_param_0=name: Name of the updated value
|
||||
doc_param_1=value: New value
|
||||
doc_param_2=%(doc_profile)s
|
||||
sig_in=ssss
|
||||
doc=An entity's data has been updated
|
||||
doc_param_0=jid: entity's bare jid
|
||||
doc_param_1=name: Name of the updated value
|
||||
doc_param_2=value: New value
|
||||
doc_param_3=%(doc_profile)s
|
||||
|
||||
;methods
|
||||
|
||||
|
|
@ -171,6 +172,18 @@ sig_in=
|
|||
sig_out=as
|
||||
doc=Get all profiles
|
||||
|
||||
[getEntityData]
|
||||
type=method
|
||||
category=core
|
||||
sig_in=sass
|
||||
sig_out=a{ss}
|
||||
doc=Get data for an entity
|
||||
doc_param_0=jid: entity's bare jid
|
||||
doc_param_1=keys: list of keys to get
|
||||
doc_param_2=%(doc_profile)s
|
||||
doc_return=dictionary of asked key,
|
||||
if key doesn't exist, the resulting dictionary will neither have the key
|
||||
|
||||
[createProfile]
|
||||
deprecated=
|
||||
type=method
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ class SAT(service.Service):
|
|||
self.bridge.register("getVersion", lambda: self.get_const('client_version'))
|
||||
self.bridge.register("getProfileName", self.memory.getProfileName)
|
||||
self.bridge.register("getProfilesList", self.memory.getProfilesList)
|
||||
self.bridge.register("getEntityData", lambda _jid, keys, profile: self.memory.getEntityData(jid.JID(_jid),keys, profile))
|
||||
self.bridge.register("createProfile", self.memory.createProfile)
|
||||
self.bridge.register("asyncCreateProfile", self.memory.asyncCreateProfile)
|
||||
self.bridge.register("deleteProfile", self.memory.deleteProfile)
|
||||
|
|
@ -279,7 +280,7 @@ class SAT(service.Service):
|
|||
if not client:
|
||||
raise ProfileUnknownError(_('Asking contacts for a non-existant profile'))
|
||||
ret = []
|
||||
for item in client.roster.getItems(): #we get all item for client's roster
|
||||
for item in client.roster.getItems(): #we get all items for client's roster
|
||||
#and convert them to expected format
|
||||
attr = client.roster.getAttributes(item)
|
||||
ret.append([item.jid.userhost(), attr, item.groups])
|
||||
|
|
|
|||
|
|
@ -720,6 +720,8 @@ class Memory:
|
|||
raise exceptions.ProfileNotInCacheError
|
||||
entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(),{})
|
||||
entity_data[key] = value
|
||||
if isinstance(value,basestring):
|
||||
self.host.bridge.entityDataUpdated(entity_jid.userhost(), key, value, profile)
|
||||
|
||||
def getEntityData(self, entity_jid, keys_list, profile_key):
|
||||
"""Get a list of cached values for entity
|
||||
|
|
@ -727,7 +729,7 @@ class Memory:
|
|||
@param keys_list: list of keys to get, empty list for everything
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
@return: dict withs values for each key in keys_list.
|
||||
if there is no value of a given key, resultint dict will
|
||||
if there is no value of a given key, resulting dict will
|
||||
have nothing with that key nether
|
||||
@raise: exceptions.UnknownEntityError if entity is not in cache
|
||||
exceptions.ProfileNotInCacheError if profile is not in cache
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
"""
|
||||
|
||||
from logging import debug, info, error
|
||||
from twisted.words.xish import domish
|
||||
from twisted.internet import protocol, defer, threads, reactor
|
||||
from twisted.internet import threads
|
||||
from twisted.internet.defer import inlineCallbacks, returnValue
|
||||
from twisted.words.protocols.jabber import client, jid, xmlstream
|
||||
from twisted.words.protocols.jabber import error as jab_error
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.protocols.jabber.xmlstream import IQ
|
||||
import os.path
|
||||
|
||||
|
|
@ -34,8 +32,7 @@ from wokkel import disco, iwokkel
|
|||
|
||||
from base64 import b64decode
|
||||
from hashlib import sha1
|
||||
from time import sleep
|
||||
from sat.memory.persistent import PersistentBinaryDict
|
||||
from sat.core import exceptions
|
||||
|
||||
try:
|
||||
from twisted.words.protocols.xmlstream import XMPPHandler
|
||||
|
|
@ -64,6 +61,9 @@ PLUGIN_INFO = {
|
|||
}
|
||||
|
||||
class XEP_0054():
|
||||
#TODO: - check that nickname is ok
|
||||
# - refactor the code/better use of Wokkel
|
||||
# - get missing values
|
||||
|
||||
def __init__(self, host):
|
||||
info(_("Plugin XEP_0054 initialization"))
|
||||
|
|
@ -73,7 +73,6 @@ class XEP_0054():
|
|||
os.makedirs(self.avatar_path)
|
||||
host.bridge.addMethod("getCard", ".plugin", in_sign='ss', out_sign='s', method=self.getCard)
|
||||
host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile)
|
||||
host.bridge.addMethod("getCardCache", ".plugin", in_sign='ss', out_sign='a{ss}', method=self.getCardCache)
|
||||
|
||||
def getHandler(self, profile):
|
||||
return XEP_0054_handler(self)
|
||||
|
|
@ -81,35 +80,29 @@ class XEP_0054():
|
|||
def update_cache(self, jid, name, value, profile):
|
||||
"""update cache value
|
||||
- save value in memory in case of change
|
||||
- send updatedValue signal if the value is new or updated
|
||||
@param jid: jid of the owner of the vcard
|
||||
@param name: name of the item which changed
|
||||
@param value: new value of the item
|
||||
@param profile: profile which received the update
|
||||
"""
|
||||
client = self.host.getClient(profile)
|
||||
if not jid.userhost() in client._vcard_cache:
|
||||
client._vcard_cache[jid.userhost()] = {}
|
||||
|
||||
cache = client._vcard_cache[jid.userhost()]
|
||||
old_value = cache[name] if name in cache else None
|
||||
if not old_value or value != old_value:
|
||||
cache[name] = value
|
||||
client._vcard_cache.force(jid.userhost()) #we force saving of data to storage
|
||||
self.host.bridge.updatedValue('card_'+name, {'jid':jid.userhost(), name:value}, profile)
|
||||
|
||||
try:
|
||||
cached = self.host.memory.getEntityData(jid, [name], profile)
|
||||
except exceptions.UnknownEntityError:
|
||||
cached = {}
|
||||
if not name in cached or cached[name] != value:
|
||||
self.host.memory.updateEntityData(jid, name, value, profile)
|
||||
|
||||
def get_cache(self, jid, name, profile):
|
||||
"""return cached value for jid
|
||||
@param jid: target contact
|
||||
@param name: name of the value ('nick' or 'avatar')
|
||||
@param profile: %(doc_profile)s
|
||||
@return: wanted value or None"""
|
||||
client = self.host.getClient(profile)
|
||||
try:
|
||||
return client._vcard_cache[jid.userhost()][name]
|
||||
except KeyError:
|
||||
data = self.host.memory.getEntityData(jid, [name], profile)
|
||||
except exceptions.UnknownEntityError:
|
||||
return None
|
||||
|
||||
return data.get(name)
|
||||
|
||||
def save_photo(self, photo_xml):
|
||||
"""Parse a <PHOTO> elem and save the picture"""
|
||||
|
|
@ -134,7 +127,6 @@ class XEP_0054():
|
|||
"""Convert a VCard to a dict, and save binaries"""
|
||||
debug (_("parsing vcard"))
|
||||
dictionary = {}
|
||||
d = defer.Deferred()
|
||||
|
||||
for elem in vcard.elements():
|
||||
if elem.name == 'FN':
|
||||
|
|
@ -189,7 +181,7 @@ class XEP_0054():
|
|||
reg_request=IQ(xmlstream,'get')
|
||||
reg_request["from"]=current_jid.full()
|
||||
reg_request["to"] = to_jid.userhost()
|
||||
query=reg_request.addElement('vCard', NS_VCARD)
|
||||
reg_request.addElement('vCard', NS_VCARD)
|
||||
reg_request.send(to_jid.userhost()).addCallbacks(self.vcard_ok, self.vcard_err, [profile])
|
||||
return reg_request["id"]
|
||||
|
||||
|
|
@ -204,27 +196,6 @@ class XEP_0054():
|
|||
return ""
|
||||
return filename
|
||||
|
||||
def getCardCache(self, target, profile_key):
|
||||
"""Request for cached values of profile
|
||||
return the cached nickname and avatar if exists, else get VCard
|
||||
@param target: target's jid
|
||||
@param profile_key: %(doc_profile_key)s
|
||||
"""
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
if not profile:
|
||||
error(_("Profile not found"))
|
||||
return {}
|
||||
to_jid = jid.JID(target)
|
||||
result = {}
|
||||
nick = self.get_cache(to_jid, 'nick', profile)
|
||||
if nick:
|
||||
result['nick'] = nick
|
||||
avatar = self.get_cache(to_jid, 'avatar', profile)
|
||||
if avatar:
|
||||
result['avatar'] = avatar
|
||||
return result
|
||||
|
||||
|
||||
|
||||
class XEP_0054_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
@ -234,8 +205,6 @@ class XEP_0054_handler(XMPPHandler):
|
|||
self.host = plugin_parent.host
|
||||
|
||||
def connectionInitialized(self):
|
||||
self.parent._vcard_cache = PersistentBinaryDict(NS_VCARD, self.parent.profile)
|
||||
self.parent._vcard_cache.load()
|
||||
self.xmlstream.addObserver(VCARD_UPDATE, self.update)
|
||||
|
||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
|
||||
|
|
|
|||
Loading…
Reference in a new issue