diff options
author | Raul Gutierrez Segales <rgs@itevenworks.net> | 2013-02-03 18:23:02 (GMT) |
---|---|---|
committer | Raul Gutierrez Segales <rgs@itevenworks.net> | 2013-02-03 19:10:55 (GMT) |
commit | 92f971100d2a7a302992ed694b1dfc2719513429 (patch) | |
tree | 65b2101ce65850e0244d894b7d47d79939ed1931 /src | |
parent | 8a56a1dd6c6c71fc89c2ab04692b6e59c594f7f9 (diff) |
Make FacebookOnlineAccount an extension and move the OnlineAccount*
stuff into jarabe/web
Diffstat (limited to 'src')
-rw-r--r-- | src/jarabe/Makefile.am | 3 | ||||
-rw-r--r-- | src/jarabe/journal/journaltoolbox.py | 2 | ||||
-rw-r--r-- | src/jarabe/journal/palettes.py | 2 | ||||
-rw-r--r-- | src/jarabe/util/Makefile.am | 6 | ||||
-rw-r--r-- | src/jarabe/util/facebook.py | 294 | ||||
-rw-r--r-- | src/jarabe/util/facebook_online_account.py | 227 | ||||
-rw-r--r-- | src/jarabe/web/Makefile.am | 5 | ||||
-rw-r--r-- | src/jarabe/web/__init__.py | 18 | ||||
-rw-r--r-- | src/jarabe/web/online_account.py (renamed from src/jarabe/util/online_account.py) | 0 | ||||
-rw-r--r-- | src/jarabe/web/online_accounts_manager.py (renamed from src/jarabe/util/online_accounts_manager.py) | 17 |
10 files changed, 43 insertions, 531 deletions
diff --git a/src/jarabe/Makefile.am b/src/jarabe/Makefile.am index d926d9f..39099a5 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/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py index 71e614c..dc380aa 100644 --- a/src/jarabe/journal/journaltoolbox.py +++ b/src/jarabe/journal/journaltoolbox.py @@ -48,7 +48,7 @@ from jarabe.journal import model from jarabe.journal.palettes import ClipboardMenu from jarabe.journal.palettes import VolumeMenu from jarabe.journal import journalwindow -from jarabe.util import online_accounts_manager as oam +from jarabe.web import online_accounts_manager as oam _AUTOSEARCH_TIMEOUT = 1000 diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py index e03bc61..b69f55b 100644 --- a/src/jarabe/journal/palettes.py +++ b/src/jarabe/journal/palettes.py @@ -42,7 +42,7 @@ from jarabe.model import mimeregistry from jarabe.journal import misc from jarabe.journal import model from jarabe.journal import journalwindow -from jarabe.util import online_accounts_manager as oam +from jarabe.web import online_accounts_manager as oam class ObjectPalette(Palette): diff --git a/src/jarabe/util/Makefile.am b/src/jarabe/util/Makefile.am index 7f80330..3054b5a 100644 --- a/src/jarabe/util/Makefile.am +++ b/src/jarabe/util/Makefile.am @@ -5,8 +5,4 @@ sugardir = $(pythondir)/jarabe/util sugar_PYTHON = \ __init__.py \ emulator.py \ - facebook.py \ - facebook_online_account.py \ - normalize.py \ - online_account.py \ - online_accounts_manager.py + normalize.py diff --git a/src/jarabe/util/facebook.py b/src/jarabe/util/facebook.py deleted file mode 100644 index 92529dc..0000000 --- a/src/jarabe/util/facebook.py +++ /dev/null @@ -1,294 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2012 Raul Gutierrez S. - rgs@itevenworks.net - -#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 json -import logging -import pycurl -import time -import urllib - -from gi.repository import GObject - -class FbAccount(): - _access_token = "" - - @classmethod - def set_access_token(cls, access_token): - logging.debug("LOOK = %s" % (access_token)) - cls._access_token = access_token - - @classmethod - def access_token(cls): - return cls._access_token - -class FbObjectNotCreatedException(Exception): - pass - -class FbBadCall(Exception): - pass - -class FbPhoto(GObject.GObject): - PHOTOS_URL = "https://graph.facebook.com/me/photos?access_token=%s" - COMMENTS_URL = "https://graph.facebook.com/%s/comments" - - __gsignals__ = { - 'photo-created': (GObject.SignalFlags.RUN_FIRST, None, ([str])), - 'photo-create-failed': (GObject.SignalFlags.RUN_FIRST, None, ([str])), - 'comment-added': (GObject.SignalFlags.RUN_FIRST, None, ([str])), - 'comment-add-failed': (GObject.SignalFlags.RUN_FIRST, None, ([str])), - 'comments-downloaded': (GObject.SignalFlags.RUN_FIRST, None, ([object])), - 'comments-download-failed': (GObject.SignalFlags.RUN_FIRST, None, ([str])), - 'likes-downloaded': (GObject.SignalFlags.RUN_FIRST, None, ([object])), - } - - def __init__(self, fb_object_id=None): - GObject.GObject.__init__(self) - self.fb_object_id = fb_object_id - - def create(self, image_path): - GObject.idle_add(self._create, image_path) - - def add_comment(self, comment): - self.check_created('add_comment') - GObject.idle_add(self._add_comment, comment) - - def refresh_comments(self): - """ raise an exception if no one is listening """ - self.check_created('refresh_comments') - GObject.idle_add(self._refresh_comments) - - def check_created(self, method_name): - if self.fb_object_id is None: - errmsg = "Need to call create before calling %s" % (method_name) - raise FbObjectNotCreatedException(errmsg) - - def _add_comment(self, comment): - url = self.COMMENTS_URL % (self.fb_object_id) - - response = [] - def write_cb(buf): - response.append(buf) - - res = self._http_call(url, [('message', comment)], write_cb, post=True) - if res == 200: - try: - comment_id = self._id_from_response("".join(response)) - self.emit('comment-added', comment_id) - except FbBadCall as ex: - self.emit('comment-add-failed', str(ex)) - else: - logging.debug("_add_comment failed, HTTP resp code: %d" % (res)) - self.emit('comment-add-failed', "Add comment failed: %d" % (res)) - - def _create(self, image_path): - url = self.PHOTOS_URL % (FbAccount.access_token()) - c = pycurl.Curl() - params = [('source', (c.FORM_FILE, image_path))] - - response = [] - def write_cb(buf): - response.append(buf) - - result = self._http_call(url, params, write_cb, post=True) - if result == 200: - photo_id = self._id_from_response("".join(response)) - self.fb_object_id = photo_id - self.emit('photo-created', photo_id) - else: - logging.debug("_create failed, HTTP resp code: %d" % result) - - if result == 400: - failed_reason = "Expired access token." - elif result == 6: - failed_reason = "Network is down." - failed_reason += \ - "Please connect to the network and try again." - else: - failed_reason = "Failed reason unknown: %s" % (str(result)) - - self.emit('photo-create-failed', failed_reason) - - def _id_from_response(self, response_str): - response_object = json.loads(response_str) - - if not "id" in response_object: - raise FbBadCall(response_str) - - fb_object_id = response_object['id'].encode('ascii', 'replace') - return fb_object_id - - def _refresh_comments(self): - """ this blocks """ - url = self.COMMENTS_URL % (self.fb_object_id) - - logging.debug("_refresh_comments fetching %s" % (url)) - - response_comments = [] - def write_cb(buf): - response_comments.append(buf) - - ret = self._http_call(url, [], write_cb, post=False) - if ret != 200: - logging.debug("_refresh_comments failed, HTTP resp code: %d" % ret) - self.emit('comments-download-failed', - "Comments download failed: %d" % (ret)) - return - - logging.debug("_refresh_comments: %s" % ("".join(response_comments))) - - try: - response_data = json.loads("".join(response_comments)) - if 'data' not in response_data: - logging.debug("No data inside the FB response") - self.emit('comments-download-failed', - "Comments download failed with no data") - return - except Exception as ex: - logging.debug("Couldn't parse FB response: %s" % str(ex)) - self.emit('comments-download-failed', - "Comments download failed: %s" % (str(ex))) - return - - comments = [] - for c in response_data['data']: - comment = {} # this should be an Object - comment['from'] = c['from']['name'] - comment['message'] = c['message'] - comment['created_time'] = c['created_time'] - comment['like_count'] = c['like_count'] - comments.append(comment) - - if len(comments) > 0: - self.emit('comments-downloaded', comments) - else: - self.emit('comments-download-failed', 'No comments found') - - def _http_call(self, url, params, write_cb, post=False): - app_auth_params = [('access_token', FbAccount.access_token())] - - c = pycurl.Curl() - c.setopt(c.WRITEFUNCTION, write_cb) - - if post: - c.setopt(c.POST, 1) - c.setopt(c.HTTPPOST, app_auth_params + params) - else: - c.setopt(c.HTTPGET, 1) - params_str = urllib.urlencode(app_auth_params + params) - url = "%s?%s" % (url, params_str) - - logging.debug("_http_call: %s" % (url)) - - c.setopt(c.URL, url) - c.perform() - result = c.getinfo(c.HTTP_CODE) - c.close() - - return result - - -if __name__ == '__main__': - import sys - if len(sys.argv) != 3: - print "Tests need access_token and an image path!" - exit(1) - - access_token, photo_path = sys.argv[1:3] - FbAccount.set_access_token(access_token) - - -def test_create_photo(loop): - def photo_created_cb(photo, photo_id, loop): - print "Photo created: %s" % (photo_id) - loop.quit() - - photo = FbPhoto() - photo.connect('photo-created', photo_created_cb, loop) - photo.create(photo_path) - -def test_add_comment(loop): - def photo_created_cb(photo, photo_id, loop): - print "Photo created: %s" % (photo_id) - - def comment_added_cb(photo, comment_id, loop): - print "Comment created: %s" % (comment_id) - loop.quit() - return False - - photo = FbPhoto(photo_id) - photo.connect("comment-added", comment_added_cb, loop) - photo.add_comment("this is a test") - return False - - photo = FbPhoto() - photo.connect('photo-created', photo_created_cb, loop) - photo.create(photo_path) - -def test_get_comments(loop): - def photo_created_cb(photo, photo_id, loop): - print "Photo created: %s" % (photo_id) - - def comment_added_cb(photo, comment_id, loop): - print "Comment created: %s" % (comment_id) - - def comments_downloaded_cb(photo, comments, loop): - print "%s comments for photo %s" % \ - (len(comments), photo.fb_object_id) - - for c in comments: - print "Comment from %s with message: %s" % \ - (c["from"], c["message"]) - - loop.quit() - - photo.connect('comments-downloaded', - comments_downloaded_cb, - loop) - photo.refresh_comments() - return False - - photo = FbPhoto(photo_id) - photo.connect("comment-added", comment_added_cb, loop) - photo.add_comment("this is a test") - return False - - photo = FbPhoto() - photo.connect('photo-created', photo_created_cb, loop) - photo.create(photo_path) - - -def timeout_cb(test_name, loop): - print "%s timed out and failed" % (test_name) - loop.quit() - return False - -if __name__ == '__main__': - tests = [eval(t) for t in dir() if t.startswith('test_')] - - for t in tests: - print "\n=== Starting %s (%s) ===" % (t.__name__, time.time()) - loop = GObject.MainLoop() - tid = GObject.timeout_add(30000, timeout_cb, t.__name__, loop) - t(loop) - loop.run() - GObject.source_remove(tid) - print "=== Finished %s (%s) ===\n" % (t.__name__, time.time()) diff --git a/src/jarabe/util/facebook_online_account.py b/src/jarabe/util/facebook_online_account.py deleted file mode 100644 index dcc82ad..0000000 --- a/src/jarabe/util/facebook_online_account.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 Walter Bender, Raul Gutierrez Segales - -#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. - -from gettext import gettext as _ -import logging -import os -import tempfile -import time - -from gi.repository import Gtk -from gi.repository import GdkPixbuf -from gi.repository import GConf - -from sugar3.datastore import datastore -from sugar3.graphics.alert import Alert, NotifyAlert -from sugar3.graphics.icon import Icon - -from jarabe.journal import journalwindow -from jarabe.util import facebook -from jarabe.util import online_account - -ACCOUNT_NEEDS_ATTENTION = 0 -ACCOUNT_ACTIVE = 1 - - -class FacebookOnlineAccount(online_account.OnlineAccount): - - ACCESS_TOKEN_KEY = "/desktop/sugar/collaboration/facebook_access_token" - ACCESS_TOKEN_KEY_EXPIRATION_DATE = \ - "/desktop/sugar/collaboration/facebook_access_token_expiration_date" - - def __init__(self): - online_account.OnlineAccount.__init__(self) - self._client = GConf.Client.get_default() - facebook.FbAccount.set_access_token(self._access_token()) - - def is_configured(self): - return self._access_token() is not None - - def is_active(self): - expiration_date = \ - self._client.get_int(self.ACCESS_TOKEN_KEY_EXPIRATION_DATE) - return expiration_date != 0 and expiration_date > time.time() - - def get_share_menu(self, journal_entry_metadata): - if self.is_active(): - icon_name = 'facebook-share' - else: - icon_name = 'facebook-share-insensitive' - fb_share_menu = FacebookShareMenu(journal_entry_metadata) - fb_share_menu.set_image(Icon(icon_name=icon_name, - icon_size=Gtk.IconSize.MENU)) - fb_share_menu.show() - return fb_share_menu - - def get_refresh_button(self): - return FacebookRefreshButton(self.is_active()) - - def _access_token(self): - return self._client.get_string(self.ACCESS_TOKEN_KEY) - - -class FacebookShareMenu(online_account.OnlineShareMenu): - __gtype_name__ = 'JournalFacebookMenu' - - def __init__(self, metadata): - online_account.OnlineShareMenu.__init__(self, _('Facebook')) - - self._metadata = metadata - self._comment = '%s: %s' % (self._get_metadata_by_key('title'), - self._get_metadata_by_key('description')) - - self.connect('activate', self._facebook_share_menu_cb) - - def _get_metadata_by_key(self, key, default_value=''): - if key in self._metadata: - return self._metadata[key] - return default_value - - def _facebook_share_menu_cb(self, menu_item): - logging.debug('_facebook_share_menu_cb') - - tmp_file = tempfile.mktemp() - self._image_file_from_metadata(tmp_file) - - photo = facebook.FbPhoto() - photo.connect('photo-created', self._photo_created_cb, tmp_file) - photo.connect('photo-create-failed', - self._photo_create_failed_cb, - tmp_file) - result = photo.create(tmp_file) - - def _photo_created_cb(self, fb_photo, fb_object_id, tmp_file): - logging.debug("_photo_created_cb") - - if os.path.exists(tmp_file): - os.unlink(tmp_file) - - fb_photo.connect('comment-added', self._comment_added_cb) - fb_photo.connect('comment-add-failed', self._comment_add_failed_cb) - fb_photo.add_comment(self._comment) - - try: - ds_object = datastore.get(self._metadata['uid']) - ds_object.metadata['fb_object_id'] = fb_object_id - datastore.write(ds_object, update_mtime=False) - except Exception as ex: - logging.debug("_photo_created_cb failed to write to datastore: " % \ - str(ex)) - - def _photo_create_failed_cb(self, fb_photo, failed_reason, tmp_file): - logging.debug("_photo_create_failed_cb") - - if os.path.exists(tmp_file): - os.unlink(tmp_file) - - self._fb_notify(failed_reason) - - def _comment_added_cb(self, fb_photo, fb_comment_id): - logging.debug("_comment_added_cb") - self._fb_notify(_('Upload successful')) - - def _comment_add_failed_cb(self, fb_photo, failed_reason): - logging.debug("_comment_add_failed_cb") - self._fb_notify(failed_reason) - - def _fb_notify(self, message): - alert = NotifyAlert() - title_string = _('Facebook') - alert.props.title = title_string - alert.props.msg = message - alert.connect('response', self._facebook_alert_response_cb) - journalwindow.get_journal_window().add_alert(alert) - alert.show() - - def _facebook_alert_response_cb(self, alert, response_id): - journalwindow.get_journal_window().remove_alert(alert) - - def _image_file_from_metadata(self, image_path): - """ Load a pixbuf from a Journal object. """ - pixbufloader = \ - GdkPixbuf.PixbufLoader.new_with_mime_type('image/png') - pixbufloader.set_size(300, 225) - try: - pixbufloader.write(self._metadata['preview']) - pixbuf = pixbufloader.get_pixbuf() - except Exception as ex: - logging.debug("_image_file_from_metadata: %s" % (str(ex))) - pixbuf = None - - pixbufloader.close() - if pixbuf: - pixbuf.savev(image_path, 'png', [], []) - - -class FacebookRefreshButton(online_account.OnlineRefreshButton): - def __init__(self, is_active): - online_account.OnlineRefreshButton.__init__(self, 'facebook-refresh-insensitive') - - self._metadata = None - self._is_active = is_active - self.set_tooltip(_('Facebook refresh')) - self.set_sensitive(False) - self.connect('clicked', self._fb_refresh_button_clicked_cb) - self.show() - - def set_metadata(self, metadata): - self._metadata = metadata - if self._is_active: - if self._metadata and 'fb_object_id' in self._metadata: - self.set_sensitive(True) - self.set_icon_name('facebook-refresh') - - def _fb_refresh_button_clicked_cb(self, button): - logging.debug('_fb_refresh_button_clicked_cb') - - if self._metadata is None: - logging.debug('_fb_refresh_button_clicked_cb called without metadata') - return - - fb_photo = facebook.FbPhoto(self._metadata['fb_object_id']) - fb_photo.connect('comments-downloaded', - self._fb_comments_downloaded_cb) - fb_photo.connect('comments-download-failed', - self._fb_comments_download_failed_cb) - fb_photo.refresh_comments() - - def _fb_comments_downloaded_cb(self, fb_photo, comments): - logging.debug('_fb_comments_downloaded_cb') - - ds_object = datastore.get(self._metadata['uid']) - for comment in comments: - c_str = "%s: %s" % (comment['from'], comment['message']) - ds_object.metadata['description'] += c_str - - datastore.write(ds_object, update_mtime=False) - - def _fb_comments_download_failed_cb(self, fb_photo, failed_reason): - logging.debug('_fb_comments_download_failed_cb: %s' % (failed_reason)) - alert = NotifyAlert() - alert.props.title = _('Comments download') - alert.props.msg = failed_reason - alert.connect('response', self.__fb_refresh_offline_response_cb) - journalwindow.get_journal_window().add_alert(alert) - alert.show() - - def __fb_refresh_offline_response_cb(self, alert, alert_id): - pass diff --git a/src/jarabe/web/Makefile.am b/src/jarabe/web/Makefile.am new file mode 100644 index 0000000..b751410 --- /dev/null +++ b/src/jarabe/web/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/jarabe/web +sugar_PYTHON = \ + __init__.py \ + online_account.py \ + online_accounts_manager.py diff --git a/src/jarabe/web/__init__.py b/src/jarabe/web/__init__.py new file mode 100644 index 0000000..9c80ecb --- /dev/null +++ b/src/jarabe/web/__init__.py @@ -0,0 +1,18 @@ +"""OLPC Sugar Jarabe utility modules +""" + +# Copyright (C) 2008, One Laptop Per Child +# +# 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 diff --git a/src/jarabe/util/online_account.py b/src/jarabe/web/online_account.py index 560c252..560c252 100644 --- a/src/jarabe/util/online_account.py +++ b/src/jarabe/web/online_account.py diff --git a/src/jarabe/util/online_accounts_manager.py b/src/jarabe/web/online_accounts_manager.py index c262968..cb7a7ce 100644 --- a/src/jarabe/util/online_accounts_manager.py +++ b/src/jarabe/web/online_accounts_manager.py @@ -21,15 +21,28 @@ #THE SOFTWARE. from gi.repository import GObject +import logging +import os -from jarabe.util import facebook_online_account as fboa +from jarabe import config class OnlineAccountsManager(GObject.GObject): @classmethod def all_accounts(cls): accounts = [] - accounts.append(fboa.FacebookOnlineAccount()) + + for f in os.listdir(os.path.join(config.ext_path, 'web')): + if f.endswith('.py') and not f.startswith('__'): + module_name = f[:-3] + logging.debug("OnlineAccountsManager loading %s" % \ + (module_name)) + try: + mod = __import__('web.' + module_name, globals(), + locals(), [module_name]) + accounts.append(mod.get_account()) + except Exception: + logging.exception('Exception while loading extension:') return accounts @classmethod |