core, frontends: refactoring to base Libervia on QuickFrontend (big mixed commit):
/!\ not finished, everything is still instable !
- bridge: DBus bridge has been modified to allow blocking call to be called in the same way as asynchronous calls
- bridge: calls with a callback and no errback are now possible, default errback log the error
- constants: removed hack to manage presence without OrderedDict, as an OrderedDict like class has been implemented in Libervia
- core: getLastResource has been removed and replaced by getMainResource (there is a global better management of resources)
- various style improvments: use of constants when possible, fixed variable overlaps, import of module instead of direct class import
- frontends: printInfo and printMessage methods in (Quick)Chat are more generic (use of extra instead of timestamp)
- frontends: bridge creation and option parsing (command line arguments) are now specified by the frontend in QuickApp __init__
- frontends: ProfileManager manage a more complete plug sequence (some stuff formerly manage in contact_list have moved to ProfileManager)
- quick_frontend (quick_widgets): QuickWidgetsManager is now iterable (all widgets are then returned), or can return an iterator on a specific class (return all widgets of this class) with getWidgets
- frontends: tools.jid can now be used in Pyjamas, with some care
- frontends (XMLUI): profile is now managed
- core (memory): big improvment on entities cache management (and specially resource management)
- core (params/exceptions): added PermissionError
- various fixes and improvments, check diff for more details
This commit is contained in:
parent
f45e55388f
commit
8ce5c1862b
5 changed files with 1125 additions and 452 deletions
|
|
@ -209,7 +209,7 @@ class TextCommands(object):
|
|||
return False
|
||||
|
||||
if not target_jid.resource:
|
||||
target_jid.resource = self.host.memory.getLastResource(target_jid, profile)
|
||||
target_jid.resource = self.host.memory.getMainResource(target_jid, profile)
|
||||
|
||||
whois_msg = [_(u"whois for %(jid)s") % {'jid': target_jid}]
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,9 @@ from sat.tools import xml_tools
|
|||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.python import failure
|
||||
from twisted.internet import defer
|
||||
import potr
|
||||
from sat.memory import persistent
|
||||
import potr
|
||||
import copy
|
||||
|
||||
NS_OTR = "otr_plugin"
|
||||
PRIVATE_KEY = "PRIVATE KEY"
|
||||
|
|
@ -269,7 +270,7 @@ class OTR(object):
|
|||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
|
|
@ -287,7 +288,7 @@ class OTR(object):
|
|||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
|
|
@ -304,7 +305,7 @@ class OTR(object):
|
|||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
|
|
@ -374,7 +375,7 @@ class OTR(object):
|
|||
try:
|
||||
to_jid = jid.JID(menu_data['jid'])
|
||||
if not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
except KeyError:
|
||||
log.error(_("jid key is not present !"))
|
||||
return defer.fail(exceptions.DataError)
|
||||
|
|
@ -447,9 +448,9 @@ class OTR(object):
|
|||
def sendMessageTrigger(self, mess_data, pre_xml_treatments, post_xml_treatments, profile):
|
||||
if profile in self.skipped_profiles:
|
||||
return True
|
||||
to_jid = mess_data['to']
|
||||
to_jid = copy.copy(mess_data['to'])
|
||||
if mess_data['type'] != 'groupchat' and not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile) # FIXME: it's dirty, but frontends don't manage resources correctly now, refactoring is planed
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile) # FIXME: it's dirty, but frontends don't manage resources correctly now, refactoring is planed
|
||||
otrctx = self.context_managers[profile].getContextForUser(to_jid)
|
||||
if mess_data['type'] != 'groupchat' and otrctx.state != potr.context.STATE_PLAINTEXT:
|
||||
if otrctx.state == potr.context.STATE_ENCRYPTED:
|
||||
|
|
@ -475,7 +476,7 @@ class OTR(object):
|
|||
if show != "unavailable":
|
||||
return True
|
||||
if not entity.resource:
|
||||
entity.resource = self.host.memory.getLastResource(entity, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
entity.resource = self.host.memory.getMainResource(entity, profile) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
||||
otrctx = self.context_managers[profile].getContextForUser(entity)
|
||||
otrctx.disconnect()
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ class XEP_0054(object):
|
|||
host.bridge.addMethod("getAvatarFile", ".plugin", in_sign='s', out_sign='s', method=self.getAvatarFile)
|
||||
host.bridge.addMethod("setAvatar", ".plugin", in_sign='ss', out_sign='', method=self.setAvatar, async=True)
|
||||
host.trigger.add("presence_available", self.presenceTrigger)
|
||||
host.memory.setSignalOnUpdate("avatar")
|
||||
|
||||
def getHandler(self, profile):
|
||||
return XEP_0054_handler(self)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class XEP_0060(object):
|
|||
OPT_PUBLISH_MODEL = 'pubsub#publish_model'
|
||||
|
||||
def __init__(self, host):
|
||||
log.info(_("PubSub plugin initialization"))
|
||||
log.info(_(u"PubSub plugin initialization"))
|
||||
self.host = host
|
||||
self.managedNodes = []
|
||||
self.clients = {}
|
||||
|
|
@ -176,7 +176,7 @@ class XEP_0060(object):
|
|||
d_dict = {}
|
||||
for publisher, node in data.items():
|
||||
if node not in found_nodes:
|
||||
log.debug("Skip the items retrieval for [{node}]: node doesn't exist".format(node=node))
|
||||
log.debug(u"Skip the items retrieval for [{node}]: node doesn't exist".format(node=node))
|
||||
continue # avoid pubsub "item-not-found" error
|
||||
d_dict[publisher] = client.items(service, node, max_items, item_ids, sub_id, client.parent.jid)
|
||||
defer.returnValue(d_dict)
|
||||
|
|
@ -222,7 +222,7 @@ class XEP_0060(object):
|
|||
d_list = []
|
||||
for nodeIdentifier in (set(nodeIdentifiers) - set(subscribed_nodes)):
|
||||
if nodeIdentifier not in found_nodes:
|
||||
log.debug("Skip the subscription to [{node}]: node doesn't exist".format(node=nodeIdentifier))
|
||||
log.debug(u"Skip the subscription to [{node}]: node doesn't exist".format(node=nodeIdentifier))
|
||||
continue # avoid sat-pubsub "SubscriptionExists" error
|
||||
d_list.append(client.subscribe(service, nodeIdentifier, sub_jid or client.parent.jid.userhostJID(), options=options))
|
||||
defer.returnValue(d_list)
|
||||
|
|
@ -318,7 +318,7 @@ class SatPubSubClient(pubsub.PubSubClient):
|
|||
|
||||
def deleteReceived(self, event):
|
||||
#TODO: manage delete event
|
||||
log.debug(_("Publish node deleted"))
|
||||
log.debug(_(u"Publish node deleted"))
|
||||
|
||||
# def purgeReceived(self, event):
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ MESSAGE_TYPES = ["chat", "groupchat"]
|
|||
PARAM_KEY = "Notifications"
|
||||
PARAM_NAME = "Enable chat state notifications"
|
||||
ENTITY_KEY = PARAM_KEY + "_" + PARAM_NAME
|
||||
DELETE_VALUE = "DELETE"
|
||||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Chat State Notifications Protocol Plugin",
|
||||
|
|
@ -126,28 +127,31 @@ class XEP_0085(object):
|
|||
del self.map[profile]
|
||||
|
||||
def updateEntityData(self, entity_jid, value, profile):
|
||||
"""
|
||||
Update the entity data of the given profile for one or all contacts.
|
||||
"""Update the entity data of the given profile for one or all contacts.
|
||||
|
||||
Reset the chat state(s) display if the notification has been disabled.
|
||||
@param entity_jid: contact's JID, or '@ALL@' to update all contacts.
|
||||
@param value: True, False or C.PROF_KEY_NONE to delete the entity data
|
||||
@param entity_jid: contact's JID, or C.ENTITY_ALL to update all contacts.
|
||||
@param value: True, False or DELETE_VALUE to delete the entity data
|
||||
@param profile: current profile
|
||||
"""
|
||||
self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile)
|
||||
if not value or value == C.PROF_KEY_NONE:
|
||||
# disable chat state for this or these contact(s)
|
||||
self.host.bridge.chatStateReceived(unicode(entity_jid), "", profile)
|
||||
if value == DELETE_VALUE:
|
||||
self.host.memory.delEntityData(entity_jid, ENTITY_KEY, profile)
|
||||
else:
|
||||
self.host.memory.updateEntityData(entity_jid, ENTITY_KEY, value, profile)
|
||||
if not value or value == DELETE_VALUE:
|
||||
# reinit chat state UI for this or these contact(s)
|
||||
self.host.bridge.chatStateReceived(entity_jid.full(), "", profile)
|
||||
|
||||
def paramUpdateTrigger(self, name, value, category, type_, profile):
|
||||
"""
|
||||
Reset all the existing chat state entity data associated with this profile after a parameter modification.
|
||||
"""Reset all the existing chat state entity data associated with this profile after a parameter modification.
|
||||
|
||||
@param name: parameter name
|
||||
@param value: "true" to activate the notifications, or any other value to delete it
|
||||
@param category: parameter category
|
||||
@param type_: parameter type
|
||||
"""
|
||||
if (category, name) == (PARAM_KEY, PARAM_NAME):
|
||||
self.updateEntityData("@ALL@", True if value == "true" else C.PROF_KEY_NONE, profile)
|
||||
self.updateEntityData(C.ENTITY_ALL, True if bool("true") else DELETE_VALUE, profile)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
@ -160,7 +164,7 @@ class XEP_0085(object):
|
|||
return True
|
||||
|
||||
from_jid = JID(message.getAttribute("from"))
|
||||
if self.__isMUC(from_jid, profile):
|
||||
if self._isMUC(from_jid, profile):
|
||||
from_jid = from_jid.userhostJID()
|
||||
else: # update entity data for one2one chat
|
||||
# assert from_jid.resource # FIXME: assert doesn't work on normal message from server (e.g. server announce), because there is no resource
|
||||
|
|
@ -200,27 +204,27 @@ class XEP_0085(object):
|
|||
def treatment(mess_data):
|
||||
message = mess_data['xml']
|
||||
to_jid = JID(message.getAttribute("to"))
|
||||
if not self.__checkActivation(to_jid, forceEntityData=True, profile=profile):
|
||||
if not self._checkActivation(to_jid, forceEntityData=True, profile=profile):
|
||||
return mess_data
|
||||
try:
|
||||
# message with a body always mean active state
|
||||
domish.generateElementsNamed(message.elements(), name="body").next()
|
||||
message.addElement('active', NS_CHAT_STATES)
|
||||
# launch the chat state machine (init the timer)
|
||||
if self.__isMUC(to_jid, profile):
|
||||
if self._isMUC(to_jid, profile):
|
||||
to_jid = to_jid.userhostJID()
|
||||
self.__chatStateActive(to_jid, mess_data["type"], profile)
|
||||
except StopIteration:
|
||||
if "chat_state" in mess_data["extra"]:
|
||||
state = mess_data["extra"].pop("chat_state")
|
||||
assert(state in CHAT_STATES)
|
||||
assert state in CHAT_STATES
|
||||
message.addElement(state, NS_CHAT_STATES)
|
||||
return mess_data
|
||||
|
||||
post_xml_treatments.addCallback(treatment)
|
||||
return True
|
||||
|
||||
def __isMUC(self, to_jid, profile):
|
||||
def _isMUC(self, to_jid, profile):
|
||||
"""Tell if that JID is a MUC or not
|
||||
|
||||
@param to_jid (JID): full or bare JID to check
|
||||
|
|
@ -229,13 +233,13 @@ class XEP_0085(object):
|
|||
"""
|
||||
try:
|
||||
type_ = self.host.memory.getEntityDatum(to_jid.userhostJID(), 'type', profile)
|
||||
if type_ == 'chatroom':
|
||||
if type_ == 'chatroom': # FIXME: should not use disco instead ?
|
||||
return True
|
||||
except (exceptions.UnknownEntityError, KeyError):
|
||||
pass
|
||||
return False
|
||||
|
||||
def __checkActivation(self, to_jid, forceEntityData, profile):
|
||||
def _checkActivation(self, to_jid, forceEntityData, profile):
|
||||
"""
|
||||
@param to_jid: the contact's full JID (or bare if you know it's a MUC)
|
||||
@param forceEntityData: if set to True, a non-existing
|
||||
|
|
@ -247,9 +251,10 @@ class XEP_0085(object):
|
|||
if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile):
|
||||
return False
|
||||
# check if notifications should be sent to this contact
|
||||
if self.__isMUC(to_jid, profile):
|
||||
if self._isMUC(to_jid, profile):
|
||||
return True
|
||||
assert to_jid.resource or not self.host.memory.isContactConnected(to_jid, profile)
|
||||
# FIXME: this assertion crash when we want to send a message to an online bare jid
|
||||
# assert to_jid.resource or not self.host.memory.isEntityAvailable(to_jid, profile) # must either have a resource, or talk to an offline contact
|
||||
try:
|
||||
return self.host.memory.getEntityDatum(to_jid, ENTITY_KEY, profile)
|
||||
except (exceptions.UnknownEntityError, KeyError):
|
||||
|
|
@ -305,11 +310,11 @@ class XEP_0085(object):
|
|||
if profile is None:
|
||||
raise exceptions.ProfileUnknownError
|
||||
to_jid = JID(to_jid_s)
|
||||
if self.__isMUC(to_jid, profile):
|
||||
if self._isMUC(to_jid, profile):
|
||||
to_jid = to_jid.userhostJID()
|
||||
elif not to_jid.resource:
|
||||
to_jid.resource = self.host.memory.getLastResource(to_jid, profile)
|
||||
if not self.__checkActivation(to_jid, forceEntityData=False, profile=profile):
|
||||
to_jid.resource = self.host.memory.getMainResource(to_jid, profile)
|
||||
if not self._checkActivation(to_jid, forceEntityData=False, profile=profile):
|
||||
return
|
||||
try:
|
||||
self.map[profile][to_jid]._onEvent("composing")
|
||||
|
|
@ -347,9 +352,9 @@ class ChatStateMachine:
|
|||
notification to the contact (the "active" state is
|
||||
automatically sent with each message) and set the timer.
|
||||
"""
|
||||
assert(state in TRANSITIONS)
|
||||
assert state in TRANSITIONS
|
||||
transition = TRANSITIONS[state]
|
||||
assert("next_state" in transition and "delay" in transition)
|
||||
assert "next_state" in transition and "delay" in transition
|
||||
|
||||
if state != self.state and state != "active":
|
||||
if state != 'gone' or self.mess_type != 'groupchat':
|
||||
|
|
|
|||
Loading…
Reference in a new issue