diff options
Diffstat (limited to 'src/jarabe/view/customizebundle.py')
-rw-r--r-- | src/jarabe/view/customizebundle.py | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/src/jarabe/view/customizebundle.py b/src/jarabe/view/customizebundle.py new file mode 100644 index 0000000..42da1b3 --- /dev/null +++ b/src/jarabe/view/customizebundle.py @@ -0,0 +1,217 @@ +# Copyright (C) 2011 Walter Bender +# +# 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 os +import glob +import hashlib + +import gtk + +from sugar import profile +from sugar.activity import bundlebuilder +from sugar.datastore import datastore +from sugar.env import get_user_activities_path + +import logging +_logger = logging.getLogger('ViewSource') + + +BADGE_SUBPATH = 'emblems/emblem-view-source.svg' +BADGE_TRANSFORM = ' <g transform="matrix(0.45,0,0,0.45,32,32)">\n' +ICON_TRANSFORM = ' <g transform="matrix(1.0,0,0,1.0,0,0)">\n' +XML_HEADER = '<?xml version="1.0" ?> \ +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" \ +"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\n\ +<!ENTITY stroke_color "#010101">\n\ +<!ENTITY fill_color "#FFFFFF">\n]>\n' +SVG_START = '<svg enable-background="new 0 0 55 55" height="55px" \ +version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" \ +xmlns="http://www.w3.org/2000/svg" \ +xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n' +SVG_END = '</svg>\n' + + +def generate_unique_id(): + """Generate an id based on the user's nick name and their public key + (Based on schema used by IRC activity). + + """ + nick = profile.get_nick_name() + pubkey = profile.get_pubkey() + m = hashlib.sha1() + m.update(pubkey) + hexhash = m.hexdigest() + + nick_letters = "".join([x for x in nick if x.isalpha()]) + + if not nick_letters: + nick_letters = 'XO' + + return nick_letters + '_' + hexhash[:4] + + +def generate_bundle(nick, new_basename): + """Generate a new .xo bundle for the activity and copy it into the + Journal. + + """ + new_activity_name = _customize_activity_info( + nick, new_basename) + + user_activities_path = get_user_activities_path() + if os.path.exists(os.path.join(user_activities_path, new_basename, + 'dist')): + for path in glob.glob(os.path.join(user_activities_path, new_basename, + 'dist', '*')): + os.remove(path) + + config = bundlebuilder.Config(source_dir=os.path.join( + user_activities_path, new_basename), + dist_name='%s-1.xo' % (new_activity_name)) + bundlebuilder.cmd_dist_xo(config, None) + + dsobject = datastore.create() + dsobject.metadata['title'] = '%s-1.xo' % (new_activity_name) + dsobject.metadata['mime_type'] = 'application/vnd.olpc-sugar' + dsobject.set_file_path(os.path.join( + user_activities_path, new_basename, 'dist', + '%s-1.xo' % (new_activity_name))) + datastore.write(dsobject) + dsobject.destroy() + + +def _customize_activity_info(nick, new_basename): + """Modify bundle_id in new activity.info file: + (1) change the bundle_id to bundle_id_[NICKNAME]; + (2) change the activity_icon [NICKNAME]-activity-icon.svg; + (3) set activity_version to 1; + (4) modify the activity icon by applying a customize overlay. + + """ + new_activity_name = '' + user_activities_path = get_user_activities_path() + + info_old = open(os.path.join(user_activities_path, new_basename, + 'activity', 'activity.info'), 'r') + info_new = open(os.path.join(user_activities_path, new_basename, + 'activity', 'new_activity.info'), 'w') + + for line in info_old: + if line.find('=') < 0: + info_new.write(line) + continue + name, value = [token.strip() for token in line.split('=', 1)] + if name == 'bundle_id': + new_value = '%s_%s' % (value, nick) + elif name == 'activity_version': + new_value = '1' + elif name == 'icon': + new_value = value + icon_name = value + elif name == 'name': + new_value = '%s_copy_of_%s' % (nick, value) + new_activity_name = new_value + else: + info_new.write(line) + continue + + info_new.write('%s = %s\n' % (name, new_value)) + + info_old.close() + info_new.close() + + os.rename(os.path.join(user_activities_path, new_basename, + 'activity', 'new_activity.info'), + os.path.join(user_activities_path, new_basename, + 'activity', 'activity.info')) + + _create_custom_icon(new_basename, icon_name) + + return new_activity_name + + +def _create_custom_icon(new_basename, icon_name): + """Modify activity icon by overlaying a badge: + (1) Extract the payload from the badge icon; + (2) Add a transform to resize it and position it; + (3) Insert it into the activity icon. + + """ + user_activities_path = get_user_activities_path() + badge_path = None + for path in gtk.icon_theme_get_default().get_search_path(): + if os.path.exists(os.path.join(path, 'sugar', 'scalable', + BADGE_SUBPATH)): + badge_path = path + break + + if badge_path is None: + _logger.debug('%s not found', BADGE_SUBPATH) + return + + badge_fd = open(os.path.join(badge_path, 'sugar', 'scalable', + BADGE_SUBPATH), 'r') + badge_payload = _extract_svg_payload(badge_fd) + badge_fd.close() + + badge_svg = BADGE_TRANSFORM + badge_payload + '\n</g>' + + icon_path = os.path.join(user_activities_path, new_basename, 'activity', + icon_name + '.svg') + icon_fd = open(icon_path, 'r') + icon_payload = _extract_svg_payload(icon_fd) + icon_fd.close() + + icon_svg = ICON_TRANSFORM + icon_payload + '\n</g>' + + tmp_path = os.path.join(user_activities_path, new_basename, 'activity', + 'tmp.svg') + tmp_icon_fd = open(tmp_path, 'w') + tmp_icon_fd.write(XML_HEADER) + tmp_icon_fd.write(SVG_START) + tmp_icon_fd.write(icon_svg) + tmp_icon_fd.write(badge_svg) + tmp_icon_fd.write(SVG_END) + tmp_icon_fd.close() + + os.remove(icon_path) + os.rename(tmp_path, icon_path) + + +def _extract_svg_payload(fd): + """Returns everything between <svg ...> and </svg>""" + payload = '' + looking_for_start_svg_token = True + looking_for_close_token = True + looking_for_end_svg_token = True + for line in fd: + if looking_for_start_svg_token: + if line.find('<svg') < 0: + continue + looking_for_start_svg_token = False + line = line.split('<svg', 1)[1] + if looking_for_close_token: + if line.find('>') < 0: + continue + looking_for_close_token = False + line = line.split('>', 1)[1] + if looking_for_end_svg_token: + if line.find('</svg>') < 0: + payload += line + continue + payload += line.split('</svg>')[0] + break + return payload |