# Copyright (C) 2012 Aleksey Lim # # 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 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import time import logging import subprocess from gettext import gettext as _ from gi.repository import Gtk from gi.repository import GObject from sugar3.graphics.alert import NotifyAlert, ErrorAlert from sugar_network import api_url, server_mode, IPCClient from active_toolkit.options import Option from jarabe import plugins ORDER = 5 TITLE = _('Sugar Network integration') SN_BROWSER_NAME = 'sugar-network-browser' SN_MASER_URL = 'http://api-devel.network.sugarlabs.org' _ALERT_SEVERITIES = { # severity: (alert_class, alert_message) 'error': (ErrorAlert, _('Sugar Network error')), None: (NotifyAlert, _('Sugar Network')), } _logger = logging.getLogger('plugins.sn') _bundleregistry = None _launcher = None _client = None _browser = None Option.seek('local', [api_url, server_mode]) def init(): global _bundleregistry if api_url.value and api_url.value != SN_MASER_URL: # For now, keep only global SN server to make testing more relibale api_url.value = SN_MASER_URL Option.save() # Activities are assumed to be handled by SN plugins.blacklist('cpsection', 'updater') import jarabe.model.bundleregistry from .bundleregistry import BundleRegistry _bundleregistry = BundleRegistry() jarabe.model.bundleregistry.get_registry = lambda: _bundleregistry from jarabe.model.shell import Activity from jarabe.view import palettes from .browser import Palette Activity.get_bundle_path = _get_bundle_path palettes.predefined[SN_BROWSER_NAME] = Palette def start(): global _launcher from jarabe.journal import misc from .launcher import Launcher _launcher = Launcher() misc.launch = _launcher.launch from jarabe.model.shell import get_model shell = get_model() def delayed_start(): get_client().post([], {'event': 'delayed-start'}, cmd='publish') get_browser() def activity_added_cb(model, activity): if activity.is_journal(): shell.disconnect_by_func(activity_added_cb) GObject.idle_add(delayed_start) shell.connect('activity-added', activity_added_cb) def control_panel_section(): section = Gtk.VBox() server_box = Gtk.VBox() section.pack_start(server_box, False, False, 0) master_button = Gtk.RadioButton() master_button.props.label = _('Connect to master server') server_box.pack_start(master_button, False, False, 0) discover_button = Gtk.RadioButton(master_button) discover_button.props.label = _('Auto discover server in local network') server_box.pack_start(discover_button, False, False, 0) if api_url.value: master_button.props.active = True else: discover_button.props.active = True server_mode_button = Gtk.CheckButton() server_mode_button.props.label = _('Behave as a Sugar Network server ' 'providing data from mounted device') server_mode_button.props.active = server_mode.value section.pack_start(server_mode_button, False, False, 0) def master_toggled_cb(button, url): api_url.value = url Option.save() master_button.connect('toggled', master_toggled_cb, SN_MASER_URL) discover_button.connect('toggled', master_toggled_cb, '') def server_mode_toggled_cb(button): server_mode.value = button.props.active Option.save() server_mode_button.connect('toggled', server_mode_toggled_cb) section.show_all() return section def get_registry(): return _bundleregistry def get_client(): global _client if _client is None: ts = time.time() subprocess.check_call([ 'sugar-network-service', 'start', '--webui', '--delayed-start', '--lazy-open', '--replace', ]) _logger.debug('Starting sugar-network-service took %s seconds', time.time() - ts) _client = _Client() return _client def get_browser(): global _browser if _browser is None: from .browser import Browser _browser = Browser() return _browser def add_alert(severity, callback=None, **kwargs): window = get_browser() def response_cb(alert, response_id): window.remove_alert(alert) if callback is not None: callback() del alert cls, title = _ALERT_SEVERITIES.get(severity) or _ALERT_SEVERITIES[None] if 'title' not in kwargs: kwargs['title'] = title alert = cls(**kwargs) alert.connect('response', response_cb) window.add_alert(alert) window.reveal() def _get_bundle_path(self): # pylint: disable-msg=W0212 if not self._windows: return None pid = self._windows[0].get_pid() with file('/proc/%d/environ' % pid) as f: for env in f.read().split('\0'): if env.startswith('SUGAR_BUNDLE_PATH='): return env.split('=', 1)[-1] class _Client(GObject.GObject): __gsignals__ = { 'event': ( GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, [str, GObject.TYPE_PYOBJECT]), } def __init__(self): GObject.GObject.__init__(self) self._client = IPCClient() self._subscription = self._client.subscribe() GObject.io_add_watch(self._subscription.fileno(), GObject.IO_IN | GObject.IO_HUP, self.__subscription_cb) def __getattr__(self, name): return getattr(self._client, name) def __subscription_cb(self, source, cb_condition, pipe, activity_id): try: event = self._subscription.pull() if event is not None: event_type = event['event'] if event_type == 'alert': add_alert(event['severity'], msg=event['message']) elif event_type == 'sync_complete': add_alert('info', msg=_('Synchronization completed')) self.emit('event', event_type, event) except Exception: _logger.exception('Cannot dispatch %r event', event) return True