From cb9dd212e989d531621aeaa5b08b9e982fbb4f5d Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 15 Jun 2007 09:36:08 +0000 Subject: Add to Bundle facilities for dealing with not-yet-installed bundles. --- diff --git a/bin/sugar-install-bundle b/bin/sugar-install-bundle index 01a45ae..883dbfb 100755 --- a/bin/sugar-install-bundle +++ b/bin/sugar-install-bundle @@ -1,58 +1,9 @@ -a#!/usr/bin/env python +#!/usr/bin/env python import sys -import os -import zipfile -import dbus -from sugar import env +from sugar.activity.bundle import Bundle -DBUS_SERVICE = "org.laptop.Shell" -DBUS_PATH = "/org/laptop/Shell" - -# We check here that all the files in the .xo are inside one only dir (bundle_root_dir). -def get_bundle_root_dir(file_names): - bundle_root_dir = None - for file_name in file_names: - if not bundle_root_dir: - bundle_root_dir = file_name.split('/')[0] - if not bundle_root_dir.endswith('.activity'): - raise 'Incorrect bundle.' - else: - if not file_name.startswith(bundle_root_dir): - raise 'Incorrect bundle.' - - return bundle_root_dir - -bus = dbus.SessionBus() -proxy_obj = bus.get_object(DBUS_SERVICE, DBUS_PATH) -dbus_service = dbus.Interface(proxy_obj, DBUS_SERVICE) - -bundle_dir = env.get_user_activities_path() -if not os.path.isdir(bundle_dir): - os.mkdir(bundle_dir) - -zip_file = zipfile.ZipFile(sys.argv[1]) -file_names = zip_file.namelist() -bundle_root_dir = get_bundle_root_dir(file_names) -bundle_path = os.path.join(bundle_dir, bundle_root_dir) - -# FIXME: we need to support installing different versions of the same bundle. -if os.path.exists(bundle_path): - raise IOError, 'This bundle is already installed as ' + bundle_path - -if os.spawnlp(os.P_WAIT, 'unzip', 'unzip', sys.argv[1], '-d', bundle_dir): - raise RuntimeError, 'An error occurred while extracting the .xo contents.' - -# notify shell of new bundle -if not dbus_service.AddBundle(bundle_path): - # error, let's delete the just expanded bundle. - for root, dirs, files in os.walk(bundle_path, topdown=False): - for name in files: - os.remove(os.path.join(root, name)) - for name in dirs: - os.rmdir(os.path.join(root, name)) - os.rmdir(bundle_path) - - raise RuntimeError, 'Bundle is not well-formed.' +bundle = Bundle(sys.argv[1]) +bundle.install() print "%s: '%s' installed." % (sys.argv[0], sys.argv[1]) diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index cee64fa..70954f9 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -20,12 +20,28 @@ import logging import locale import os +import zipfile from ConfigParser import ConfigParser +import StringIO +import tempfile + +import dbus from sugar import env +from sugar import activity _PYTHON_FACTORY='sugar-activity-factory' +_DBUS_SHELL_SERVICE = "org.laptop.Shell" +_DBUS_SHELL_PATH = "/org/laptop/Shell" +_DBUS_ACTIVITY_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry" + +class AlreadyInstalledException(Exception): pass +class NotInstalledException(Exception): pass +class InvalidPathException(Exception): pass +class ZipExtractException(Exception): pass +class RegistrationException(Exception): pass + class Bundle: """Metadata description of a given application/activity @@ -39,6 +55,9 @@ class Bundle: http://wiki.laptop.org/go/Activity_bundles """ def __init__(self, path): + self._init_with_path(path) + + def _init_with_path(self, path): self._name = None self._icon = None self._service_name = None @@ -48,19 +67,39 @@ class Bundle: self._path = path self._activity_version = 0 - info_path = os.path.join(path, 'activity', 'activity.info') - if os.path.isfile(info_path): - self._parse_info(info_path) + info_file = self._get_info_file() + if info_file: + self._parse_info(info_file) else: self._valid = False - linfo_path = self._get_linfo_path() - if linfo_path and os.path.isfile(linfo_path): - self._parse_linfo(linfo_path) + linfo_file = self._get_linfo_file() + if linfo_file: + self._parse_linfo(linfo_file) - def _parse_info(self, info_path): + def _get_info_file(self): + info_file = None + + ext = os.path.splitext(self._path)[1] + if ext == '.activity': + info_path = os.path.join(self._path, 'activity', 'activity.info') + if os.path.isfile(info_path): + info_file = open(info_path) + else: + zip_file = zipfile.ZipFile(self._path) + file_names = zip_file.namelist() + root_dir = self._get_bundle_root_dir(file_names) + info_path = os.path.join(root_dir, 'activity', 'activity.info') + if info_path in file_names: + info_data = zip_file.read(info_path) + info_file = StringIO.StringIO(info_data) + zip_file.close() + + return info_file + + def _parse_info(self, info_file): cp = ConfigParser() - cp.read([info_path]) + cp.readfp(info_file) section = 'Activity' @@ -101,36 +140,45 @@ class Bundle: self._show_launcher = False if cp.has_option(section, 'icon'): - icon = cp.get(section, 'icon') - activity_path = os.path.join(self._path, 'activity') - self._icon = os.path.join(activity_path, icon + ".svg") + self._icon = cp.get(section, 'icon') if cp.has_option(section, 'activity_version'): self._activity_version = int(cp.get(section, 'activity_version')) - def _parse_linfo(self, linfo_path): + def _parse_linfo(self, linfo_file): cp = ConfigParser() - cp.read([linfo_path]) + cp.readfp(linfo_file) section = 'Activity' if cp.has_option(section, 'name'): self._name = cp.get(section, 'name') - def _get_linfo_path(self): - path = None + def _get_linfo_file(self): + linfo_file = None lang = locale.getdefaultlocale()[0] - if lang != None: - path = os.path.join(self.get_locale_path(), lang) - if not os.path.isdir(path): - path = os.path.join(self._path, 'locale', lang[:2]) - if not os.path.isdir(path): - path = None - - if path: - return os.path.join(path, 'activity.linfo') + + ext = os.path.splitext(self._path)[1] + if ext == '.activity': + linfo_path = os.path.join(self.get_locale_path(), lang, 'activity.linfo') + if not os.path.isfile(linfo_path): + linfo_path = os.path.join(self.get_locale_path(), lang[:2], 'activity.linfo') + if os.path.isfile(linfo_path): + linfo_file = open(linfo_path) else: - return None + zip_file = zipfile.ZipFile(self._path) + file_names = zip_file.namelist() + root_dir = self._get_bundle_root_dir(file_names) + linfo_path = os.path.join(root_dir, 'locale', lang, 'activity.linfo') + if not linfo_path in file_names: + linfo_path = os.path.join(root_dir, 'locale', lang[:2], 'activity.linfo') + if linfo_path in zip_file.namelist(): + linfo_data = zip_file.read(linfo_path) + linfo_file = StringIO.StringIO(linfo_data) + + zip_file.close() + + return linfo_file def is_valid(self): return self._valid @@ -157,7 +205,25 @@ class Bundle: def get_icon(self): """Get the activity icon name""" - return self._icon + ext = os.path.splitext(self._path)[1] + if ext == '.activity': + activity_path = os.path.join(self._path, 'activity') + return os.path.join(activity_path, self._icon + '.svg') + else: + zip_file = zipfile.ZipFile(self._path) + file_names = zip_file.namelist() + root_dir = self._get_bundle_root_dir(file_names) + icon_path = os.path.join(root_dir, 'activity', self._icon + '.svg') + print icon_path + print file_names + if icon_path in file_names: + icon_data = zip_file.read(icon_path) + temp_file, temp_file_path = tempfile.mkstemp(self._icon) + os.write(temp_file, icon_data) + os.close(temp_file) + return temp_file_path + else: + return None def get_activity_version(self): """Get the activity version""" @@ -178,3 +244,74 @@ class Bundle: def get_show_launcher(self): """Get whether there should be a visible launcher for the activity""" return self._show_launcher + + def is_installed(self): + if self._valid and activity.get_registry().get_activity(self._service_name): + return True + else: + return False + + def _get_bundle_root_dir(self, file_names): + """ + We check here that all the files in the .xo are inside one only dir + (bundle_root_dir). + """ + bundle_root_dir = None + for file_name in file_names: + if not bundle_root_dir: + bundle_root_dir = file_name.split('/')[0] + if not bundle_root_dir.endswith('.activity'): + raise 'Incorrect bundle.' + else: + if not file_name.startswith(bundle_root_dir): + raise 'Incorrect bundle.' + + return bundle_root_dir + + def install(self): + if self.is_installed(): + raise AlreadyInstalledException + + ext = os.path.splitext(self._path)[1] + if not os.path.isfile(self._path): + raise InvalidPathException + + bundle_dir = env.get_user_activities_path() + if not os.path.isdir(bundle_dir): + os.mkdir(bundle_dir) + + zip_file = zipfile.ZipFile(self._path) + file_names = zip_file.namelist() + bundle_root_dir = self._get_bundle_root_dir(file_names) + bundle_path = os.path.join(bundle_dir, bundle_root_dir) + + if os.spawnlp(os.P_WAIT, 'unzip', 'unzip', self._path, '-d', bundle_dir): + raise ZipExtractException + + self._init_with_path(bundle_path) + + bus = dbus.SessionBus() + proxy_obj = bus.get_object(_DBUS_SHELL_SERVICE, _DBUS_SHELL_PATH) + dbus_service = dbus.Interface(proxy_obj, _DBUS_ACTIVITY_REGISTRY_IFACE) + if not dbus_service.AddBundle(bundle_path): + raise RegistrationException + + def deinstall(self): + if not self.is_installed(): + raise NotInstalledException + + ext = os.path.splitext(self._path)[1] + if not os.path.isfile(self._path) or ext != '.activity': + raise InvalidPathException + + for root, dirs, files in os.walk(self._path, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + for name in dirs: + os.rmdir(os.path.join(root, name)) + os.rmdir(self._path) + + self._init_with_path(None) + + # TODO: notify shell + diff --git a/sugar/activity/registry.py b/sugar/activity/registry.py index 79d6403..255185b 100644 --- a/sugar/activity/registry.py +++ b/sugar/activity/registry.py @@ -22,6 +22,8 @@ _SHELL_PATH = "/org/laptop/Shell" _REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry" def _activity_info_from_dict(info_dict): + if not info_dict: + return None return ActivityInfo(info_dict['name'], info_dict['icon'], info_dict['service_name'], info_dict['path']) -- cgit v0.9.1