diff options
Diffstat (limited to 'rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch')
-rw-r--r-- | rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch b/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch new file mode 100644 index 0000000..3cb7872 --- /dev/null +++ b/rpms/sugar/0060-Replace-activity-updater-with-microformat-compatible.patch @@ -0,0 +1,626 @@ +From 15aa8fb2be53a1950f15d1a4ae630967a82319b9 Mon Sep 17 00:00:00 2001 +From: Anish Mangal <anish@sugarlabs.org> +Date: Mon, 10 Jan 2011 22:36:03 -0300 +Subject: [PATCH sugar 60/74] Replace activity updater with microformat + compatible one + +This patch replaces the Sugar activity updater with one, that supports +activity microformats. + ++ The updater now allows installation of new activities which were not +previously installed. + ++ The updater uses the optional olpc-activity-name and +olpc-activity-size +tags. + + - If olpc-activity-name is not present, the activity name is derived + from the bundle id. For example, org.Sugarlabs.RecordActivity + will be listed as Record. + + - If olpc-activity-size is not present, an additional + http request is made to ascertain the size of the + bundle. + ++ If the size returned as zero, the bundle is removed from the list of + those which may be updated. + ++ To install new bundles, a metabundle class has been created, which +acts as an empty structure. + +Co-Authored by Anish Mangal <anish@sugarlabs.org> +Co-Authored by Akash Gangil <akashg1611@gmail.com> + +Signed-off-by: Anish Mangal <anish@sugarlabs.org> +--- + data/sugar.schemas.in | 12 ++ + extensions/cpsection/updater/backends/Makefile.am | 2 +- + extensions/cpsection/updater/backends/aslo.py | 164 ---------------- + .../cpsection/updater/backends/microformat.py | 203 ++++++++++++++++++++ + extensions/cpsection/updater/model.py | 87 ++++++--- + extensions/cpsection/updater/view.py | 18 ++- + 6 files changed, 285 insertions(+), 201 deletions(-) + delete mode 100644 extensions/cpsection/updater/backends/aslo.py + create mode 100644 extensions/cpsection/updater/backends/microformat.py + +diff --git a/data/sugar.schemas.in b/data/sugar.schemas.in +index 6c0d70b..4c17ff0 100644 +--- a/data/sugar.schemas.in ++++ b/data/sugar.schemas.in +@@ -117,6 +117,18 @@ + </schema> + + <schema> ++ <key>/schemas/desktop/sugar/updater_url</key> ++ <applyto>/desktop/sugar/updater_url</applyto> ++ <owner>sugar</owner> ++ <type>string</type> ++ <default>http://activities-testing.sugarlabs.org/services/micro-format.php?collection_nickname=fructose</default> ++ <locale name="C"> ++ <short>Activity updater URL.</short> ++ <long>This key contains the url which the microformat compatible activity updater will search for activity updates.</long> ++ </locale> ++ </schema> ++ ++ <schema> + <key>/schemas/desktop/sugar/backup_url</key> + <applyto>/desktop/sugar/backup_url</applyto> + <owner>sugar</owner> +diff --git a/extensions/cpsection/updater/backends/Makefile.am b/extensions/cpsection/updater/backends/Makefile.am +index e280a07..e9c1284 100644 +--- a/extensions/cpsection/updater/backends/Makefile.am ++++ b/extensions/cpsection/updater/backends/Makefile.am +@@ -1,5 +1,5 @@ + sugardir = $(pkgdatadir)/extensions/cpsection/updater/backends + + sugar_PYTHON = \ +- aslo.py \ ++ microformat.py \ + __init__.py +diff --git a/extensions/cpsection/updater/backends/aslo.py b/extensions/cpsection/updater/backends/aslo.py +deleted file mode 100644 +index 6504e9e..0000000 +--- a/extensions/cpsection/updater/backends/aslo.py ++++ /dev/null +@@ -1,164 +0,0 @@ +-#!/usr/bin/python +-# Copyright (C) 2009, Sugar Labs +-# +-# 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 +- +-"""Activity information microformat parser. +- +-Activity information is embedded in HTML/XHTML/XML pages using a +-Resource Description Framework (RDF) http://www.w3.org/RDF/ . +- +-An example:: +- +-<?xml version="1.0" encoding="UTF-8"?> +-<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" +- xmlns:em="http://www.mozilla.org/2004/em-rdf#"> +-<RDF:Description about="urn:mozilla:extension:bounce"> +- <em:updates> +- <RDF:Seq> +- <RDF:li resource="urn:mozilla:extension:bounce:7"/> +- </RDF:Seq> +- </em:updates> +-</RDF:Description> +- +-<RDF:Description about="urn:mozilla:extension:bounce:7"> +- <em:version>7</em:version> +- <em:targetApplication> +- <RDF:Description> +- <em:id>{3ca105e0-2280-4897-99a0-c277d1b733d2}</em:id> +- <em:minVersion>0.82</em:minVersion> +- <em:maxVersion>0.84</em:maxVersion> +- <em:updateLink>http://foo.xo</em:updateLink> +- <em:updateSize>7</em:updateSize> +- <em:updateHash>sha256:816a7c43b4f1ea4769c61c03ea4..</em:updateHash> +- </RDF:Description> +- </em:targetApplication> +-</RDF:Description></RDF:RDF> +-""" +- +-import logging +-from xml.etree.ElementTree import XML +-import traceback +- +-import gio +- +-from sugar.bundle.bundleversion import NormalizedVersion +-from sugar.bundle.bundleversion import InvalidVersionError +- +-from jarabe import config +- +-_FIND_DESCRIPTION = \ +- './/{http://www.w3.org/1999/02/22-rdf-syntax-ns#}Description' +-_FIND_VERSION = './/{http://www.mozilla.org/2004/em-rdf#}version' +-_FIND_LINK = './/{http://www.mozilla.org/2004/em-rdf#}updateLink' +-_FIND_SIZE = './/{http://www.mozilla.org/2004/em-rdf#}updateSize' +- +-_UPDATE_PATH = 'http://activities.sugarlabs.org/services/update-aslo.php' +- +-_fetcher = None +- +- +-class _UpdateFetcher(object): +- +- _CHUNK_SIZE = 10240 +- +- def __init__(self, bundle, completion_cb): +- # ASLO knows only about stable SP releases +- major, minor = config.version.split('.')[0:2] +- sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2) +- +- url = '%s?id=%s&appVersion=%s' % \ +- (_UPDATE_PATH, bundle.get_bundle_id(), sp_version) +- +- logging.debug('Fetch %s', url) +- +- self._completion_cb = completion_cb +- self._file = gio.File(url) +- self._stream = None +- self._xml_data = '' +- self._bundle = bundle +- +- self._file.read_async(self.__file_read_async_cb) +- +- def __file_read_async_cb(self, gfile, result): +- try: +- self._stream = self._file.read_finish(result) +- except: +- global _fetcher +- _fetcher = None +- self._completion_cb(None, None, None, None, traceback.format_exc()) +- return +- +- self._stream.read_async(self._CHUNK_SIZE, self.__stream_read_async_cb) +- +- def __stream_read_async_cb(self, stream, result): +- xml_data = self._stream.read_finish(result) +- if xml_data is None: +- global _fetcher +- _fetcher = None +- self._completion_cb(self._bundle, None, None, None, +- 'Error reading update information for %s from ' +- 'server.' % self._bundle.get_bundle_id()) +- return +- elif not xml_data: +- self._process_result() +- else: +- self._xml_data += xml_data +- self._stream.read_async(self._CHUNK_SIZE, +- self.__stream_read_async_cb) +- +- def _process_result(self): +- document = XML(self._xml_data) +- +- if document.find(_FIND_DESCRIPTION) is None: +- logging.debug('Bundle %s not available in the server for the ' +- 'version %s', self._bundle.get_bundle_id(), config.version) +- version = None +- link = None +- size = None +- else: +- try: +- version = NormalizedVersion(document.find(_FIND_VERSION).text) +- except InvalidVersionError: +- logging.exception('Exception occured while parsing version') +- version = '0' +- +- link = document.find(_FIND_LINK).text +- +- try: +- size = long(document.find(_FIND_SIZE).text) * 1024 +- except ValueError: +- logging.exception('Exception occured while parsing size') +- size = 0 +- +- global _fetcher +- _fetcher = None +- self._completion_cb(self._bundle, version, link, size, None) +- +- +-def fetch_update_info(bundle, completion_cb): +- """Queries the server for a newer version of the ActivityBundle. +- +- completion_cb receives bundle, version, link, size and possibly an error +- message: +- +- def completion_cb(bundle, version, link, size, error_message): +- """ +- global _fetcher +- +- if _fetcher is not None: +- raise RuntimeError('Multiple simultaneous requests are not supported') +- +- _fetcher = _UpdateFetcher(bundle, completion_cb) +diff --git a/extensions/cpsection/updater/backends/microformat.py b/extensions/cpsection/updater/backends/microformat.py +new file mode 100644 +index 0000000..97499aa +--- /dev/null ++++ b/extensions/cpsection/updater/backends/microformat.py +@@ -0,0 +1,203 @@ ++#!/usr/bin/python ++# ++# Copyright (C) 2011, Anish Mangal <anish@sugarlabs.org> ++# ++# 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 HTMLParser import HTMLParser ++import urllib ++import re ++ ++import gio ++import gobject ++import gconf ++ ++from jarabe import config ++ ++client = gconf.client_get_default() ++_UPDATE_PATH = client.get_string('/desktop/sugar/updater_url') ++_ACTIVITIES_LIST = {} ++ACTION_CHECKING = 0 ++ACTION_UPDATING = 1 ++ACTION_DOWNLOADING = 2 ++ ++class MicroformatParser(HTMLParser): ++ ++ def __init__(self, data, completion_cb): ++ HTMLParser.__init__(self) ++ self.reset() ++ self._data_to_parse = data ++ self._activity_id = '' ++ self._activity_url = '' ++ self._activity_version = '' ++ self._activity_size = 1 ++ self._activity_name = '' ++ self._inside_activity_block = False ++ self._inside_activity_version = False ++ self._inside_activity_id = False ++ self._inside_activity_url = False ++ self._inside_activity_size = False ++ self._inside_activity_name = False ++ self._activity_block_tag = '' ++ self._completion_cb = completion_cb ++ ++ def parse(self): ++ self.feed(self._data_to_parse) ++ ++ def handle_endtag(self, tag): ++ if tag == self._activity_block_tag and self._inside_activity_block: ++ self._inside_activity_block = False ++ ++ _ACTIVITIES_LIST[self._activity_id] = \ ++ {'version':self._activity_version, ++ 'url':self._activity_url, ++ 'size':self._activity_size, ++ 'name':self._activity_name} ++ ++ elif tag == 'a': ++ if self._inside_activity_url: ++ self._inside_activity_url = False ++ ++ elif tag == 'body': ++ num_bundles = len(_ACTIVITIES_LIST) ++ progress = num_bundles ++ for bundle, info in _ACTIVITIES_LIST.items(): ++ progress = progress + 1 ++ if _ACTIVITIES_LIST[bundle]['size'] == 1: ++ try: ++ _ACTIVITIES_LIST[bundle]['size'] = \ ++ gio.File(_ACTIVITIES_LIST[bundle]['url']).\ ++ query_info('*').get_size() ++ ++ except Exception, e: ++ logging.exception(e) ++ ++ if _ACTIVITIES_LIST[bundle]['size'] == 0: ++ logging.error('Size of activity %s reported as ' ++ '0 bytes. Excluding from update list' % bundle) ++ del _ACTIVITIES_LIST[bundle] ++ ++ elif _ACTIVITIES_LIST[bundle]['name'] == '': ++ # Do some regex magic to get the 'probable' ++ # activity name. ++ activity_name = re.split('\.', ++ bundle)[-1] ++ activity_name = re.sub('^[\s|\t]*', '', ++ activity_name) ++ activity_name = re.sub('[\s|\t]*$', '', ++ activity_name) ++ activity_name = re.sub('[A|a]ctivity$', '', ++ activity_name) ++ _ACTIVITIES_LIST[bundle]['name'] = \ ++ activity_name ++ ++ self._completion_cb(_ACTIVITIES_LIST, None) ++ ++ def handle_starttag(self, tag, attrs): ++ for attribute, value in attrs: ++ if value == 'olpc-activity-info': ++ self._inside_activity_block = True ++ self._activity_block_tag = tag ++ ++ if tag == 'span': ++ for attribute, value in attrs: ++ if value == 'olpc-activity-id': ++ self._inside_activity_id = True ++ elif value == 'olpc-activity-version': ++ self._inside_activity_version = True ++ elif value == 'olpc-activity-name': ++ self._inside_activity_name = True ++ elif value == 'olpc-activity-size': ++ self._inside_activity_size = True ++ elif value == 'olpc-activity-url': ++ self._inside_activity_url = True ++ ++ elif tag == 'a': ++ if self._inside_activity_url: ++ for attribute, value in attrs: ++ if attribute == 'href': ++ self._activity_url = value ++ ++ def handle_data(self, data): ++ if self._inside_activity_version: ++ self._activity_version = int(data) ++ self._inside_activity_version = False ++ ++ elif self._inside_activity_id: ++ self._activity_id = data ++ self._inside_activity_id = False ++ ++ elif self._inside_activity_name: ++ self._activity_name = data ++ self._inside_activity_name = False ++ ++ elif self._inside_activity_size: ++ self._activity_size = int(data) ++ self._inside_activity_size = False ++ ++class _UpdateFetcher(gobject.GObject): ++ ++ __gsignals__ = { ++ 'progress': (gobject.SIGNAL_RUN_FIRST, ++ gobject.TYPE_NONE, ++ ([int, str, float, int])), ++ } ++ ++ def __init__(self, completion_cb): ++ gobject.GObject.__init__(self) ++ # ASLO knows only about stable SP releases ++ major, minor = config.version.split('.')[0:2] ++ sp_version = '%s.%s' % (major, int(minor) + int(minor) % 2) ++ self._data = '' ++ self._completion_cb = completion_cb ++ ++ def download_bundle_updates(self): ++ self.emit('progress', ACTION_CHECKING, 'Fetching update ' ++ 'information', 1, 3) ++ self._url = _UPDATE_PATH ++ self._file = gio.File(self._url) ++ logging.debug('Fetch %s', self._url) ++ self._file.read_async(self.__read_async_cb) ++ ++ def __read_async_cb(self, gfile, result): ++ try: ++ stream = gfile.read_finish(result) ++ except gio.Error, e: ++ self.stop() ++ logging.exception('Error while fetching content from %s' % ++ self._url) ++ return ++ stream.read_async(4096, self.__stream_read_cb) ++ ++ def __stream_read_cb(self, stream, result): ++ data = stream.read_finish(result) ++ if not data: ++ self._data_finished() ++ return ++ self._data_read(data) ++ stream.read_async(4096, self.__stream_read_cb) ++ ++ def _data_read(self, data): ++ self._data += data ++ ++ def read_finish(self): ++ pass ++ ++ def _data_finished(self): ++ self.emit('progress', ACTION_CHECKING, 'Fetching update ' ++ 'information', 2, 3) ++ parser = MicroformatParser(self._data, self._completion_cb) ++ gobject.idle_add(parser.parse) +diff --git a/extensions/cpsection/updater/model.py b/extensions/cpsection/updater/model.py +index 7ea445f..d7fd528 100755 +--- a/extensions/cpsection/updater/model.py ++++ b/extensions/cpsection/updater/model.py +@@ -37,8 +37,26 @@ + + from jarabe.model import bundleregistry + +-from backends import aslo ++from backends import microformat + ++class MetaBundle(): ++ ++ def __init__(self, bundle_id, version, name): ++ self._bundle_id = bundle_id ++ self._version = version ++ self._name = name ++ ++ def get_name(self): ++ return self._name ++ ++ def get_bundle_id(self): ++ return self._bundle_id ++ ++ def get_icon(self): ++ pass ++ ++ def get_activity_version(self): ++ return self._version + + class UpdateModel(gobject.GObject): + __gtype_name__ = 'SugarUpdateModel' +@@ -63,45 +81,51 @@ def __init__(self): + self._downloader = None + self._cancelling = False + ++ def __progress_cb(self, model, action, description, current, total): ++ self.emit('progress', action, description, current, total) ++ + def check_updates(self): + self.updates = [] ++ self._current_bundles = {} ++ for bundle in bundleregistry.get_registry(): ++ self._current_bundles[bundle.get_bundle_id()] =\ ++ {'version':bundle.get_activity_version(), ++ 'bundle': bundle} + self._bundles_to_check = list(bundleregistry.get_registry()) +- self._check_next_update() +- +- def _check_next_update(self): +- total = len(bundleregistry.get_registry()) +- current = total - len(self._bundles_to_check) ++ self._fetcher = microformat._UpdateFetcher(self.__bundle_info_fetched_cb) ++ self._fetcher.connect('progress', self.__progress_cb) ++ gobject.idle_add(self._fetcher.download_bundle_updates) + +- if not self._bundles_to_check: +- return False +- +- bundle = self._bundles_to_check.pop() +- self.emit('progress', UpdateModel.ACTION_CHECKING, bundle.get_name(), +- current, total) +- +- aslo.fetch_update_info(bundle, self.__check_completed_cb) +- +- def __check_completed_cb(self, bundle, version, link, size, error_message): ++ def __bundle_info_fetched_cb(self, new_bundles, error_message): + if error_message is not None: + logging.error('Error getting update information from server:\n' + '%s' % error_message) + +- if version is not None and \ +- version > NormalizedVersion(bundle.get_activity_version()): +- self.updates.append(BundleUpdate(bundle, version, link, size)) +- + if self._cancelling: + self._cancel_checking() +- elif self._bundles_to_check: +- gobject.idle_add(self._check_next_update) + else: +- total = len(bundleregistry.get_registry()) +- if bundle is None: +- name = '' +- else: +- name = bundle.get_name() +- self.emit('progress', UpdateModel.ACTION_CHECKING, name, total, +- total) ++ for bundle_id, info in new_bundles.items(): ++ if bundle_id in self._current_bundles: ++ if new_bundles[bundle_id]['version'] >\ ++ self._current_bundles[bundle_id]['version']: ++ self.updates.append(BundleUpdate( ++ self._current_bundles[bundle_id]['bundle'], ++ new_bundles[bundle_id]['version'], ++ new_bundles[bundle_id]['url'], ++ new_bundles[bundle_id]['size'], ++ 'update')) ++ else: ++ bundle = MetaBundle(bundle_id, ++ new_bundles[bundle_id]['version'], ++ new_bundles[bundle_id]['name']) ++ self.updates.append(BundleUpdate(bundle, ++ new_bundles[bundle_id]['version'], ++ new_bundles[bundle_id]['url'], ++ new_bundles[bundle_id]['size'], ++ 'new')) ++ ++ self.emit('progress', UpdateModel.ACTION_CHECKING, 'Fetching update ' ++ 'information', 3, 3) + + def update(self, bundle_ids): + self._bundles_to_update = [] +@@ -226,12 +250,15 @@ def _cancel_updating(self): + + class BundleUpdate(object): + +- def __init__(self, bundle, version, link, size): ++ def __init__(self, bundle, version, link, size, package_type = None): + self.bundle = bundle + self.version = version + self.link = link + self.size = size + ++ # Specify whether installing a new bundle or updating an ++ # existing one ++ self.package_type = package_type + + class _Downloader(gobject.GObject): + _CHUNK_SIZE = 10240 # 10K +diff --git a/extensions/cpsection/updater/view.py b/extensions/cpsection/updater/view.py +index 814658f..30875e4 100644 +--- a/extensions/cpsection/updater/view.py ++++ b/extensions/cpsection/updater/view.py +@@ -122,7 +122,7 @@ def __progress_cb(self, model, action, bundle_name, current, total): + return + + if action == UpdateModel.ACTION_CHECKING: +- message = _('Checking %s...') % bundle_name ++ message = _('%s...') % bundle_name + elif action == UpdateModel.ACTION_DOWNLOADING: + message = _('Downloading %s...') % bundle_name + elif action == UpdateModel.ACTION_UPDATING: +@@ -361,11 +361,17 @@ def __init__(self, model): + row[self.SELECTED] = True + row[self.ICON_FILE_NAME] = bundle_update.bundle.get_icon() + +- details = _('From version %(current)s to %(new)s (Size: %(size)s)') +- details = details % \ +- {'current': bundle_update.bundle.get_activity_version(), +- 'new': bundle_update.version, +- 'size': _format_size(bundle_update.size)} ++ if bundle_update.package_type == 'update': ++ details = _('From version %(current)d to %(new)s (Size: %(size)s)') ++ details = details % \ ++ {'current': bundle_update.bundle.get_activity_version(), ++ 'new': bundle_update.version, ++ 'size': _format_size(bundle_update.size)} ++ elif bundle_update.package_type == 'new': ++ details = _('Install new activity version %(new)s (Size: %(size)s)') ++ details = details % \ ++ {'new': bundle_update.version, ++ 'size': _format_size(bundle_update.size)} + + row[self.DESCRIPTION] = '<b>%s</b>\n%s' % \ + (bundle_update.bundle.get_name(), details) +-- +1.7.6 + |