hi, <br>I attached patches of journal backup on volumenes.<br>then, we need migrate to 0.84.<br><br><br>From 3c28e29e44b8b30a2598ed7e90b3ca3ac809c89e Mon Sep 17 00:00:00 2001<br>From: Esteban Arias &lt;<a href="mailto:earias@plan.ceibal.edu.uy" target="_blank">earias@plan.ceibal.edu.uy</a>&gt;<br>
Date: Wed, 2 Jun 2010 12:32:59 -0300<br>
Subject: [PATCH] Adds-JournalVolumePalette<br>Organization: Plan Ceibal<br><br>---<br> src/jarabe/journal/volumestoolbar.py |   42 +++++++++++++++++++++++++++++++++-<br> 1 files changed, 41 insertions(+), 1 deletions(-)<br>

<br>diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py<br>index 74b974c..cc7ae38 100644<br>--- a/src/jarabe/journal/volumestoolbar.py<br>+++ b/src/jarabe/journal/volumestoolbar.py<br>

@@ -1,4 +1,6 @@<br> # Copyright (C) 2007, One Laptop Per Child<br>+# Copyright (C) 2010, Plan Ceibal &lt;<a href="mailto:comunidad@plan.ceibal.edu.uy" target="_blank">comunidad@plan.ceibal.edu.uy</a>&gt;<br>+# Copyright (C) 2010, Paraguay Educa &lt;<a href="mailto:tecnologia@paraguayeduca.org" target="_blank">tecnologia@paraguayeduca.org</a>&gt;<br>

 #<br> # This program is free software; you can redistribute it and/or modify<br> # it under the terms of the GNU General Public License as published by<br>@@ -22,13 +24,17 @@ import gio<br> import gtk<br> import gconf<br>

 <br>+from sugar.graphics.icon import Icon<br> from sugar.graphics.radiotoolbutton import RadioToolButton<br> from sugar.graphics.palette import Palette<br> from sugar.graphics.xocolor import XoColor<br>+from sugar.graphics.menuitem import MenuItem<br>

 <br> from jarabe.journal import model<br>+from jarabe.journal.backup import Backup<br> from jarabe.view.palettes import VolumePalette<br> <br>+<br> class VolumesToolbar(gtk.Toolbar):<br>     __gtype_name__ = &#39;VolumesToolbar&#39;<br>

 <br>@@ -164,7 +170,7 @@ class VolumeButton(BaseButton):<br>         self.props.xo_color = color<br> <br>     def create_palette(self):<br>-        palette = VolumePalette(self._mount)<br>+        palette = JournalVolumePalette(self._mount)<br>

         #palette.props.invoker = FrameWidgetInvoker(self)<br>         #palette.set_group_id(&#39;frame&#39;)<br>         return palette<br>@@ -179,3 +185,37 @@ class JournalButton(BaseButton):<br>         color = XoColor(client.get_string(&#39;/desktop/sugar/user/color&#39;))<br>

         self.props.xo_color = color<br> <br>+class JournalVolumePalette(VolumePalette):<br>+<br>+    __gtype_name__ = &#39;JournalVolumePalette&#39;<br>+<br>+    def __init__(self, mount):<br>+        VolumePalette.__init__(self, mount)<br>

+<br>+        journal_separator = gtk.SeparatorMenuItem()<br>+        self.menu.append(journal_separator)<br>+        journal_separator.show()<br>+<br>+        menu_item_journal_backup = MenuItem(_(&#39;Journal Backup&#39;))<br>

+        icon = Icon(icon_name=&#39;transfer-to&#39;, icon_size=gtk.ICON_SIZE_MENU)<br>+        menu_item_journal_backup.set_image(icon)<br>+        icon.show()<br>+        menu_item_journal_backup.connect(&#39;activate&#39;, self.__journal_backup_activate_cb, mount.get_root().get_path())<br>

+        self.menu.append(menu_item_journal_backup)<br>+        menu_item_journal_backup.show()<br>+<br>+        menu_item_journal_restore = MenuItem(_(&#39;Journal Restore&#39;))<br>+        icon = Icon(icon_name=&#39;transfer-from&#39;, icon_size=gtk.ICON_SIZE_MENU)<br>

+        menu_item_journal_restore.set_image(icon)<br>+        icon.show()<br>+        menu_item_journal_restore.connect(&#39;activate&#39;, self.__journal_restore_activate_cb, mount.get_root().get_path())<br>+        self.menu.append(menu_item_journal_restore)<br>

+        menu_item_journal_restore.show()<br>+<br>+    def __journal_backup_activate_cb(self, menu_item, mount_path):<br>+        self._do_backup = Backup(&#39;backup&#39;, mount_path)<br>+        self._do_backup.show()<br>

+<br>+    def __journal_restore_activate_cb(self, menu_item, mount_path):<br>+        self._do_restore = Backup(&#39;restore&#39;, mount_path)<br>+        self._do_restore.show()<br>-- <br>1.6.2.5<br><br clear="all">From 1e85ffa2955e7ca5761786ae63934ed9a41acffb Mon Sep 17 00:00:00 2001<br>
From: Esteban Arias &lt;<a href="mailto:earias@plan.ceibal.edu.uy">earias@plan.ceibal.edu.uy</a>&gt;<br>Date: Wed, 2 Jun 2010 12:48:03 -0300<br>Subject: [PATCH] Adds-view-and-scripts-for-volumes-backup-restore<br>Organization: Plan Ceibal<br>
<br>---<br> bin/Makefile.am                       |    2 +<br> bin/journal-backup-volume             |   65 +++++++++<br> bin/journal-restore-volume            |   67 ++++++++++<br> src/jarabe/journal/Makefile.am        |    3 +-<br>
 src/jarabe/journal/backup.py          |  231 +++++++++++++++++++++++++++++++++<br> src/jarabe/model/Makefile.am          |    3 +-<br> src/jarabe/model/processmanagement.py |   88 +++++++++++++<br> 7 files changed, 457 insertions(+), 2 deletions(-)<br>
 create mode 100644 bin/journal-backup-volume<br> create mode 100644 bin/journal-restore-volume<br> create mode 100644 src/jarabe/journal/backup.py<br> create mode 100644 src/jarabe/model/processmanagement.py<br><br>diff --git a/bin/Makefile.am b/bin/Makefile.am<br>
index 05a9215..90f157a 100644<br>--- a/bin/Makefile.am<br>+++ b/bin/Makefile.am<br>@@ -9,6 +9,8 @@ python_scripts =        \<br> <br> bin_SCRIPTS =             \<br>     sugar            \<br>+    journal-backup-volume            \<br>
+    journal-restore-volume            \<br>     $(python_scripts)<br> <br> EXTRA_DIST = $(python_scripts) <a href="http://sugar.in">sugar.in</a><br>diff --git a/bin/journal-backup-volume b/bin/journal-backup-volume<br>new file mode 100644<br>
index 0000000..e4a070f<br>--- /dev/null<br>+++ b/bin/journal-backup-volume<br>@@ -0,0 +1,65 @@<br>+#!/bin/bash<br>+# Journal Backup<br>+# Copyright (C) 2010 Plan Ceibal<br>+# Copyright (C) 2010 Paraguay Educa<br>+#<br>+# This program is free software: you can redistribute it and/or modify<br>
+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation, either version 3 of the License, or<br>+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>
+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>
+# along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>+#<br>+# Contact information: <a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a><br>
+# Plan Ceibal <a href="http://www.ceibal.edu.uy">http://www.ceibal.edu.uy</a><br>+<br>+function check_for_errors() {<br>+    if [[ $? != 0 ]]; then<br>+        echo &quot;$1&quot; &gt;&amp;2<br>+        exit 1<br>+    fi;<br>
+}<br>+<br>+function validate_path() {<br>+    if !(test -e &quot;$1&quot;); then<br>+        echo &quot;No such file $1&quot; &gt;&amp;2<br>+        exit 1<br>+    fi;<br>+}<br>+<br>+DATASTORE=&quot;$HOME/.sugar/default/datastore/&quot;<br>
+validate_path $DATASTORE<br>+<br>+if [ -z &quot;$1&quot; ]; then<br>+    echo &quot;Backup path is not valid.&quot; &gt;&amp;2<br>+    exit 1<br>+fi;<br>+<br>+PARAMS_PATH=$1<br>+validate_path $PARAMS_PATH<br>+<br>+if [ -z &quot;$2&quot; ]; then<br>
+    echo &quot;Backup name is not valid.&quot; &gt;&amp;2<br>+    exit 1<br>+fi;<br>+<br>+SERIAL=$2<br>+<br>+#TODO: Lets find a sugar-way to do this?<br>+pkill -f -x &quot;python /usr/bin/datastore-service&quot;<br>+<br>
+BACKUP_PATH=$PARAMS_PATH/.backup/$SERIAL/<br>+mkdir -p $BACKUP_PATH<br>+<br>+cd $DATASTORE;<br>+tar -zcf &quot;$BACKUP_PATH&quot;datastore.tar.gz ./*<br>+<br>+check_for_errors<br>+<br>+exit 0<br>diff --git a/bin/journal-restore-volume b/bin/journal-restore-volume<br>
new file mode 100644<br>index 0000000..134be1c<br>--- /dev/null<br>+++ b/bin/journal-restore-volume<br>@@ -0,0 +1,67 @@<br>+#!/bin/bash<br>+# Journal Restore<br>+# Copyright (C) 2010 Plan Ceibal<br>+# Copyright (C) 2010 Paraguay Educa<br>
+#<br>+# This program is free software: you can redistribute it and/or modify<br>+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation, either version 3 of the License, or<br>
+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>+# along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>
+#<br>+# Contact information: <a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a><br>+# Plan Ceibal <a href="http://www.ceibal.edu.uy">http://www.ceibal.edu.uy</a><br>+<br>+function check_for_errors() {<br>
+    if [[ $? != 0 ]]; then<br>+        echo &quot;$1&quot; &gt;&amp;2<br>+        exit 1<br>+    fi;<br>+}<br>+<br>+function validate_path() {<br>+    if !(test -e &quot;$1&quot;); then<br>+        echo &quot;No such file $1&quot; &gt;&amp;2<br>
+        exit 1<br>+    fi;<br>+}<br>+<br>+DATASTORE_PATH=&quot;$HOME/.sugar/default/datastore/&quot;<br>+validate_path $DATASTORE_PATH<br>+<br>+if [ -z &quot;$1&quot; ]; then<br>+    echo &quot;Backup path is not valid.&quot; &gt;&amp;2<br>
+    exit 1<br>+fi;<br>+<br>+PARAMS_PATH=$1<br>+validate_path $PARAMS_PATH<br>+<br>+if [ -z &quot;$2&quot; ]; then<br>+    echo &quot;Backup name is not valid.&quot; &gt;&amp;2<br>+    exit 1<br>+fi;<br>+<br>+SERIAL=$2<br>
+<br>+BACKUP_FILE=$PARAMS_PATH/.backup/$SERIAL/datastore.tar.gz<br>+validate_path $BACKUP_FILE<br>+<br>+#TODO: Lets find a sugar-way to do this?<br>+pkill -f -x &quot;python /usr/bin/datastore-service&quot;<br>+<br>+cd $DATASTORE_PATH<br>
+rm -rf $DATASTORE_PATH*<br>+<br>+tar -xvzf $BACKUP_FILE<br>+<br>+check_for_errors<br>+<br>+exit 0<br>diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am<br>index f4bf273..6a3091e 100644<br>--- a/src/jarabe/journal/Makefile.am<br>
+++ b/src/jarabe/journal/Makefile.am<br>@@ -14,4 +14,5 @@ sugar_PYTHON =                \<br>     model.py            \<br>     objectchooser.py        \<br>     palettes.py            \<br>-    volumestoolbar.py<br>+    volumestoolbar.py \<br>
+    backup.py<br>diff --git a/src/jarabe/journal/backup.py b/src/jarabe/journal/backup.py<br>new file mode 100644<br>index 0000000..1b12660<br>--- /dev/null<br>+++ b/src/jarabe/journal/backup.py<br>@@ -0,0 +1,231 @@<br>+#!/usr/bin/env python<br>
+# Copyright (C) 2010, Plan Ceibal &lt;<a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a>&gt;<br>+# Copyright (C) 2010, Paraguay Educa &lt;<a href="mailto:tecnologia@paraguayeduca.org">tecnologia@paraguayeduca.org</a>&gt;<br>
+#<br>+# This program is free software: you can redistribute it and/or modify<br>+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation, either version 3 of the License, or<br>
+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>+# along with this program.  If not, see &lt;<a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>&gt;.<br>
+<br>+import os<br>+import gtk<br>+import gobject<br>+import gconf<br>+import logging<br>+<br>+from gettext import gettext as _<br>+from sugar.graphics import style<br>+from sugar.graphics.icon import Icon<br>+from sugar.graphics.xocolor import XoColor<br>
+<br>+from jarabe.model.session import get_session_manager<br>+from jarabe.model.processmanagement import ProcessManagement<br>+<br>+PATH_BACKUP = &#39;/usr/bin/journal-backup-volume&#39;<br>+PATH_RESTORE = &#39;/usr/bin/journal-restore-volume&#39;<br>
+<br>+class Backup(gtk.Window):<br>+<br>+    __gtype_name__ = &#39;SugarJournalBackup&#39;<br>+<br>+    def __init__(self, type_ps, mount_path):<br>+<br>+        self._type_ps = type_ps<br>+        self._mount_path = mount_path<br>
+<br>+        if self._isValidType(self._type_ps):<br>+            if ((not self._mount_path == &quot;&quot;) and (not self._mount_path == None)):<br>+                self._progressBarHandler = None<br>+<br>+                gtk.Window.__init__(self)<br>
+<br>+                self.set_border_width(style.LINE_WIDTH)<br>+                width = gtk.gdk.screen_width()<br>+                height = gtk.gdk.screen_height()<br>+                self.set_size_request(width, height)<br>
+                self.set_position(gtk.WIN_POS_CENTER_ALWAYS)<br>+                self.set_decorated(False)<br>+                self.set_resizable(False)<br>+                self.set_modal(True)<br>+<br>+                self._main_view = gtk.EventBox()<br>
+                self._vbox = gtk.VBox()<br>+                self._vbox.set_spacing(style.DEFAULT_SPACING)<br>+                self._vbox.set_border_width(style.GRID_CELL_SIZE)<br>+                self._main_view.modify_bg(gtk.STATE_NORMAL,<br>
+                                          style.COLOR_BLACK.get_gdk_color())<br>+                self._main_view.add(self._vbox)<br>+                self._vbox.show()<br>+<br>+                client = gconf.client_get_default()<br>
+                color = XoColor(client.get_string(&#39;/desktop/sugar/user/color&#39;))<br>+<br>+                icon = Icon(icon_name=&#39;activity-journal&#39;,<br>+                            pixel_size=style.XLARGE_ICON_SIZE,<br>
+                            xo_color=color)<br>+                self._vbox.pack_start(icon, False)<br>+                icon.show()<br>+<br>+                self._title = gtk.Label()<br>+                self._title.modify_fg(gtk.STATE_NORMAL,<br>
+                                      style.COLOR_WHITE.get_gdk_color())<br>+                self._title.set_markup(&#39;&lt;b&gt;%s&lt;/b&gt;&#39; % _(self._type_ps))<br>+                self._vbox.pack_start(self._title, False)<br>
+                self._title.show()<br>+<br>+                if self._type_ps == &#39;backup&#39;:<br>+                    lbl_txt = _(&quot;Close all activities and do not remove the external device during the process. \n\n&quot;<br>
+                                &quot;To make the backup of the Journal, a restart is required. \n\n&quot;<br>+                                &quot;The backup can only be restore from this computer&quot;)<br>+                elif self._type_ps == &#39;restore&#39;:<br>
+                    lbl_txt = _(&quot;The content you restore will overwrite the current contents of the Journal. \n\n&quot;<br>+                                &quot;Do not remove the external device during the process. \n&quot;<br>
+                                &quot;To implement the restoration of the Journal, a restart is required. \n\n&quot;<br>+                                &quot;It will restore the backup done for this computer &quot;)<br>
+                lbl_txt += self._get_serial_number()<br>+<br>+                self._message = gtk.Label(lbl_txt)<br>+                self._message.modify_fg(gtk.STATE_NORMAL,<br>+                                      style.COLOR_WHITE.get_gdk_color())<br>
+                self._message.set_line_wrap(True)<br>+                self._vbox.pack_start(self._message, True)<br>+                self._message.show()<br>+<br>+<br>+                vbox = gtk.VBox(False, 5)<br>+                vbox.show()<br>
+                hbox = gtk.HBox(True, 3)<br>+                hbox.show()<br>+<br>+                valign = gtk.Alignment(0, 1, 0, 0)<br>+                valign.show()<br>+                vbox.pack_start(valign)<br>+<br>+                self._accept = gtk.Button()<br>
+                self._accept.set_label(_(&#39;Accept&#39;))<br>+                self._accept.show()<br>+                self._accept.connect(&#39;clicked&#39;, self.__accept_cb)<br>+<br>+                self._show_journal = gtk.Button()<br>
+                self._show_journal.set_label(_(&#39;Close&#39;))<br>+                self._show_journal.show()<br>+                self._show_journal.connect(&#39;clicked&#39;, self.__show_journal_cb)<br>+<br>+                self._restart = gtk.Button()<br>
+                self._restart.set_label(_(&#39;Restart&#39;))<br>+                self._restart.hide()<br>+                self._restart.connect(&#39;clicked&#39;, self.__restart_cb)<br>+<br>+                hbox.add(self._accept)<br>
+                hbox.add(self._show_journal)<br>+                hbox.add(self._restart)<br>+<br>+                halign = gtk.Alignment(1, 0, 0, 0)<br>+                halign.show()<br>+                halign.add(hbox)<br>
+<br>+                vbox.pack_start(halign, False, False, 3)<br>+<br>+                self._vbox.add(vbox)<br>+<br>+                self._progress_bar = gtk.ProgressBar(adjustment=None)<br>+<br>+                self.add(self._main_view)<br>
+                self._main_view.show()<br>+<br>+                self.connect(&quot;realize&quot;, self.__realize_cb)<br>+<br>+                self._process_management = ProcessManagement()<br>+                self._process_management.connect(&#39;process-management-running&#39;, self._set_update_log)<br>
+                self._process_management.connect(&#39;process-management-started&#39;, self._set_status_started)<br>+                self._process_management.connect(&#39;process-management-finished&#39;, self._set_status_finished)<br>
+                self._process_management.connect(&#39;process-management-failed&#39;, self._set_status_failed)<br>+<br>+    def __realize_cb(self, widget):<br>+        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)<br>
+        self.window.set_accept_focus(True)<br>+<br>+    def __show_journal_cb(self, button):<br>+        self.destroy()<br>+<br>+    def __accept_cb(self, *args):<br>+        if ((not self._mount_path == &quot;&quot;) and (not self._mount_path == None)):<br>
+            if self._isValidType(self._type_ps):<br>+                if self._type_ps == &#39;backup&#39;:<br>+                    self._process_management.do_process([PATH_BACKUP, self._mount_path, self._get_serial_number()])<br>
+                elif self._type_ps == &#39;restore&#39;:<br>+                    self._process_management.do_process([PATH_RESTORE, self._mount_path, self._get_serial_number()])<br>+<br>+    def _set_status_started(self, model, data=None):<br>
+        self._message.set_text(_(&quot;Is running, Please wait&quot;))<br>+        self._accept.hide()<br>+        self._show_journal.hide()<br>+        self._showProgress()<br>+<br>+    def _set_update_log(self, model, data):<br>
+        pass<br>+<br>+    def _set_status_finished(self, model, data=None):<br>+        self._message.set_text(_(&quot;It is done, please restart sugar&quot;))<br>+        self._progress_bar.hide()<br>+        self._restart.show()<br>
+<br>+    def _set_status_failed(self, model, data=None):<br>+        logging.error(data)<br>+        self._showMsgError(data)<br>+<br>+    def __restart_cb(self, *args):<br>+        session_manager = get_session_manager()<br>
+        session_manager.logout()<br>+<br>+    def _showProgress(self):<br>+        self._progress_bar.set_fraction(0.0)<br>+        self._progress_bar.show()<br>+        self._vbox.add(self._progress_bar)<br>+        if (self._progressBarHandler == None):<br>
+            self._progressBarHandler = gobject.timeout_add(1000, self._timerProgressBar)<br>+<br>+    def _timerProgressBar(self):<br>+        self._progress_bar.pulse()<br>+        return True<br>+<br>+    def _showMsgError(self, msg):<br>
+        self._title.set_markup(&#39;&lt;b&gt;%s&lt;/b&gt;&#39; % _(&quot;Error&quot;))<br>+        self._message.set_text(msg)<br>+        self._show_journal.show()<br>+        self._progress_bar.hide()<br>+<br>+    def _isValidType(self, type_ps):<br>
+        return (type_ps == &#39;backup&#39;) | (type_ps == &#39;restore&#39;)<br>+<br>+    def _get_serial_number(self):<br>+        serial_no = self._read_file(&#39;/ofw/serial-number&#39;)<br>+        if serial_no is None:<br>
+            serial_no = self._get_nick()<br>+        return serial_no<br>+<br>+    def _read_file(self, path):<br>+        if os.access(path, os.R_OK) == 0:<br>+            return None<br>+<br>+        fd = open(path, &#39;r&#39;)<br>
+        value = fd.read()<br>+        fd.close()<br>+        if value:<br>+            value = value.strip(&#39;\n&#39;)<br>+            return value<br>+        else:<br>+            logging.error(&#39;No information in file or directory: %s&#39; % path)<br>
+            return None<br>+<br>+    def _get_nick(self):<br>+        client = gconf.client_get_default()<br>+        return client.get_string(&quot;/desktop/sugar/user/nick&quot;)<br>diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am<br>
index e9f0700..a4f89a3 100644<br>--- a/src/jarabe/model/Makefile.am<br>+++ b/src/jarabe/model/Makefile.am<br>@@ -15,4 +15,5 @@ sugar_PYTHON =            \<br>     shell.py        \<br>     screen.py        \<br>         session.py        \<br>
-    sound.py<br>+    sound.py \<br>+    processmanagement.py<br>diff --git a/src/jarabe/model/processmanagement.py b/src/jarabe/model/processmanagement.py<br>new file mode 100644<br>index 0000000..d28ad64<br>--- /dev/null<br>
+++ b/src/jarabe/model/processmanagement.py<br>@@ -0,0 +1,88 @@<br>+# Copyright (C) 2010 Paraguay Educa, Martin Abente, Bernie Innocenti<br>+# Copyright (C) 2010, Plan Ceibal &lt;<a href="mailto:comunidad@plan.ceibal.edu.uy">comunidad@plan.ceibal.edu.uy</a>&gt;<br>
+#<br>+# This program is free software; you can redistribute it and/or modify<br>+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation; either version 2 of the License, or<br>
+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>
+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>+# along with this program; if not, write to the Free Software<br>+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA<br>
+<br>+import os<br>+import gobject<br>+import glib<br>+import gio<br>+<br>+from gettext import gettext as _<br>+<br>+BYTES_TO_READ = 100<br>+<br>+class ProcessManagement(gobject.GObject):<br>+<br>+    __gtype_name__ = &#39;ProcessManagement&#39;<br>
+<br>+    __gsignals__ = {<br>+        &#39;process-management-running&#39;    : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])),<br>+        &#39;process-management-started&#39;    : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),<br>
+        &#39;process-management-finished&#39;    : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),<br>+        &#39;process-management-failed&#39;    : (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str]))<br>+    }<br>
+<br>+    def __init__(self):<br>+        gobject.GObject.__init__(self)<br>+        self._running = False<br>+<br>+    def do_process(self, cmd):<br>+        self._run_cmd_async(cmd)<br>+<br>+    def _report_process_status(self, stream, result):<br>
+        data = stream.read_finish(result)<br>+<br>+        if len(data):<br>+            self.emit(&#39;process-management-running&#39;, data)<br>+            stream.read_async(BYTES_TO_READ, self._report_process_status)<br>
+<br>+    def _report_process_error(self, stream, result, concat_err=&#39;&#39;):<br>+        data = stream.read_finish(result)<br>+        concat_err = concat_err + data<br>+<br>+        if len(data) == 0:<br>+                self.emit(&#39;process-management-failed&#39;, concat_err)<br>
+        else:<br>+            stream.read_async(BYTES_TO_READ, self._report_process_error, user_data=concat_err)<br>+<br>+    def _notify_error(self, stderr):<br>+        stdin_stream = gio.unix.InputStream(stderr, True)<br>
+        stdin_stream.read_async(BYTES_TO_READ, self._report_process_error)<br>+<br>+    def _notify_process_status(self, stdout):<br>+        stdin_stream = gio.unix.InputStream(stdout, True)<br>+        stdin_stream.read_async(BYTES_TO_READ, self._report_process_status)<br>
+<br>+    def _run_cmd_async(self, cmd):<br>+        if self._running == False:<br>+            try:<br>+                pid, stdin, stdout, stderr = glib.spawn_async(cmd, flags=glib.SPAWN_DO_NOT_REAP_CHILD, standard_output=True, standard_error=True)<br>
+                gobject.child_watch_add(pid, _handle_process_end, (self, stderr))<br>+            except Exception:<br>+                self.emit(&#39;process-management-failed&#39;, _(&quot;Error - Call process: &quot;) + str(cmd))<br>
+            else:<br>+                self._notify_process_status(stdout)<br>+                self._running  = True<br>+                self.emit(&#39;process-management-started&#39;)<br>+<br>+def _handle_process_end(pid, condition, (myself, stderr)):<br>
+    myself._running = False<br>+<br>+    if os.WIFEXITED(condition) and\<br>+        os.WEXITSTATUS(condition) == 0:<br>+            myself.emit(&#39;process-management-finished&#39;)<br>+    else:<br>+        myself._notify_error(stderr)<br>
-- <br>1.6.2.5<br><br>From c68f3f55bebc332643ee5d0c282fa29851cbd6a5 Mon Sep 17 00:00:00 2001<br>From: Esteban Arias &lt;<a href="mailto:earias@plan.ceibal.edu.uy">earias@plan.ceibal.edu.uy</a>&gt;<br>Date: Wed, 2 Jun 2010 12:50:36 -0300<br>
Subject: [PATCH] Traslate<br>Organization: Plan Ceibal<br><br>---<br> po/es.po |   39 +++++++++++++++++++++++++++++++++++++++<br> 1 files changed, 39 insertions(+), 0 deletions(-)<br><br>diff --git a/po/es.po b/po/es.po<br>
index 587608f..0e138a4 100644<br>--- a/po/es.po<br>+++ b/po/es.po<br>@@ -1275,6 +1275,45 @@ msgstr &quot;Fuente del paquete de la actividad&quot;<br> msgid &quot;View source: %r&quot;<br> msgstr &quot;Ver código fuente: %r&quot;<br>
 <br>+#: ../src/jarabe/journal/volumestoolbar.py:196<br>+msgid &quot;Journal Backup&quot;<br>+msgstr &quot;Respaldar Diario&quot;<br>+<br>+#: ../src/jarabe/journal/volumestoolbar.py:208<br>+msgid &quot;Journal Restore&quot;<br>
+msgstr &quot;Restaurar Diario&quot;<br>+<br>+#: ../src/jarabe/journal/backup.py:84<br>+msgid &quot;backup&quot;<br>+msgstr &quot;respaldar&quot;<br>+<br>+#: ../src/jarabe/journal/backup.py:84<br>+msgid &quot;restore&quot;<br>
+msgstr &quot;restaurar&quot;<br>+<br>+#: ../src/jarabe/journal/backup.py:89<br>+msgid &quot;&quot;<br>+&quot;Close all activities and do not remove the external device during the process. \n\n&quot;<br>+&quot;To make the backup of the Journal, a restart is required. \n\n&quot;<br>
+&quot;The backup can only be restore from this computer&quot;<br>+msgstr &quot;&quot;<br>+&quot;Cierre todas las actividades y no extraiga el dispositivo externo durante el proceso. \n\n&quot;<br>+&quot;Para realizar el respaldo del Diario, se requiere reiniciar. \n\n&quot;<br>
+&quot;El resplado sólo podrá ser recuperado desde este equipo &quot;<br>+<br>+#: ../src/jarabe/journal/backup.py:94<br>+msgid &quot;&quot;<br>+&quot;The content you restore will overwrite the current contents of the Journal. \n\n&quot;<br>
+&quot;Do not remove the external device during the process. \n&quot;<br>+&quot;To implement the restoration of the Journal, a restart is required. \n\n&quot;<br>+&quot;It will restore the backup done for this computer &quot;<br>
+msgstr &quot;&quot;<br>+&quot;El contenido que restaurará va a sobrescribir el contenido acutal del Diario. \n\n&quot;<br>+&quot;No extraiga el dispositivo externo durante el proceso. \n&quot;<br>+&quot;Para aplicar la restauración del Diario, se requiere reiniciar. \n\n&quot;<br>
+&quot;Se restaurará el respado realizado para este equipo &quot;<br>+<br>+<br> #~ msgid &quot;Cannot obtain data needed for registration.&quot;<br> #~ msgstr &quot;No se puede obtener datos necesarios para el registro&quot;<br>
 <br>-- <br>1.6.2.5<br><br><br><br><br>-- <br>
    Esteban Arias<br>    Plan Ceibal - Área Técnica<br>    Avda. Italia 6201<br>    Montevideo - Uruguay.<br>    Tel.: 601.57.73 Interno 2228<br>    E-mail : <a href="mailto:earias@plan.ceibal.edu.uy" target="_blank">earias@plan.ceibal.edu.uy</a><br>

<br>