Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch
diff options
context:
space:
mode:
Diffstat (limited to 'rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch')
-rw-r--r--rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch700
1 files changed, 700 insertions, 0 deletions
diff --git a/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch b/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch
new file mode 100644
index 0000000..4b1594a
--- /dev/null
+++ b/rpms/sugar/0019-Journal-Volumes-Backup-and-Restore.patch
@@ -0,0 +1,700 @@
+From a0a18343e1823367813de3ec3cea579202ae5ea4 Mon Sep 17 00:00:00 2001
+From: Martin Abente <mabente@paraguayeduca.org>
+Date: Tue, 22 Jun 2010 15:59:13 -0400
+Subject: [PATCH sugar 19/74] Journal Volumes Backup and Restore
+
+Add a basic backup and restore feature for the Sugar Journal.
+It provides:
+
+- Generic Backup and Restore dialog GUI.
+- Process manager class as an abstraction layer between the dialog and
+ backup/restore scripts. (Allowing to work with many backup and restore
+ technologies, using the same GUI, with no need for script rewrite).
+- Basic file system Volume Restore and Backup scripts implemented in Python.
+- New backup and restore options for journal volumes palettes.
+
+This patch is based on Esteban Arias (Plan Ceibal) Volume Backup and Restore
+patch, with a few changes:
+
+- Refactor original Backup dialog class into a generic dialog class.
+- Create specialized VolumeBackupDialog and VolumeRestoreDialog subclasses.
+- Rewrite backup and restore scripts in python for an easier sugar interaction.
+- Add backup identification helpers to jarabe.journal.misc.
+---
+ bin/Makefile.am | 4 +-
+ bin/journal-backup-volume | 57 ++++++++
+ bin/journal-restore-volume | 67 +++++++++
+ src/jarabe/journal/Makefile.am | 3 +-
+ src/jarabe/journal/misc.py | 27 ++++
+ src/jarabe/journal/processdialog.py | 248 +++++++++++++++++++++++++++++++++
+ src/jarabe/journal/volumestoolbar.py | 4 +-
+ src/jarabe/model/Makefile.am | 1 +
+ src/jarabe/model/processmanagement.py | 98 +++++++++++++
+ src/jarabe/view/palettes.py | 44 ++++++
+ 10 files changed, 549 insertions(+), 4 deletions(-)
+ create mode 100644 bin/journal-backup-volume
+ create mode 100644 bin/journal-restore-volume
+ create mode 100644 src/jarabe/journal/processdialog.py
+ create mode 100644 src/jarabe/model/processmanagement.py
+
+diff --git a/bin/Makefile.am b/bin/Makefile.am
+index 05a9215..65aab9b 100644
+--- a/bin/Makefile.am
++++ b/bin/Makefile.am
+@@ -5,7 +5,9 @@ python_scripts = \
+ sugar-install-bundle \
+ sugar-launch \
+ sugar-session \
+- sugar-ui-check
++ sugar-ui-check \
++ journal-backup-volume \
++ journal-restore-volume
+
+ bin_SCRIPTS = \
+ sugar \
+diff --git a/bin/journal-backup-volume b/bin/journal-backup-volume
+new file mode 100644
+index 0000000..9246760
+--- /dev/null
++++ b/bin/journal-backup-volume
+@@ -0,0 +1,57 @@
++#!/usr/bin/env python
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.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 3 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, see <http://www.gnu.org/licenses/>.
++#
++
++import os
++import sys
++import subprocess
++import logging
++
++from sugar import env
++#from sugar.datastore import datastore
++
++backup_identifier = sys.argv[2]
++volume_path = sys.argv[1]
++
++if len(sys.argv) != 3:
++ print 'Usage: %s <volume_path> <backup_identifier>' % sys.argv[0]
++ exit(1)
++
++logging.debug('Backup started')
++
++backup_path = os.path.join(volume_path, 'backup', backup_identifier)
++
++if not os.path.exists(backup_path):
++ os.makedirs(backup_path)
++
++#datastore.freeze()
++#subprocess.call(['pkill', '-9', '-f', 'python.*datastore-service'])
++
++result = 0
++try:
++ cmd = ['tar', '-C', env.get_profile_path(), '-czf', \
++ os.path.join(backup_path, 'datastore.tar.gz'), 'datastore']
++
++ subprocess.check_call(cmd)
++
++except Exception, e:
++ logging.error('Backup failed: %s', str(e))
++ result = 1
++
++#datastore.thaw()
++
++logging.debug('Backup finished')
++exit(result)
+diff --git a/bin/journal-restore-volume b/bin/journal-restore-volume
+new file mode 100644
+index 0000000..aa14ad0
+--- /dev/null
++++ b/bin/journal-restore-volume
+@@ -0,0 +1,67 @@
++#!/usr/bin/env python
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.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 3 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, see <http://www.gnu.org/licenses/>.
++#
++
++import os
++import sys
++import shutil
++import logging
++import subprocess
++
++from sugar import env
++#from sugar.datastore import datastore
++
++backup_identifier = sys.argv[2]
++volume_path = sys.argv[1]
++
++if len(sys.argv) != 3:
++ print 'Usage: %s <volume_path> <backup_identifier>' % sys.argv[0]
++ exit(1)
++
++logging.debug('Restore started')
++
++journal_path = os.path.join(env.get_profile_path(), 'datastore')
++backup_path = os.path.join(volume_path, 'backup', backup_identifier, 'datastore.tar.gz')
++
++if not os.path.exists(backup_path):
++ logging.error('Could not find backup file %s', backup_path)
++ exit(1)
++
++#datastore.freeze()
++subprocess.call(['pkill', '-9', '-f', 'python.*datastore-service'])
++
++result = 0
++try:
++ if os.path.exists(journal_path):
++ shutil.rmtree(journal_path)
++
++ subprocess.check_call(['tar', '-C', env.get_profile_path(), '-xzf', backup_path])
++
++except Exception, e:
++ logging.error('Restore failed: %s', str(e))
++ result = 1
++
++try:
++ shutil.rmtree(os.path.join(journal_path, 'index'))
++ os.remove(os.path.join(journal_path, 'index_updated'))
++ os.remove(os.path.join(journal_path, 'version'))
++except:
++ logging.debug('Restore has no index files')
++
++#datastore.thaw()
++
++logging.debug('Restore finished')
++exit(result)
+diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am
+index ba29062..f24dcfe 100644
+--- a/src/jarabe/journal/Makefile.am
++++ b/src/jarabe/journal/Makefile.am
+@@ -15,4 +15,5 @@ sugar_PYTHON = \
+ model.py \
+ objectchooser.py \
+ palettes.py \
+- volumestoolbar.py
++ volumestoolbar.py \
++ processdialog.py
+diff --git a/src/jarabe/journal/misc.py b/src/jarabe/journal/misc.py
+index 1431d5f..fac56ad 100644
+--- a/src/jarabe/journal/misc.py
++++ b/src/jarabe/journal/misc.py
+@@ -1,4 +1,5 @@
+ # Copyright (C) 2007, One Laptop Per Child
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.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
+@@ -313,3 +314,29 @@ def get_icon_color(metadata):
+ return XoColor(client.get_string('/desktop/sugar/user/color'))
+ else:
+ return XoColor(metadata['icon-color'])
++
++def get_backup_identifier():
++ serial_number = get_xo_serial()
++ if serial_number is None:
++ serial_number = get_nick()
++ return serial_number
++
++def get_xo_serial():
++ path = '/ofw/serial-number'
++
++ if os.access(path, os.R_OK) == 0:
++ return None
++
++ file_descriptor = open(path, 'r')
++ content = file_descriptor.read()
++ file_descriptor.close()
++
++ if content:
++ return content.strip()
++ else:
++ logging.error('No serial number at %s', path)
++ return None
++
++def get_nick():
++ client = gconf.client_get_default()
++ return client.get_string("/desktop/sugar/user/nick")
+diff --git a/src/jarabe/journal/processdialog.py b/src/jarabe/journal/processdialog.py
+new file mode 100644
+index 0000000..b96abd9
+--- /dev/null
++++ b/src/jarabe/journal/processdialog.py
+@@ -0,0 +1,248 @@
++#!/usr/bin/env python
++# Copyright (C) 2010, Plan Ceibal <comunidad@plan.ceibal.edu.uy>
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.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 3 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, see <http://www.gnu.org/licenses/>.
++
++import gtk
++import gobject
++import gconf
++import logging
++
++from gettext import gettext as _
++from sugar.graphics import style
++from sugar.graphics.icon import Icon
++from sugar.graphics.xocolor import XoColor
++
++from jarabe.journal import misc
++from jarabe.model import shell
++from jarabe.model import processmanagement
++from jarabe.model.session import get_session_manager
++
++class ProcessDialog(gtk.Window):
++
++ __gtype_name__ = 'SugarProcessDialog'
++
++ def __init__(self, process_script='', process_params=[], restart_after=True):
++
++ #FIXME: Workaround limitations of Sugar core modal handling
++ shell_model = shell.get_model()
++ shell_model.set_zoom_level(shell_model.ZOOM_HOME)
++
++ gtk.Window.__init__(self)
++
++ self._process_script = processmanagement.find_and_absolutize(process_script)
++ self._process_params = process_params
++ self._restart_after = restart_after
++ self._start_message = _('Running')
++ self._failed_message = _('Failed')
++ self._finished_message = _('Finished')
++
++ self.set_border_width(style.LINE_WIDTH)
++ width = gtk.gdk.screen_width()
++ height = gtk.gdk.screen_height()
++ self.set_size_request(width, height)
++ self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
++ self.set_decorated(False)
++ self.set_resizable(False)
++ self.set_modal(True)
++
++ self._colored_box = gtk.EventBox()
++ self._colored_box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("white"))
++ self._colored_box.show()
++
++ self._vbox = gtk.VBox()
++ self._vbox.set_spacing(style.DEFAULT_SPACING)
++ self._vbox.set_border_width(style.GRID_CELL_SIZE)
++
++ self._colored_box.add(self._vbox)
++ self.add(self._colored_box)
++
++ self._setup_information()
++ self._setup_progress_bar()
++ self._setup_options()
++
++ self._vbox.show()
++
++ self.connect("realize", self.__realize_cb)
++
++ self._process_management = processmanagement.ProcessManagement()
++ self._process_management.connect('process-management-running', self._set_status_updated)
++ self._process_management.connect('process-management-started', self._set_status_started)
++ self._process_management.connect('process-management-finished', self._set_status_finished)
++ self._process_management.connect('process-management-failed', self._set_status_failed)
++
++ def _setup_information(self):
++ client = gconf.client_get_default()
++ color = XoColor(client.get_string('/desktop/sugar/user/color'))
++
++ self._icon = Icon(icon_name='activity-journal', pixel_size=style.XLARGE_ICON_SIZE, xo_color=color)
++ self._icon.show()
++
++ self._vbox.pack_start(self._icon, False)
++
++ self._title = gtk.Label()
++ self._title.modify_fg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color())
++ self._title.set_use_markup(True)
++ self._title.set_justify(gtk.JUSTIFY_CENTER)
++ self._title.show()
++
++ self._vbox.pack_start(self._title, False)
++
++ self._message = gtk.Label()
++ self._message.modify_fg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color())
++ self._message.set_use_markup(True)
++ self._message.set_line_wrap(True)
++ self._message.set_justify(gtk.JUSTIFY_CENTER)
++ self._message.show()
++
++ self._vbox.pack_start(self._message, True)
++
++ def _setup_options(self):
++ hbox = gtk.HBox(True, 3)
++ hbox.show()
++
++ icon = Icon(icon_name='dialog-ok')
++
++ self._start_button = gtk.Button()
++ self._start_button.set_image(icon)
++ self._start_button.set_label(_('Start'))
++ self._start_button.connect('clicked', self.__start_cb)
++ self._start_button.show()
++
++ icon = Icon(icon_name='dialog-cancel')
++
++ self._close_button = gtk.Button()
++ self._close_button.set_image(icon)
++ self._close_button.set_label(_('Cancel'))
++ self._close_button.connect('clicked', self.__close_cb)
++ self._close_button.show()
++
++ icon = Icon(icon_name='system-restart')
++
++ self._restart_button = gtk.Button()
++ self._restart_button.set_image(icon)
++ self._restart_button.set_label(_('Restart'))
++ self._restart_button.connect('clicked', self.__restart_cb)
++ self._restart_button.hide()
++
++ hbox.add(self._start_button)
++ hbox.add(self._close_button)
++ hbox.add(self._restart_button)
++
++ halign = gtk.Alignment(1, 0, 0, 0)
++ halign.show()
++ halign.add(hbox)
++
++ self._vbox.pack_start(halign, False, False, 3)
++
++ def _setup_progress_bar(self):
++ alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5)
++ alignment.show()
++
++ self._progress_bar = gtk.ProgressBar(adjustment=None)
++ self._progress_bar.hide()
++
++ alignment.add(self._progress_bar)
++ self._vbox.pack_start(alignment)
++
++ def __realize_cb(self, widget):
++ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
++ self.window.set_accept_focus(True)
++
++ def __close_cb(self, button):
++ self.destroy()
++
++ def __start_cb(self, button):
++ self._process_management.do_process([self._process_script] + self._process_params)
++
++ def __restart_cb(self, button):
++ session_manager = get_session_manager()
++ session_manager.logout()
++
++ def _set_status_started(self, model, data=None):
++ self._message.set_markup(self._start_message)
++
++ self._start_button.hide()
++ self._close_button.hide()
++
++ self._progress_bar.set_fraction(0.05)
++ self._progress_bar_handler = gobject.timeout_add(1000, self.__progress_bar_handler_cb)
++ self._progress_bar.show()
++
++ def __progress_bar_handler_cb(self):
++ self._progress_bar.pulse()
++ return True
++
++ def _set_status_updated(self, model, data):
++ pass
++
++ def _set_status_finished(self, model, data=None):
++ self._message.set_markup(self._finished_message)
++
++ self._progress_bar.hide()
++ self._start_button.hide()
++
++ if self._restart_after:
++ self._restart_button.show()
++ else:
++ self._close_button.show()
++
++ def _set_status_failed(self, model, error_message=''):
++ self._message.set_markup('%s %s' % (self._failed_message, error_message))
++
++ self._progress_bar.hide()
++ self._start_button.show()
++ self._close_button.show()
++
++ logging.error(error_message)
++
++
++class VolumeBackupDialog(ProcessDialog):
++
++ def __init__(self, volume_path):
++ ProcessDialog.__init__(self, 'journal-backup-volume', \
++ [volume_path, misc.get_backup_identifier()], restart_after=False)
++
++ self._resetup_information(volume_path)
++
++ def _resetup_information(self, volume_path):
++ self._start_message = '%s %s. \n\n' % (_('Please wait, saving Journal content to'), volume_path) + \
++ '<big><b>%s</b></big>' % _('Do not remove the storage device!')
++
++ self._finished_message = _('The Journal content has been saved.')
++
++ self._title.set_markup('<big><b>%s</b></big>' % _('Backup'))
++
++ self._message.set_markup('%s %s' % (_('Journal content will be saved to'), volume_path))
++
++class VolumeRestoreDialog(ProcessDialog):
++
++ def __init__(self, volume_path):
++ ProcessDialog.__init__(self, 'journal-restore-volume', \
++ [volume_path, misc.get_backup_identifier()])
++
++ self._resetup_information(volume_path)
++
++ def _resetup_information(self, volume_path):
++ self._start_message = '%s %s. \n\n' % (_('Please wait, restoring Journal content from'), volume_path) + \
++ '<big><b>%s</b></big>' % _('Do not remove the storage device!')
++
++ self._finished_message = _('The Journal content has been restored.')
++
++ self._title.set_markup('<big><b>%s</b></big>' % _('Restore'))
++
++ self._message.set_markup('%s %s.\n\n' % (_('Journal content will be restored from'), volume_path) + \
++ '<big><b>%s</b> %s</big>' % (_('Warning:'), _('Current Journal content will be deleted!')))
++
+diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py
+index 71b6ea8..48e25ec 100644
+--- a/src/jarabe/journal/volumestoolbar.py
++++ b/src/jarabe/journal/volumestoolbar.py
+@@ -36,7 +36,7 @@
+ from sugar import env
+
+ from jarabe.journal import model
+-from jarabe.view.palettes import VolumePalette
++from jarabe.view.palettes import JournalVolumePalette
+
+
+ _JOURNAL_0_METADATA_DIR = '.olpc.store'
+@@ -341,7 +341,7 @@ def __init__(self, mount):
+ self.props.xo_color = color
+
+ def create_palette(self):
+- palette = VolumePalette(self._mount)
++ palette = JournalVolumePalette(self._mount)
+ #palette.props.invoker = FrameWidgetInvoker(self)
+ #palette.set_group_id('frame')
+ return palette
+diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
+index 92e8712..bd9bef2 100644
+--- a/src/jarabe/model/Makefile.am
++++ b/src/jarabe/model/Makefile.am
+@@ -12,6 +12,7 @@ sugar_PYTHON = \
+ neighborhood.py \
+ network.py \
+ notifications.py \
++ processmanagement.py \
+ shell.py \
+ screen.py \
+ session.py \
+diff --git a/src/jarabe/model/processmanagement.py b/src/jarabe/model/processmanagement.py
+new file mode 100644
+index 0000000..466e1f6
+--- /dev/null
++++ b/src/jarabe/model/processmanagement.py
+@@ -0,0 +1,98 @@
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.org>
++# Copyright (C) 2010, Plan Ceibal <comunidad@plan.ceibal.edu.uy>
++#
++# 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 gobject
++import glib
++import gio
++
++from sugar import env
++from gettext import gettext as _
++
++BYTES_TO_READ = 100
++
++class ProcessManagement(gobject.GObject):
++
++ __gtype_name__ = 'ProcessManagement'
++
++ __gsignals__ = {
++ 'process-management-running' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])),
++ 'process-management-started' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
++ 'process-management-finished' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
++ 'process-management-failed' : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str]))
++ }
++
++ def __init__(self):
++ gobject.GObject.__init__(self)
++ self._running = False
++
++ def do_process(self, cmd):
++ self._run_cmd_async(cmd)
++
++ def _report_process_status(self, stream, result):
++ data = stream.read_finish(result)
++
++ if len(data):
++ self.emit('process-management-running', data)
++ stream.read_async(BYTES_TO_READ, self._report_process_status)
++
++ def _report_process_error(self, stream, result, concat_err=''):
++ data = stream.read_finish(result)
++ concat_err = concat_err + data
++
++ if len(data) == 0:
++ self.emit('process-management-failed', concat_err)
++ else:
++ stream.read_async(BYTES_TO_READ, self._report_process_error, user_data=concat_err)
++
++ def _notify_error(self, stderr):
++ stdin_stream = gio.unix.InputStream(stderr, True)
++ stdin_stream.read_async(BYTES_TO_READ, self._report_process_error)
++
++ def _notify_process_status(self, stdout):
++ stdin_stream = gio.unix.InputStream(stdout, True)
++ stdin_stream.read_async(BYTES_TO_READ, self._report_process_status)
++
++ def _run_cmd_async(self, cmd):
++ if self._running == False:
++ try:
++ pid, stdin, stdout, stderr = glib.spawn_async(cmd, flags=glib.SPAWN_DO_NOT_REAP_CHILD, standard_output=True, standard_error=True)
++ gobject.child_watch_add(pid, _handle_process_end, (self, stderr))
++ except Exception:
++ self.emit('process-management-failed', _("Error - Call process: ") + str(cmd))
++ else:
++ self._notify_process_status(stdout)
++ self._running = True
++ self.emit('process-management-started')
++
++def _handle_process_end(pid, condition, (myself, stderr)):
++ myself._running = False
++
++ if os.WIFEXITED(condition) and\
++ os.WEXITSTATUS(condition) == 0:
++ myself.emit('process-management-finished')
++ else:
++ myself._notify_error(stderr)
++
++def find_and_absolutize(script_name):
++ paths = env.os.environ['PATH'].split(':')
++ for path in paths:
++ looking_path = path + '/' + script_name
++ if env.os.path.isfile(looking_path):
++ return looking_path
++
++ return None
+diff --git a/src/jarabe/view/palettes.py b/src/jarabe/view/palettes.py
+index 3195c0c..a9e3629 100644
+--- a/src/jarabe/view/palettes.py
++++ b/src/jarabe/view/palettes.py
+@@ -1,4 +1,6 @@
+ # Copyright (C) 2008 One Laptop Per Child
++# Copyright (C) 2010, Plan Ceibal <comunidad@plan.ceibal.edu.uy>
++# Copyright (C) 2010, Paraguay Educa <tecnologia@paraguayeduca.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
+@@ -31,6 +33,7 @@
+ from sugar.graphics.xocolor import XoColor
+ from sugar.activity.i18n import pgettext
+
++from jarabe.journal.processdialog import VolumeBackupDialog, VolumeRestoreDialog
+ from jarabe.model import shell
+ from jarabe.view.viewsource import setup_view_source
+ from jarabe.journal import misc
+@@ -253,3 +256,44 @@ def __popup_cb(self, palette):
+ self._progress_bar.props.fraction = fraction
+ self._free_space_label.props.label = _('%(free_space)d MB Free') % \
+ {'free_space': free_space / (1024 * 1024)}
++
++
++class JournalVolumePalette(VolumePalette):
++
++ __gtype_name__ = 'JournalVolumePalette'
++
++ def __init__(self, mount):
++ VolumePalette.__init__(self, mount)
++
++ journal_separator = gtk.SeparatorMenuItem()
++ journal_separator.show()
++
++ self.menu.prepend(journal_separator)
++
++ icon = Icon(icon_name='transfer-from', icon_size=gtk.ICON_SIZE_MENU)
++ icon.show()
++
++ menu_item_journal_restore = MenuItem(_('Restore Journal'))
++ menu_item_journal_restore.set_image(icon)
++ menu_item_journal_restore.connect('activate', self.__journal_restore_activate_cb, mount.get_root().get_path())
++ menu_item_journal_restore.show()
++
++ self.menu.prepend(menu_item_journal_restore)
++
++ icon = Icon(icon_name='transfer-to', icon_size=gtk.ICON_SIZE_MENU)
++ icon.show()
++
++ menu_item_journal_backup = MenuItem(_('Backup Journal'))
++ menu_item_journal_backup.set_image(icon)
++ menu_item_journal_backup.connect('activate', self.__journal_backup_activate_cb, mount.get_root().get_path())
++ menu_item_journal_backup.show()
++
++ self.menu.prepend(menu_item_journal_backup)
++
++ def __journal_backup_activate_cb(self, menu_item, mount_path):
++ dialog = VolumeBackupDialog(mount_path)
++ dialog.show()
++
++ def __journal_restore_activate_cb(self, menu_item, mount_path):
++ dialog = VolumeRestoreDialog(mount_path)
++ dialog.show()
+--
+1.7.6
+