# HG changeset patch # User Goffi # Date 1348615436 -7200 # Node ID 65ecbb473cbb50892cef906bd82b0fe60f006377 # Parent 10119c2a9d33e7335d234fe37844214ac1f5cfda 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 diff -r 10119c2a9d33 -r 65ecbb473cbb frontends/src/bridge/DBus.py --- a/frontends/src/bridge/DBus.py Wed Sep 26 00:38:41 2012 +0200 +++ b/frontends/src/bridge/DBus.py Wed Sep 26 01:23:56 2012 +0200 @@ -99,6 +99,9 @@ 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:])) diff -r 10119c2a9d33 -r 65ecbb473cbb frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Wed Sep 26 00:38:41 2012 +0200 +++ b/frontends/src/quick_frontend/quick_app.py Wed Sep 26 01:23:56 2012 +0200 @@ -52,7 +52,7 @@ 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 @@ 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 @@ 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 diff -r 10119c2a9d33 -r 65ecbb473cbb frontends/src/quick_frontend/quick_contact_list.py --- a/frontends/src/quick_frontend/quick_contact_list.py Wed Sep 26 00:38:41 2012 +0200 +++ b/frontends/src/quick_frontend/quick_contact_list.py Wed Sep 26 01:23:56 2012 +0200 @@ -54,7 +54,7 @@ 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 @@ 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 diff -r 10119c2a9d33 -r 65ecbb473cbb src/bridge/DBus.py --- a/src/bridge/DBus.py Wed Sep 26 00:38:41 2012 +0200 +++ b/src/bridge/DBus.py Wed Sep 26 01:23:56 2012 +0200 @@ -141,6 +141,11 @@ @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): pass @@ -169,11 +174,6 @@ 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 ### @@ -262,6 +262,12 @@ 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')) def getHistory(self, from_jid, to_jid, limit, between=True, callback=None, errback=None): @@ -518,6 +524,9 @@ 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 @@ 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) diff -r 10119c2a9d33 -r 65ecbb473cbb src/bridge/bridge_constructor/bridge_template.ini --- a/src/bridge/bridge_constructor/bridge_template.ini Wed Sep 26 00:38:41 2012 +0200 +++ b/src/bridge/bridge_constructor/bridge_template.ini Wed Sep 26 01:23:56 2012 +0200 @@ -136,14 +136,15 @@ 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_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 diff -r 10119c2a9d33 -r 65ecbb473cbb src/core/sat_main.py --- a/src/core/sat_main.py Wed Sep 26 00:38:41 2012 +0200 +++ b/src/core/sat_main.py Wed Sep 26 01:23:56 2012 +0200 @@ -118,6 +118,7 @@ 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 @@ 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]) diff -r 10119c2a9d33 -r 65ecbb473cbb src/memory/memory.py --- a/src/memory/memory.py Wed Sep 26 00:38:41 2012 +0200 +++ b/src/memory/memory.py Wed Sep 26 01:23:56 2012 +0200 @@ -720,6 +720,8 @@ 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 @@ @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 diff -r 10119c2a9d33 -r 65ecbb473cbb src/plugins/plugin_xep_0054.py --- a/src/plugins/plugin_xep_0054.py Wed Sep 26 00:38:41 2012 +0200 +++ b/src/plugins/plugin_xep_0054.py Wed Sep 26 01:23:56 2012 +0200 @@ -20,11 +20,9 @@ """ 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 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 @@ } 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 @@ 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 @@ 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 elem and save the picture""" @@ -134,7 +127,6 @@ """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 @@ 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 @@ 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 @@ 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=''):