From 664b914ec43186d50e1fc8c3271d15830f71b47e Mon Sep 17 00:00:00 2001 From: Walter Bender Date: Thu, 20 Jun 2013 08:25:13 -0400 Subject: [PATCH] Add webaccount/webservice extension support This is another patch in a series of patches for interacting with online accounts from Sugar. The relevant feature request is [1]. This patch introduces two new sections in sugar extensions: (1) webservice is stub for installing webservices; each web service is installed in a subdirectory of this directory. (2) cpsection/webaccount is a new control panel section for managing web service accounts. Account management services are loaded into individual subdirectories of cpsection/webaccount/services. A new helper method was also added to jarabe/webservice/accountsmanager and the unit test as updated accordingly. Note that this same directory structure can be set up in env.get_profile_path()/extensions for web services installed by the end user. [1] http://wiki.sugarlabs.org/go/Features/Web_services A first pass at a unit test for the new webaccount cpsection is also included. For naming consistency, this test was called test_webaccount while the exsiting test_webaccount was renamed to the more appropriate test_webservice. --- configure.ac | 3 + data/icons/Makefile.am | 3 +- data/icons/module-webaccount.svg | 15 ++++ extensions/Makefile.am | 2 +- extensions/cpsection/Makefile.am | 2 +- extensions/cpsection/__init__.py | 2 + extensions/cpsection/webaccount/Makefile.am | 8 ++ extensions/cpsection/webaccount/__init__.py | 24 ++++++ extensions/cpsection/webaccount/model.py | 0 .../cpsection/webaccount/services/Makefile.am | 6 ++ .../cpsection/webaccount/services/__init__.py | 2 + extensions/cpsection/webaccount/view.py | 82 +++++++++++++++++++ .../cpsection/webaccount/web_service.py | 23 ++++++ extensions/webservice/Makefile.am | 5 ++ extensions/webservice/__init__.py | 2 + po/POTFILES.in | 2 + src/jarabe/webservice/accountsmanager.py | 31 ++++++- tests/extensions/cpsection/__init__.py | 2 + .../cpsection/webaccount/services/__init__.py | 2 + .../webaccount/services/mock/__init__.py | 2 + .../webaccount/services/mock/service.py | 45 ++++++++++ .../cpsection/webaccount/web_service.py | 23 ++++++ tests/test_webaccount.py | 32 ++++++++ ...test_webaccounts.py => test_webservice.py} | 14 +++- tests/views/webaccount.py | 47 +++++++++++ 25 files changed, 374 insertions(+), 5 deletions(-) create mode 100644 data/icons/module-webaccount.svg create mode 100644 extensions/cpsection/webaccount/Makefile.am create mode 100644 extensions/cpsection/webaccount/__init__.py create mode 100644 extensions/cpsection/webaccount/model.py create mode 100644 extensions/cpsection/webaccount/services/Makefile.am create mode 100644 extensions/cpsection/webaccount/services/__init__.py create mode 100644 extensions/cpsection/webaccount/view.py create mode 100644 extensions/cpsection/webaccount/web_service.py create mode 100644 extensions/webservice/Makefile.am create mode 100644 extensions/webservice/__init__.py create mode 100644 tests/extensions/cpsection/__init__.py create mode 100644 tests/extensions/cpsection/webaccount/services/__init__.py create mode 100644 tests/extensions/cpsection/webaccount/services/mock/__init__.py create mode 100644 tests/extensions/cpsection/webaccount/services/mock/service.py create mode 100644 tests/extensions/cpsection/webaccount/web_service.py create mode 100644 tests/test_webaccount.py rename tests/{test_webaccounts.py => test_webservice.py} (90%) create mode 100644 tests/views/webaccount.py diff --git a/configure.ac b/configure.ac index ce927f10d2..454b69f387 100644 --- a/configure.ac +++ b/configure.ac @@ -61,8 +61,11 @@ extensions/cpsection/network/Makefile extensions/cpsection/power/Makefile extensions/cpsection/updater/backends/Makefile extensions/cpsection/updater/Makefile +extensions/cpsection/webaccount/services/Makefile +extensions/cpsection/webaccount/Makefile extensions/deviceicon/Makefile extensions/globalkey/Makefile +extensions/webservice/Makefile extensions/Makefile Makefile po/Makefile.in diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am index a35643a53a..6f0b172867 100644 --- a/data/icons/Makefile.am +++ b/data/icons/Makefile.am @@ -10,6 +10,7 @@ sugar_DATA = \ module-modemconfiguration.svg \ module-network.svg \ module-power.svg \ - module-updater.svg + module-updater.svg \ + module-webaccount.svg EXTRA_DIST = $(sugar_DATA) diff --git a/data/icons/module-webaccount.svg b/data/icons/module-webaccount.svg new file mode 100644 index 0000000000..5ec731683d --- /dev/null +++ b/data/icons/module-webaccount.svg @@ -0,0 +1,15 @@ + + +]> + + + + + + + + + + + diff --git a/extensions/Makefile.am b/extensions/Makefile.am index d4ab5342f2..b6d8af7a08 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -1 +1 @@ -SUBDIRS = cpsection deviceicon globalkey +SUBDIRS = cpsection deviceicon globalkey webservice diff --git a/extensions/cpsection/Makefile.am b/extensions/cpsection/Makefile.am index 9434d455ba..35794d345c 100644 --- a/extensions/cpsection/Makefile.am +++ b/extensions/cpsection/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = aboutme aboutcomputer background datetime frame keyboard language \ - modemconfiguration network power updater + modemconfiguration network power updater webaccount sugardir = $(pkgdatadir)/extensions/cpsection sugar_PYTHON = __init__.py diff --git a/extensions/cpsection/__init__.py b/extensions/cpsection/__init__.py index e69de29bb2..3ad9513f40 100644 --- a/extensions/cpsection/__init__.py +++ b/extensions/cpsection/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/extensions/cpsection/webaccount/Makefile.am b/extensions/cpsection/webaccount/Makefile.am new file mode 100644 index 0000000000..918656013c --- /dev/null +++ b/extensions/cpsection/webaccount/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = services +sugardir = $(pkgdatadir)/extensions/cpsection/webaccount + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py \ + web_service.py diff --git a/extensions/cpsection/webaccount/__init__.py b/extensions/cpsection/webaccount/__init__.py new file mode 100644 index 0000000000..0bb8067a6d --- /dev/null +++ b/extensions/cpsection/webaccount/__init__.py @@ -0,0 +1,24 @@ +# Copyright (C) 2013, Walter Bender +# +# 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 + +from gettext import gettext as _ +from pkgutil import extend_path + +__path__ = extend_path(__path__, __name__) + +CLASS = 'WebServicesConfig' +ICON = 'module-webaccount' +TITLE = _('Configure your Web Services') diff --git a/extensions/cpsection/webaccount/model.py b/extensions/cpsection/webaccount/model.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/cpsection/webaccount/services/Makefile.am b/extensions/cpsection/webaccount/services/Makefile.am new file mode 100644 index 0000000000..9e8af2df50 --- /dev/null +++ b/extensions/cpsection/webaccount/services/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = + +sugardir = $(pkgdatadir)/extensions/cpsection/webaccount/services + +sugar_PYTHON = \ + __init__.py diff --git a/extensions/cpsection/webaccount/services/__init__.py b/extensions/cpsection/webaccount/services/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/extensions/cpsection/webaccount/services/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/extensions/cpsection/webaccount/view.py b/extensions/cpsection/webaccount/view.py new file mode 100644 index 0000000000..047dfe0a86 --- /dev/null +++ b/extensions/cpsection/webaccount/view.py @@ -0,0 +1,82 @@ +# Copyright (C) 2013, Walter Bender - Raul Gutierrez Segales +# +# 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 glib +from gettext import gettext as _ + +from gi.repository import Gtk + +from jarabe.webservice.accountsmanager import get_webaccount_services +from jarabe.controlpanel.sectionview import SectionView + +from sugar3.graphics.icon import CanvasIcon +from sugar3.graphics import style + + +class WebServicesConfig(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + + services = get_webaccount_services() + if len(services) == 0: + label = Gtk.Label() + label.set_markup( + '' + + glib.markup_escape_text( + _('No web services are installed.\n' + 'Please visit %s for more details.' % + 'http://wiki.sugarlabs.org/go/WebServices')) + + '') + label.show() + self.add(label) + return + + vbox = Gtk.VBox() + hbox = Gtk.HBox(style.DEFAULT_SPACING) + + self._service_config_box = Gtk.VBox() + + for service in services: + icon = CanvasIcon(icon_name=service.get_icon_name()) + icon.connect('button_press_event', + service.config_service_cb, + self._service_config_box) + icon.show() + hbox.pack_start(icon, False, False, 0) + + hbox.show() + vbox.pack_start(hbox, False, False, 0) + + scrolled = Gtk.ScrolledWindow() + vbox.pack_start(scrolled, True, True, 0) + + self.add(vbox) + scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) + scrolled.show() + + workspace = Gtk.VBox() + scrolled.add_with_viewport(workspace) + workspace.show() + + workspace.add(self._service_config_box) + workspace.show_all() + vbox.show() + + def undo(self): + pass diff --git a/extensions/cpsection/webaccount/web_service.py b/extensions/cpsection/webaccount/web_service.py new file mode 100644 index 0000000000..3276f4b958 --- /dev/null +++ b/extensions/cpsection/webaccount/web_service.py @@ -0,0 +1,23 @@ +# Copyright (C) 2013, Walter Bender - Raul Gutierrez Segales +# +# 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 + + +class WebService(): + def get_icon_name(self): + raise "Not implemented" + + def config_service_cb(self, widget, event, container): + raise "Not implemented" diff --git a/extensions/webservice/Makefile.am b/extensions/webservice/Makefile.am new file mode 100644 index 0000000000..c3dc35a5d5 --- /dev/null +++ b/extensions/webservice/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = + +sugardir = $(pkgdatadir)/extensions/webservice +sugar_PYTHON = \ + __init__.py diff --git a/extensions/webservice/__init__.py b/extensions/webservice/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/extensions/webservice/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/po/POTFILES.in b/po/POTFILES.in index d7fa9eeab1..e1b647c473 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -27,6 +27,8 @@ extensions/cpsection/power/model.py extensions/cpsection/power/view.py extensions/cpsection/updater/__init__.py extensions/cpsection/updater/view.py +extensions/cpsection/webaccount/__init__.py +extensions/cpsection/webaccount/view.py extensions/deviceicon/battery.py extensions/deviceicon/frame.py extensions/deviceicon/network.py diff --git a/src/jarabe/webservice/accountsmanager.py b/src/jarabe/webservice/accountsmanager.py index 2b41752ffe..0e3b5aa699 100644 --- a/src/jarabe/webservice/accountsmanager.py +++ b/src/jarabe/webservice/accountsmanager.py @@ -49,7 +49,7 @@ def _get_webservice_paths(): return paths -def get_webaccount_paths(): +def _get_webaccount_paths(): paths = [] for path in [os.path.join(_user_extensions_path, 'cpsection', 'webaccount'), @@ -218,3 +218,32 @@ def get_active_accounts(): def has_configured_accounts(): return len(get_configured_accounts()) > 0 + + +def get_webaccount_services(): + _ensure_module_repository() + + service_paths = [] + for path in _get_webaccount_paths(): + service_paths.append(os.path.join(path, 'services')) + + services = [] + for service_path in service_paths: + if not os.path.exists(service_path): + continue + + folders = os.listdir(service_path) + for folder in folders: + if not os.path.isdir(os.path.join(service_path, folder)): + continue + + if not os.path.exists(os.path.join( + service_path, folder, 'service.py')): + continue + + module = _load_module(os.path.join(service_path, folder), + 'service') + if hasattr(module, 'get_service'): + services.append(module.get_service()) + + return services diff --git a/tests/extensions/cpsection/__init__.py b/tests/extensions/cpsection/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/tests/extensions/cpsection/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/tests/extensions/cpsection/webaccount/services/__init__.py b/tests/extensions/cpsection/webaccount/services/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/tests/extensions/cpsection/webaccount/services/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/tests/extensions/cpsection/webaccount/services/mock/__init__.py b/tests/extensions/cpsection/webaccount/services/mock/__init__.py new file mode 100644 index 0000000000..3ad9513f40 --- /dev/null +++ b/tests/extensions/cpsection/webaccount/services/mock/__init__.py @@ -0,0 +1,2 @@ +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/tests/extensions/cpsection/webaccount/services/mock/service.py b/tests/extensions/cpsection/webaccount/services/mock/service.py new file mode 100644 index 0000000000..8bf2815bc3 --- /dev/null +++ b/tests/extensions/cpsection/webaccount/services/mock/service.py @@ -0,0 +1,45 @@ +# Copyright (C) 2013, Walter Bender - Raul Gutierrez Segales +# +# 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 + +from gi.repository import Gtk + +from jarabe.webservice import accountsmanager + +from cpsection.webaccount.web_service import WebService + +_SERVICE_NAME = 'mock' + + +class WebService(WebService): + + def __init__(self): + self._account = accountsmanager.get_account(_SERVICE_NAME) + + def get_icon_name(self): + return _SERVICE_NAME + + def config_service_cb(self, widget, event, container): + label = Gtk.Label(_SERVICE_NAME) + + for c in container.get_children(): + container.remove(c) + + container.add(label) + container.show_all() + + +def get_service(): + return WebService() diff --git a/tests/extensions/cpsection/webaccount/web_service.py b/tests/extensions/cpsection/webaccount/web_service.py new file mode 100644 index 0000000000..3276f4b958 --- /dev/null +++ b/tests/extensions/cpsection/webaccount/web_service.py @@ -0,0 +1,23 @@ +# Copyright (C) 2013, Walter Bender - Raul Gutierrez Segales +# +# 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 + + +class WebService(): + def get_icon_name(self): + raise "Not implemented" + + def config_service_cb(self, widget, event, container): + raise "Not implemented" diff --git a/tests/test_webaccount.py b/tests/test_webaccount.py new file mode 100644 index 0000000000..5d1e575adf --- /dev/null +++ b/tests/test_webaccount.py @@ -0,0 +1,32 @@ +# Copyright (C) 2012, Daniel Narvaez +# Copyright (C) 2013, Walter Bender +# +# 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 + +from sugar3.test import unittest +from sugar3.test import uitree + +ACCOUNT_NAME = 'mock' + + +class TestWebAccount(unittest.UITestCase): + + def test_webaccount(self): + with self.run_view("webaccount"): + root = uitree.get_root() + + for name in [ACCOUNT_NAME]: + node = root.find_child(name=name, role_name='label') + self.assertIsNotNone(node) diff --git a/tests/test_webaccounts.py b/tests/test_webservice.py similarity index 90% rename from tests/test_webaccounts.py rename to tests/test_webservice.py index 16d94febd7..8a2a133aa9 100644 --- a/tests/test_webaccounts.py +++ b/tests/test_webservice.py @@ -26,7 +26,7 @@ ACCOUNT_NAME = 'mock' -tests_dir = os.path.dirname(__file__) +tests_dir = os.getcwd() extension_dir = os.path.join(tests_dir, 'extensions') web_extension_dir = os.path.join(extension_dir, 'webservice') @@ -53,10 +53,22 @@ def test_icon_theme(self): icon_path = os.path.join(web_extension_dir, ACCOUNT_NAME, 'icons') self.assertTrue(icon_path in icon_search_path) + def test_get_webaccount_services(self): + services = accountsmanager.get_webaccount_services() + self.assertTrue(len(services) > 0) + def test_get_all_accounts(self): accounts = accountsmanager.get_all_accounts() self.assertTrue(len(accounts) > 0) + def test_get_account(self): + account = accountsmanager.get_account('mock') + self.assertIsNotNone(account) + + def test_get_service(self): + account = accountsmanager.get_service('mock') + self.assertIsNotNone(account) + def test_get_configured_accounts(self): os.environ["MOCK_ACCOUNT_STATE"] = str(Account.STATE_VALID) accounts = accountsmanager.get_configured_accounts() diff --git a/tests/views/webaccount.py b/tests/views/webaccount.py new file mode 100644 index 0000000000..e1947f5897 --- /dev/null +++ b/tests/views/webaccount.py @@ -0,0 +1,47 @@ +# Copyright (C) 2013, Walter Bender +# +# 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 os +import sys + +from gi.repository import Gtk + +from jarabe import config +from jarabe.webservice.account import Account +from jarabe.webservice import accountsmanager + +ACCOUNT_NAME = 'mock' + +tests_dir = os.getcwd() +extension_dir = os.path.join(tests_dir, 'extensions') + +os.environ["MOCK_ACCOUNT_STATE"] = str(Account.STATE_VALID) +config.ext_path = extension_dir +sys.path.append(config.ext_path) + +window = Gtk.Window() +box = Gtk.HBox() +box.show() +window.add(box) + +services = accountsmanager.get_webaccount_services() +for service in services: + if service.get_icon_name() == ACCOUNT_NAME: + service.config_service_cb(None, None, box) + +window.show() + +Gtk.main()