Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/model/invites.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/model/invites.py')
-rw-r--r--src/jarabe/model/invites.py289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/jarabe/model/invites.py b/src/jarabe/model/invites.py
new file mode 100644
index 0000000..631e49f
--- /dev/null
+++ b/src/jarabe/model/invites.py
@@ -0,0 +1,289 @@
+# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import logging
+from functools import partial
+import simplejson
+
+import gobject
+import dbus
+import gconf
+from telepathy.interfaces import CHANNEL, \
+ CHANNEL_DISPATCHER, \
+ CHANNEL_DISPATCH_OPERATION, \
+ CHANNEL_TYPE_CONTACT_LIST, \
+ CHANNEL_TYPE_TEXT, \
+ CLIENT
+from telepathy.constants import HANDLE_TYPE_ROOM
+
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.model import telepathyclient
+from jarabe.model import bundleregistry
+from jarabe.model import neighborhood
+from jarabe.journal import misc
+
+
+CONNECTION_INTERFACE_ACTIVITY_PROPERTIES = \
+ 'org.laptop.Telepathy.ActivityProperties'
+
+_instance = None
+
+
+class BaseInvite(object):
+ """Invitation to shared activity or private 1-1 Telepathy channel"""
+ def __init__(self, dispatch_operation_path, handle, handler):
+ self.dispatch_operation_path = dispatch_operation_path
+ self._handle = handle
+ self._handler = handler
+
+ def get_bundle_id(self):
+ if CLIENT in self._handler:
+ return self._handler[len(CLIENT + '.'):]
+ else:
+ return None
+
+ def _call_handle_with(self):
+ bus = dbus.Bus()
+ obj = bus.get_object(CHANNEL_DISPATCHER, self.dispatch_operation_path)
+ dispatch_operation = dbus.Interface(obj, CHANNEL_DISPATCH_OPERATION)
+ dispatch_operation.HandleWith(self._handler,
+ reply_handler=self._handle_with_reply_cb,
+ error_handler=self._handle_with_reply_cb)
+
+ def _handle_with_reply_cb(self, error=None):
+ if error is not None:
+ raise error
+ else:
+ logging.debug('_handle_with_reply_cb')
+
+ def _name_owner_changed_cb(self, name, old_owner, new_owner):
+ logging.debug('BaseInvite._name_owner_changed_cb %r %r %r', name,
+ new_owner, old_owner)
+ if name == self._handler and new_owner and not old_owner:
+ self._call_handle_with()
+
+
+class ActivityInvite(BaseInvite):
+ """Invitation to a shared activity."""
+ def __init__(self, dispatch_operation_path, handle, handler,
+ activity_properties):
+ BaseInvite.__init__(self, dispatch_operation_path, handle, handler)
+
+ if activity_properties is not None:
+ self._activity_properties = activity_properties
+ else:
+ self._activity_properties = {}
+
+ def get_color(self):
+ color = self._activity_properties.get('color', None)
+ return XoColor(color)
+
+ def join(self):
+ logging.error('ActivityInvite.join handler %r', self._handler)
+
+ registry = bundleregistry.get_registry()
+ bundle_id = self.get_bundle_id()
+ bundle = registry.get_bundle(bundle_id)
+ if bundle is None:
+ self._call_handle_with()
+ return
+
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(self._name_owner_changed_cb,
+ 'NameOwnerChanged',
+ 'org.freedesktop.DBus',
+ arg0=self._handler)
+
+ model = neighborhood.get_model()
+ activity_id = model.get_activity_by_room(self._handle).activity_id
+ misc.launch(bundle, color=self.get_color(), invited=True,
+ activity_id=activity_id)
+
+
+class PrivateInvite(BaseInvite):
+ def __init__(self, dispatch_operation_path, handle, handler,
+ private_channel):
+ BaseInvite.__init__(self, dispatch_operation_path, handle, handler)
+
+ self._private_channel = private_channel
+
+ def get_color(self):
+ client = gconf.client_get_default()
+ return XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ def join(self):
+ logging.error('PrivateInvite.join handler %r', self._handler)
+ registry = bundleregistry.get_registry()
+ bundle_id = self.get_bundle_id()
+ bundle = registry.get_bundle(bundle_id)
+
+ bus = dbus.SessionBus()
+ bus.add_signal_receiver(self._name_owner_changed_cb,
+ 'NameOwnerChanged',
+ 'org.freedesktop.DBus',
+ arg0=self._handler)
+ misc.launch(bundle, color=self.get_color(), invited=True,
+ uri=self._private_channel)
+
+
+class Invites(gobject.GObject):
+ __gsignals__ = {
+ 'invite-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ 'invite-removed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([object])),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._dispatch_operations = {}
+
+ client_handler = telepathyclient.get_instance()
+ client_handler.got_dispatch_operation.connect(
+ self.__got_dispatch_operation_cb)
+
+ def __got_dispatch_operation_cb(self, **kwargs):
+ logging.debug('__got_dispatch_operation_cb')
+ dispatch_operation_path = kwargs['dispatch_operation_path']
+ channel_path, channel_properties = kwargs['channels'][0]
+ properties = kwargs['properties']
+ channel_type = channel_properties[CHANNEL + '.ChannelType']
+ handle_type = channel_properties[CHANNEL + '.TargetHandleType']
+ handle = channel_properties[CHANNEL + '.TargetHandle']
+
+ if handle_type == HANDLE_TYPE_ROOM and \
+ channel_type == CHANNEL_TYPE_TEXT:
+ logging.debug('May be an activity, checking its properties')
+ connection_path = properties[CHANNEL_DISPATCH_OPERATION +
+ '.Connection']
+ connection_name = connection_path.replace('/', '.')[1:]
+
+ bus = dbus.Bus()
+ connection = bus.get_object(connection_name, connection_path)
+ connection.GetProperties(
+ channel_properties[CHANNEL + '.TargetHandle'],
+ dbus_interface=CONNECTION_INTERFACE_ACTIVITY_PROPERTIES,
+ reply_handler=partial(self.__get_properties_cb,
+ handle,
+ dispatch_operation_path),
+ error_handler=partial(self.__error_handler_cb,
+ handle,
+ channel_properties,
+ dispatch_operation_path,
+ channel_path,
+ properties))
+ else:
+ self._dispatch_non_sugar_invitation(handle,
+ channel_properties,
+ dispatch_operation_path,
+ channel_path,
+ properties)
+
+ def __get_properties_cb(self, handle, dispatch_operation_path, properties):
+ logging.debug('__get_properties_cb %r', properties)
+ handler = '%s.%s' % (CLIENT, properties['type'])
+ self._add_invite(dispatch_operation_path, handle, handler, properties)
+
+ def __error_handler_cb(self, handle, channel_properties,
+ dispatch_operation_path, channel_path,
+ properties, error):
+ logging.debug('__error_handler_cb %r', error)
+ exception_name = 'org.freedesktop.Telepathy.Error.NotAvailable'
+ if error.get_dbus_name() == exception_name:
+ self._dispatch_non_sugar_invitation(handle,
+ channel_properties,
+ dispatch_operation_path,
+ channel_path,
+ properties)
+ else:
+ raise error
+
+ def _dispatch_non_sugar_invitation(self, handle, channel_properties,
+ dispatch_operation_path, channel_path,
+ properties):
+ handler = None
+ channel_type = channel_properties[CHANNEL + '.ChannelType']
+ if channel_type == CHANNEL_TYPE_CONTACT_LIST:
+ self._handle_with(dispatch_operation_path, CLIENT + '.Sugar')
+ elif channel_type == CHANNEL_TYPE_TEXT:
+ handler = CLIENT + '.org.laptop.Chat'
+ self._add_private_invite(dispatch_operation_path, handle, handler,
+ channel_path, properties)
+ return
+ else:
+ self._call_handle_with(dispatch_operation_path, '')
+
+ if handler is not None:
+ logging.debug('Adding an invite from a non-Sugar client')
+ self._add_invite(dispatch_operation_path, handle, handler)
+
+ def _call_handle_with(self, dispatch_operation_path, handler):
+ logging.debug('_handle_with %r %r', dispatch_operation_path, handler)
+ bus = dbus.Bus()
+ obj = bus.get_object(CHANNEL_DISPATCHER, dispatch_operation_path)
+ dispatch_operation = dbus.Interface(obj, CHANNEL_DISPATCH_OPERATION)
+ dispatch_operation.HandleWith(handler,
+ reply_handler=self.__handle_with_reply_cb,
+ error_handler=self.__handle_with_reply_cb)
+
+ def __handle_with_reply_cb(self, error=None):
+ if error is not None:
+ logging.error('__handle_with_reply_cb %r', error)
+ else:
+ logging.debug('__handle_with_reply_cb')
+
+ def _add_invite(self, dispatch_operation_path, handle, handler,
+ activity_properties=None):
+ logging.debug('_add_invite %r %r %r', dispatch_operation_path, handle,
+ handler)
+ if dispatch_operation_path in self._dispatch_operations:
+ # there is no point to have more than one invite for the same
+ # dispatch operation
+ return
+
+ invite = ActivityInvite(dispatch_operation_path, handle, handler,
+ activity_properties)
+ self._dispatch_operations[dispatch_operation_path] = invite
+ self.emit('invite-added', invite)
+
+ def _add_private_invite(self, dispatch_operation_path, handle, handler,
+ channel_path, properties):
+ connection_path = properties[CHANNEL_DISPATCH_OPERATION +
+ '.Connection']
+ connection_name = connection_path.replace('/', '.')[1:]
+ private_channel = simplejson.dumps([connection_name,
+ connection_path, channel_path])
+ invite = PrivateInvite(dispatch_operation_path, handle, handler,
+ private_channel)
+ self._dispatch_operations[dispatch_operation_path] = invite
+ self.emit('invite-added', invite)
+
+ def remove_invite(self, invite):
+ del self._dispatch_operations[invite.dispatch_operation_path]
+ self.emit('invite-removed', invite)
+
+ def __iter__(self):
+ return self._dispatch_operations.values().__iter__()
+
+
+def get_instance():
+ global _instance
+ if not _instance:
+ _instance = Invites()
+ return _instance