Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/journal/volumestoolbar.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/journal/volumestoolbar.py')
-rw-r--r--src/jarabe/journal/volumestoolbar.py153
1 files changed, 151 insertions, 2 deletions
diff --git a/src/jarabe/journal/volumestoolbar.py b/src/jarabe/journal/volumestoolbar.py
index b21832e..9a49cdf 100644
--- a/src/jarabe/journal/volumestoolbar.py
+++ b/src/jarabe/journal/volumestoolbar.py
@@ -15,7 +15,13 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import logging
+import os
from gettext import gettext as _
+import cPickle
+import xapian
+import json
+import tempfile
+import shutil
import gobject
import gio
@@ -29,13 +35,126 @@ from sugar.graphics.xocolor import XoColor
from jarabe.journal import model
from jarabe.view.palettes import VolumePalette
+_JOURNAL_0_METADATA_DIR = '.olpc.store'
+
+def _get_id(document):
+ """Get the ID for the document in the xapian database."""
+ tl = document.termlist()
+ try:
+ term = tl.skip_to('Q').term
+ if len(term) == 0 or term[0] != 'Q':
+ return None
+ return term[1:]
+ except StopIteration:
+ return None
+
+def _convert_entries(root):
+ """Converts the entries written by the datastore version 0.
+ The metadata and the preview will be written using the new
+ scheme for writing Journal entries to removable storage
+ devices.
+
+ - entries that do not have an associated file are not
+ converted.
+ - if an entry has no title we set it to Untitled and rename
+ the file accordingly, taking care of creating a unique
+ filename
+
+ """
+ try:
+ database = xapian.Database(os.path.join(root, _JOURNAL_0_METADATA_DIR,
+ 'index'))
+ except xapian.DatabaseError, e:
+ logging.error('Convert DS-0 Journal entry. Error reading db: %s',
+ os.path.join(root, _JOURNAL_0_METADATA_DIR, 'index'))
+ return
+
+ metadata_dir_path = os.path.join(root, model.JOURNAL_METADATA_DIR)
+ if not os.path.exists(metadata_dir_path):
+ os.mkdir(metadata_dir_path)
+
+ for i in range(1, database.get_lastdocid() + 1):
+ try:
+ document = database.get_document(i)
+ except xapian.DocNotFoundError, e:
+ logging.debug('Convert DS-0 Journal entry. ' \
+ 'Error getting document %s: %s', i, e)
+ continue
+
+ try:
+ metadata_loaded = cPickle.loads(document.get_data())
+ except cPickle.PickleError, e:
+ logging.debug('Convert DS-0 Journal entry. ' \
+ 'Error converting metadata: %s', e)
+ continue
+
+ if 'activity_id' in metadata_loaded and \
+ 'mime_type' in metadata_loaded and \
+ 'title' in metadata_loaded:
+ metadata = {}
+
+ uid = _get_id(document)
+ if uid is None:
+ continue
+
+ for key, value in metadata_loaded.items():
+ metadata[str(key)] = str(value[0])
+
+ if 'uid' not in metadata:
+ metadata['uid'] = uid
+
+ if 'filename' in metadata:
+ filename = metadata['filename']
+ else:
+ continue
+ if not os.path.exists(os.path.join(root, filename)):
+ continue
+
+ if metadata['title'] == '':
+ metadata['title'] = _('Untitled')
+ fn = model.get_file_name(metadata['title'],
+ metadata['mime_type'])
+ new_filename = model.get_unique_file_name(root, fn)
+ metadata['filename'] = new_filename
+ os.rename(os.path.join(root, filename),
+ os.path.join(root, new_filename))
+ filename = new_filename
+
+ preview_path = os.path.join(root, _JOURNAL_0_METADATA_DIR,
+ 'preview', uid)
+ if os.path.exists(preview_path):
+ preview_fname = filename + '.preview'
+ new_preview_path = os.path.join(root,
+ model.JOURNAL_METADATA_DIR,
+ preview_fname)
+ if not os.path.exists(new_preview_path):
+ metadata['preview'] = preview_fname
+ shutil.copy(preview_path, new_preview_path)
+
+ metadata_fname = filename + '.metadata'
+ metadata_path = os.path.join(root, model.JOURNAL_METADATA_DIR,
+ metadata_fname)
+ if not os.path.exists(metadata_path):
+ (fh, fn) = tempfile.mkstemp(dir=root)
+ os.write(fh, json.dumps(metadata))
+ os.close(fh)
+ os.rename(fn, metadata_path)
+
+ logging.debug('Convert DS-0 Journal entry. Entry converted: ' \
+ 'File=%s Metadata=%s',
+ os.path.join(root, filename), metadata)
+
+
class VolumesToolbar(gtk.Toolbar):
__gtype_name__ = 'VolumesToolbar'
__gsignals__ = {
'volume-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([str]))
+ ([str])),
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str, str]))
}
def __init__(self):
@@ -78,9 +197,15 @@ class VolumesToolbar(gtk.Toolbar):
def _add_button(self, mount):
logging.debug('VolumeToolbar._add_button: %r' % mount.get_name())
+ if os.path.exists(os.path.join(mount.get_root().get_path(),
+ _JOURNAL_0_METADATA_DIR)):
+ logging.debug('Convert DS-0 Journal entries.')
+ gobject.idle_add(_convert_entries, mount.get_root().get_path())
+
button = VolumeButton(mount)
button.props.group = self._volume_buttons[0]
button.connect('toggled', self._button_toggled_cb)
+ button.connect('volume-error', self.__volume_error_cb)
position = self.get_item_index(self._volume_buttons[-1]) + 1
self.insert(button, position)
button.show()
@@ -90,6 +215,9 @@ class VolumesToolbar(gtk.Toolbar):
if len(self.get_children()) > 1:
self.show()
+ def __volume_error_cb(self, button, strerror, severity):
+ self.emit('volume-error', strerror, severity)
+
def _button_toggled_cb(self, button):
if button.props.active:
self.emit('volume-changed', button.mount_point)
@@ -123,6 +251,12 @@ class VolumesToolbar(gtk.Toolbar):
button.props.active = True
class BaseButton(RadioToolButton):
+ __gsignals__ = {
+ 'volume-error': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str, str]))
+ }
+
def __init__(self, mount_point):
RadioToolButton.__init__(self)
@@ -137,7 +271,22 @@ class BaseButton(RadioToolButton):
info, timestamp):
object_id = selection_data.data
metadata = model.get(object_id)
- model.copy(metadata, self.mount_point)
+ file_path = model.get_file(metadata['uid'])
+
+ if not file_path or not os.path.exists(file_path):
+ logging.warn('Entries without a file cannot be copied.')
+ self.emit('volume-error',
+ _('Entries without a file cannot be copied.'),
+ _('Warning'))
+ return
+
+ try:
+ model.copy(metadata, self.mount_point)
+ except (IOError, OSError), e:
+ logging.exception('Error while copying the entry. %s', e.strerror)
+ self.emit('volume-error',
+ _('Error while copying the entry. %s') % e.strerror,
+ _('Error'))
class VolumeButton(BaseButton):
def __init__(self, mount):