From f377d6e5197f07b82c3ccd29e2899e02f03b3d8e Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 20 Sep 2007 16:20:21 +0000 Subject: Redo activity bundles in terms of sugar.bundle.bundle --- (limited to 'sugar/bundle/activitybundle.py') diff --git a/sugar/bundle/activitybundle.py b/sugar/bundle/activitybundle.py new file mode 100644 index 0000000..07a5255 --- /dev/null +++ b/sugar/bundle/activitybundle.py @@ -0,0 +1,248 @@ +# Copyright (C) 2007, Red Hat, Inc. +# +# 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. + +"""Sugar activity bundles""" + +from ConfigParser import ConfigParser +import locale +import os +import tempfile + +from sugar.bundle.bundle import Bundle, MalformedBundleException +from sugar import activity +from sugar import env + +_PYTHON_FACTORY='sugar-activity-factory' + +class ActivityBundle(Bundle): + """A Sugar activity bundle + + See http://wiki.laptop.org/go/Activity_bundles for details + """ + + MIME_TYPE = 'application/vnd.olpc-sugar' + DEPRECATED_MIME_TYPE = 'application/vnd.olpc-x-sugar' + + _zipped_extension = '.xo' + _unzipped_extension = '.activity' + _infodir = 'activity' + + def __init__(self, path): + Bundle.__init__(self, path) + self.activity_class = None + self.bundle_exec = None + + self._name = None + self._icon = None + self._service_name = None + self._mime_types = None + self._show_launcher = True + self._activity_version = 0 + + info_file = self._get_file('activity/activity.info') + if info_file is None: + raise MalformedBundleException('No activity.info file') + self._parse_info(info_file) + + linfo_file = self._get_linfo_file() + if linfo_file: + self._parse_linfo(linfo_file) + + def _parse_info(self, info_file): + cp = ConfigParser() + cp.readfp(info_file) + + section = 'Activity' + + if cp.has_option(section, 'service_name'): + self._service_name = cp.get(section, 'service_name') + else: + raise MalformedBundleException( + 'Activity bundle %s does not specify a service name' % + self._path) + + if cp.has_option(section, 'name'): + self._name = cp.get(section, 'name') + else: + raise MalformedBundleException( + 'Activity bundle %s does not specify a name' % self._path) + + if cp.has_option(section, 'class'): + self.activity_class = cp.get(section, 'class') + elif cp.has_option(section, 'exec'): + self.bundle_exec = cp.get(section, 'exec') + else: + raise MalformedBundleException( + 'Activity bundle %s must specify either class or exec' % + self._path) + + if cp.has_option(section, 'mime_types'): + mime_list = cp.get(section, 'mime_types') + self._mime_types = mime_list.strip(';').split(';') + + if cp.has_option(section, 'show_launcher'): + if cp.get(section, 'show_launcher') == 'no': + self._show_launcher = False + + if cp.has_option(section, 'icon'): + self._icon = cp.get(section, 'icon') + + if cp.has_option(section, 'activity_version'): + version = cp.get(section, 'activity_version') + try: + self._activity_version = int(version) + except ValueError: + raise MalformedBundleException( + 'Activity bundle %s has invalid version number %s' % + (self._path, version)) + + def _get_linfo_file(self): + lang = locale.getdefaultlocale()[0] + if not lang: + return None + + linfo_path = os.path.join('locale', lang, 'activity.linfo') + linfo_file = self._get_file(linfo_path) + if linfo_file is not None: + return linfo_file + + linfo_path = os.path.join('locale', lang[:2], 'activity.linfo') + linfo_file = self._get_file(linfo_path) + if linfo_file is not None: + return linfo_file + + return None + + def _parse_linfo(self, linfo_file): + cp = ConfigParser() + cp.readfp(linfo_file) + + section = 'Activity' + + if cp.has_option(section, 'name'): + self._name = cp.get(section, 'name') + + def get_locale_path(self): + """Get the locale path inside the (installed) activity bundle.""" + if not self._unpacked: + raise NotInstalledException + return os.path.join(self._path, 'locale') + + def get_icons_path(self): + """Get the icons path inside the (installed) activity bundle.""" + if not self._unpacked: + raise NotInstalledException + return os.path.join(self._path, 'icons') + + def get_path(self): + """Get the activity bundle path.""" + return self._path + + def get_name(self): + """Get the activity user visible name.""" + return self._name + + def get_service_name(self): + """Get the activity service name""" + return self._service_name + + # FIXME: this should return the icon data, not a filename, so that + # we don't need to create a temp file in the zip case + def get_icon(self): + """Get the activity icon name""" + icon_path = os.path.join('activity', self._icon + '.svg') + if self._unpacked: + return os.path.join(self._path, icon_path) + else: + icon_data = self._get_file(icon_path).read() + temp_file, temp_file_path = tempfile.mkstemp(self._icon) + os.write(temp_file, icon_data) + os.close(temp_file) + return temp_file_path + + def get_activity_version(self): + """Get the activity version""" + return self._activity_version + + def get_command(self): + """Get the command to execute to launch the activity factory""" + if self.bundle_exec: + command = os.path.join(self._path, self.bundle_exec) + command = command.replace('$SUGAR_BUNDLE_PATH', self._path) + command = os.path.expandvars(command) + else: + command = '%s --bundle-path="%s"' % ( + env.get_bin_path(_PYTHON_FACTORY), self._path) + + return command + + + def get_mime_types(self): + """Get the MIME types supported by the activity""" + return self._mime_types + + 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 activity.get_registry().get_activity(self._service_name): + return True + else: + return False + + def install(self): + if self.is_installed(): + raise AlreadyInstalledException + + install_dir = env.get_user_activities_path() + self._unzip(install_dir) + + install_path = os.path.join(install_dir, self._zip_root_dir) + if not activity.get_registry().add_bundle(install_path): + raise RegistrationException + + xdg_data_home = os.getenv('XDG_DATA_HOME', os.path.expanduser('~/.local/share')) + + mime_path = os.path.join(install_path, 'activity', 'mimetypes.xml') + if os.path.isfile(mime_path): + mime_dir = os.path.join(xdg_data_home, 'mime') + mime_pkg_dir = os.path.join(mime_dir, 'packages') + if not os.path.isdir(mime_pkg_dir): + os.makedirs(mime_pkg_dir) + installed_mime_path = os.path.join(mime_pkg_dir, '%s.xml' % self._service_name) + os.symlink(mime_path, installed_mime_path) + os.spawnlp(os.P_WAIT, 'update-mime-database', + 'update-mime-database', mime_dir) + + icons_dir = os.path.join(install_path, 'activity', 'mime-icons') + if os.path.isdir(icons_dir): + installed_icons_dir = os.path.join(xdg_data_home, 'icons/sugar/scalable/mimetypes') + if not os.path.isdir(installed_icons_dir): + os.makedirs(installed_icons_dir) + for file in os.listdir(icons_dir): + if file.endswith('.svg') or file.endswith('.icon'): + os.symlink(os.path.join(icons_dir, file), + os.path.join(installed_icons_dir, file)) + + def uninstall(self): + if not self.is_installed(): + raise NotInstalledException + + self._uninstall() + # FIXME: notify shell + -- cgit v0.9.1