Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter@sugarlabs.org>2013-06-09 10:55:49 (GMT)
committer Daniel Narvaez <dwnarvaez@gmail.com>2013-06-10 13:42:19 (GMT)
commit14659c2ef2948c2d52908e9f2ac476ab72369613 (patch)
treed8b7f4b4f6c64b477d2756c4ca205f26c13b7796
parentde87d97df7dd48288bda14c8ffc467d635d30ee4 (diff)
Add online service support
This patch adds a new section to sugar/src/jarabe to manage web services. The relevant feature request is [1]. Adds a new web section of jarabe is for a prototype of a new online account management class that manages the services described above. The services themselves are intended to be kept in subdirectors of sugar/extensions/web and are the subject of a separate patch. Also in a separate patch will be a UI extention to the Journal toolbar and palette, adding new menuitems. (Includes a unit test) [1] http://wiki.sugarlabs.org/go/Features/Web_services
-rw-r--r--configure.ac1
-rw-r--r--src/jarabe/Makefile.am3
-rw-r--r--src/jarabe/web/Makefile.am5
-rw-r--r--src/jarabe/web/__init__.py0
-rw-r--r--src/jarabe/web/account.py113
-rw-r--r--src/jarabe/web/accountsmanager.py101
-rw-r--r--tests/extensions/web/mock/__init__.py0
-rw-r--r--tests/extensions/web/mock/account.py62
-rw-r--r--tests/extensions/web/mock/icons/mock.svg25
-rw-r--r--tests/test_webaccounts.py100
10 files changed, 409 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 0ff82d6..84e2555 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,7 @@ src/jarabe/model/Makefile
src/jarabe/util/Makefile
src/jarabe/util/telepathy/Makefile
src/jarabe/view/Makefile
+src/jarabe/web/Makefile
src/Makefile
])
diff --git a/src/jarabe/Makefile.am b/src/jarabe/Makefile.am
index 2bdf412..f51cb90 100644
--- a/src/jarabe/Makefile.am
+++ b/src/jarabe/Makefile.am
@@ -6,7 +6,8 @@ SUBDIRS = \
model \
view \
intro \
- util
+ util \
+ web
sugardir = $(pythondir)/jarabe
sugar_PYTHON = \
diff --git a/src/jarabe/web/Makefile.am b/src/jarabe/web/Makefile.am
new file mode 100644
index 0000000..b51b5a9
--- /dev/null
+++ b/src/jarabe/web/Makefile.am
@@ -0,0 +1,5 @@
+sugardir = $(pythondir)/jarabe/web
+sugar_PYTHON = \
+ __init__.py \
+ account.py \
+ accountsmanager.py
diff --git a/src/jarabe/web/__init__.py b/src/jarabe/web/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/jarabe/web/__init__.py
diff --git a/src/jarabe/web/account.py b/src/jarabe/web/account.py
new file mode 100644
index 0000000..f94c7c2
--- /dev/null
+++ b/src/jarabe/web/account.py
@@ -0,0 +1,113 @@
+# Copyright (c) 2013 Walter Bender, Raul Gutierrez Segales
+# Copyright (c) 2013 SugarLabs
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import logging
+
+from gi.repository import GObject
+
+
+class Account():
+ ''' Account is a prototype class for online accounts. It provides
+ stubs for public methods that are used by online services.
+ '''
+
+ STATE_NONE = 0
+ STATE_VALID = 1
+ STATE_EXPIRED = 2
+
+ def get_description(self):
+ ''' get_description returns a brief description of the online
+ service. The description is used in palette menuitems and on
+ the webservices control panel.
+
+ :returns: online-account name
+ :rtype: string
+ '''
+ raise NotImplementedError
+
+ def get_token_state(self):
+ ''' get_token_state returns an enum to describe the state of
+ the online service:
+ State.NONE means there is no token, e.g., the service is not
+ configured.
+ State.VALID means there is a valid token, e.g., the service is
+ available for use.
+ State.EXPIRED means the token is no longer valid.
+
+ :returns: token state
+ :rtype: enum
+ '''
+ raise NotImplementedError
+
+ def get_shared_journal_entry(self):
+ ''' get_shared_journal_entry returns a class used to
+ intermediate between the online service and the Sugar UI
+ elements.
+
+ :returns: SharedJournalEntry()
+ :rtype: SharedJournalEntry
+ '''
+ return NotImplemented
+
+
+class SharedJournalEntry():
+ ''' SharedJournalEntry is a class used to intermediate between the
+ online service and the Sugar UI elements (MenuItems used in the
+ Journal UI) for online accounts. It provides stubs for public
+ methods that are used by online services.
+
+ The comments-changed signal is emitted by the online service if
+ changes to the 'comments' metadata have been made.
+
+ :emits: metadata['comments']
+ :type: string
+ '''
+
+ __gsignals__ = {
+ 'comments-changed': (GObject.SignalFlags.RUN_FIRST, None, ([str]))
+ }
+
+ def get_share_menu(self, metadata):
+ ''' get_share_menu returns a menu item used on the Copy To
+ palette in the Journal and on the Journal detail-view toolbar.
+
+ :param: journal_entry_metadata
+ :type: dict
+ :returns: MenuItem
+ :rtype: MenuItem
+ '''
+ raise NotImplementedError
+
+ def get_refresh_menu(self):
+ ''' get_refresh_menu returns a menu item used on the Journal
+ detail-view toolbar.
+
+ :returns: MenuItem
+ :rtype: MenuItem
+ '''
+ raise NotImplementedError
+
+ def set_metadata(self, metadata):
+ ''' The online account uses this method to set metadata in the
+ Sugar journal and provide a means of updating menuitem status,
+ e.g., enabling the refresh menu after a successful transfer.
+
+ :param: journal_entry_metadata
+ :type: dict
+ '''
+ raise NotImplementedError
diff --git a/src/jarabe/web/accountsmanager.py b/src/jarabe/web/accountsmanager.py
new file mode 100644
index 0000000..90cb94e
--- /dev/null
+++ b/src/jarabe/web/accountsmanager.py
@@ -0,0 +1,101 @@
+# Copyright (c) 2013 Walter Bender
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import os
+import logging
+
+from gi.repository import Gtk
+
+from jarabe import config
+from sugar3.web.account import Account
+
+_accounts = []
+
+
+def get_all_accounts():
+ ''' Returns a list of all installed online account managers '''
+ global _accounts
+ if len(_accounts) > 0:
+ return _accounts
+
+ web_path = os.path.join(config.ext_path, 'web')
+ try:
+ web_path_dirs = os.listdir(web_path)
+ except OSError, e:
+ web_path_dirs = []
+ logging.warning('listdir: %s: %s' % (web_path, e))
+
+ for d in web_path_dirs:
+ dir_path = os.path.join(web_path, d)
+ module = _load_module(dir_path)
+ if module is not None:
+ _accounts.append(module)
+ _extend_icon_theme_search_path(dir_path)
+
+ return _accounts
+
+
+def _load_module(dir_path):
+ module = None
+ if os.path.isdir(dir_path):
+ for f in os.listdir(dir_path):
+ if f == 'account.py':
+ module_name = f[:-3]
+ logging.debug('OnlineAccountsManager loading %s' %
+ (module_name))
+ module_path = 'web.%s.%s' % (os.path.basename(dir_path),
+ module_name)
+ try:
+ mod = __import__(module_path, globals(), locals(),
+ [module_name])
+ if hasattr(mod, 'get_account'):
+ module = mod.get_account()
+
+ except Exception as e:
+ logging.exception('Exception while loading %s: %s' %
+ (module_name, str(e)))
+
+ return module
+
+
+def _extend_icon_theme_search_path(dir_path):
+ icon_theme = Gtk.IconTheme.get_default()
+ icon_search_path = icon_theme.get_search_path()
+
+ try:
+ icon_path_dirs = os.listdir(dir_path)
+ except OSError, e:
+ icon_path_dirs = []
+ logging.warning('listdir: %s: %s' % (dir_path, e))
+
+ for f in icon_path_dirs:
+ if f == 'icons':
+ icon_path = os.path.join(dir_path, f)
+ if os.path.isdir(icon_path) and \
+ icon_path not in icon_search_path:
+ icon_theme.append_search_path(icon_path)
+
+
+def get_configured_accounts():
+ return [a for a in get_all_accounts()
+ if a.get_token_state() in (Account.STATE_VALID,
+ Account.STATE_EXPIRED)]
+
+
+def get_active_accounts():
+ return [a for a in get_all_accounts()
+ if a.get_token_state() == Account.STATE_VALID]
diff --git a/tests/extensions/web/mock/__init__.py b/tests/extensions/web/mock/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/extensions/web/mock/__init__.py
diff --git a/tests/extensions/web/mock/account.py b/tests/extensions/web/mock/account.py
new file mode 100644
index 0000000..176af5f
--- /dev/null
+++ b/tests/extensions/web/mock/account.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2013 Walter Bender
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy
+#of this software and associated documentation files (the "Software"), to deal
+#in the Software without restriction, including without limitation the rights
+#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+#copies of the Software, and to permit persons to whom the Software is
+#furnished to do so, subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in
+#all copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+#THE SOFTWARE.
+
+import os
+
+from sugar3.graphics.menuitem import MenuItem
+from jarabe.web import account
+
+ACCOUNT_NAME = 'mock'
+
+
+class MockAccount(account.Account):
+ def __init__(self):
+ return
+
+ def get_description(self):
+ return ACCOUNT_NAME
+
+ def get_shared_journal_entry():
+ return MockSharedJournalEntry()
+
+ def get_token_state(self):
+ return os.environ["MOCK_ACCOUNT_STATE"]
+
+
+class MockSharedJournalEnrty(account.SharedJournalEntry):
+ def __init__(self):
+ return
+
+ def get_share_menu(self, journal_entry_metadata):
+ share_menu = MenuItem()
+ return share_menu
+
+ def get_refresh_menu(self):
+ refresh_menu = MenuItem()
+ return refresh_menu
+
+ def set_metadata(self, metadata):
+ return
+
+
+def get_account():
+ return MockAccount()
diff --git a/tests/extensions/web/mock/icons/mock.svg b/tests/extensions/web/mock/icons/mock.svg
new file mode 100644
index 0000000..7a9434c
--- /dev/null
+++ b/tests/extensions/web/mock/icons/mock.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" ?><!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) --><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#A0A0A0">
+ <!ENTITY fill_color "#282828">
+]><svg enable-background="new 0 0 55.125 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">
+ <rect
+ width="54"
+ height="54"
+ ry="3.5"
+ x="0.5"
+ y="0.5"
+ style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:#FFFFFF;stroke-width:1.0;stroke-opacity:1" />
+ <rect
+ width="46.25"
+ height="7.75"
+ ry="0"
+ x="4.5"
+ y="42"
+ style="fill:&stroke_color;;fill-opacity:1;fill-rule:nonzero;stroke:none" />
+<text>
+ <tspan
+ x="6"
+ y="45"
+ id="tspan2839"
+ style="font-size:44px;font-style:normal;font-variant:normal;font-weight:bold;fill:#ffffff;font-family:DejaVu Sans;">M</tspan></text>
+</svg>
diff --git a/tests/test_webaccounts.py b/tests/test_webaccounts.py
new file mode 100644
index 0000000..a64662b
--- /dev/null
+++ b/tests/test_webaccounts.py
@@ -0,0 +1,100 @@
+# 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
+import unittest
+
+from gi.repository import Gtk
+
+from jarabe import config
+
+from sugar3.web.account import Account
+from sugar3.web import accountsmanager
+
+ACCOUNT_NAME = 'mock'
+
+tests_dir = os.path.dirname(__file__)
+base_dir = os.path.dirname(tests_dir)
+extension_dir = os.path.join(tests_dir, 'extensions')
+web_extension_dir = os.path.join(extensions_dir, 'web')
+
+
+class TestWebAccounts(unittest.TestCase):
+ def setUp(self):
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_NONE
+ self.save_ext_path = config.ext_path
+ config.ext_path = extension_dir
+
+ def test_get_description(self):
+ accounts = accountsmanager.get_all_accounts()
+ found_mock_account = False
+ for account in accounts:
+ if account.get_description() == ACCOUNT_NAME:
+ found_mock_account = True
+ break
+ self.assertTrue(found_mock_account)
+
+ def test_icon_theme(self):
+ icon_theme = Gtk.IconTheme.get_default()
+ icon_search_path = icon_theme.get_search_path()
+ icon_path = os.path.join(web_extension_dir, ACCOUNT_NAME, 'icons')
+ self.assertTrue(icon_path in icon_search_path)
+
+ def test_get_all_accounts(self):
+ accounts = accountsmanager.get_all_accounts()
+ self.assertTrue(len(self.accounts) > 0)
+
+ def test_get_configured_accounts(self):
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_VALID
+ accounts = accountsmanager.get_configured_accounts()
+ count = len(accounts)
+ self.assertTrue(count > 0)
+
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_NONE
+ accounts = accountsmanager.get_configured_accounts()
+ self.assertTrue(len(self.accounts) == count - 1)
+
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_EXPIRED
+ accounts = accountsmanager.get_active_accounts()
+ self.assertTrue(len(self.accounts) == count)
+
+ def test_get_active_accounts(self):
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_VALID
+ accounts = accountsmanager.get_active_accounts()
+ count = len(accounts)
+ self.assertTrue(count > 0)
+
+ os.environ["MOCK_ACCOUNT_STATE"] = Account.STATE_EXPIRED
+ accounts = accountsmanager.get_active_accounts()
+ self.assertTrue(len(self.accounts) == count - 1)
+
+ def test_share_menu(self):
+ accounts = accountsmanager.get_all_accounts()
+ for account in accounts:
+ shared_journal_entry = account.get_shared_journal_entry()
+ share_menu = shared_journal_entry.get_shared_menu()
+ self.assertIsNotNone(share_menu)
+
+ def test_refresh_menu(self):
+ accounts = accountsmanager.get_all_accounts()
+ for account in accounts:
+ shared_journal_entry = account.get_shared_journal_entry()
+ refresh_menu = shared_journal_entry.get_refresh_menu()
+ self.assertIsNotNone(refresh_menu)
+
+ def tearDown(self):
+ config.ext_path = self.save_ext_path