Add to Bundle facilities for dealing with not-yet-installed bundles.

This commit is contained in:
Tomeu Vizoso 2007-06-15 11:36:08 +02:00
parent a3fb02bc95
commit cb9dd212e9
3 changed files with 168 additions and 78 deletions

View File

@ -1,58 +1,9 @@
a#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
import os
import zipfile
import dbus
from sugar import env from sugar.activity.bundle import Bundle
DBUS_SERVICE = "org.laptop.Shell" bundle = Bundle(sys.argv[1])
DBUS_PATH = "/org/laptop/Shell" bundle.install()
# 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.'
print "%s: '%s' installed." % (sys.argv[0], sys.argv[1]) print "%s: '%s' installed." % (sys.argv[0], sys.argv[1])

View File

@ -20,12 +20,28 @@
import logging import logging
import locale import locale
import os import os
import zipfile
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
import StringIO
import tempfile
import dbus
from sugar import env from sugar import env
from sugar import activity
_PYTHON_FACTORY='sugar-activity-factory' _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: class Bundle:
"""Metadata description of a given application/activity """Metadata description of a given application/activity
@ -39,6 +55,9 @@ class Bundle:
http://wiki.laptop.org/go/Activity_bundles http://wiki.laptop.org/go/Activity_bundles
""" """
def __init__(self, path): def __init__(self, path):
self._init_with_path(path)
def _init_with_path(self, path):
self._name = None self._name = None
self._icon = None self._icon = None
self._service_name = None self._service_name = None
@ -48,19 +67,39 @@ class Bundle:
self._path = path self._path = path
self._activity_version = 0 self._activity_version = 0
info_path = os.path.join(path, 'activity', 'activity.info') info_file = self._get_info_file()
if os.path.isfile(info_path): if info_file:
self._parse_info(info_path) self._parse_info(info_file)
else: else:
self._valid = False self._valid = False
linfo_path = self._get_linfo_path() linfo_file = self._get_linfo_file()
if linfo_path and os.path.isfile(linfo_path): if linfo_file:
self._parse_linfo(linfo_path) 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 = ConfigParser()
cp.read([info_path]) cp.readfp(info_file)
section = 'Activity' section = 'Activity'
@ -101,36 +140,45 @@ class Bundle:
self._show_launcher = False self._show_launcher = False
if cp.has_option(section, 'icon'): if cp.has_option(section, 'icon'):
icon = cp.get(section, 'icon') self._icon = cp.get(section, 'icon')
activity_path = os.path.join(self._path, 'activity')
self._icon = os.path.join(activity_path, icon + ".svg")
if cp.has_option(section, 'activity_version'): if cp.has_option(section, 'activity_version'):
self._activity_version = int(cp.get(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 = ConfigParser()
cp.read([linfo_path]) cp.readfp(linfo_file)
section = 'Activity' section = 'Activity'
if cp.has_option(section, 'name'): if cp.has_option(section, 'name'):
self._name = cp.get(section, 'name') self._name = cp.get(section, 'name')
def _get_linfo_path(self): def _get_linfo_file(self):
path = None linfo_file = None
lang = locale.getdefaultlocale()[0] 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: ext = os.path.splitext(self._path)[1]
return os.path.join(path, 'activity.linfo') 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: 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): def is_valid(self):
return self._valid return self._valid
@ -157,7 +205,25 @@ class Bundle:
def get_icon(self): def get_icon(self):
"""Get the activity icon name""" """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): def get_activity_version(self):
"""Get the activity version""" """Get the activity version"""
@ -178,3 +244,74 @@ class Bundle:
def get_show_launcher(self): def get_show_launcher(self):
"""Get whether there should be a visible launcher for the activity""" """Get whether there should be a visible launcher for the activity"""
return self._show_launcher 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

View File

@ -22,6 +22,8 @@ _SHELL_PATH = "/org/laptop/Shell"
_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry" _REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
def _activity_info_from_dict(info_dict): def _activity_info_from_dict(info_dict):
if not info_dict:
return None
return ActivityInfo(info_dict['name'], info_dict['icon'], return ActivityInfo(info_dict['name'], info_dict['icon'],
info_dict['service_name'], info_dict['path']) info_dict['service_name'], info_dict['path'])