Ticket #1636: sugar-1636.patch

File sugar-1636.patch, 9.6 KB (added by alsroot, 14 years ago)

patch whch is based on sugar-1636-pre.patch

  • src/jarabe/journal/listview.py

    diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
    index 6556b08..9b287c4 100644
    a b class BaseListView(gtk.HBox): 
    123123                datastore.connect_to_signal('Deleted',
    124124                                            self.__datastore_deleted_cb)
    125125
     126        model.created.connect(self.__model_updated_cb)
     127        model.updated.connect(self.__model_updated_cb)
     128        model.deleted.connect(self.__model_updated_cb)
     129
     130    def __model_updated_cb(self, sender, **kwargs):
     131        if 'object_id' in kwargs and kwargs['object_id'].startswith('/'):
     132            # otherwise rely on ds' Deleted event for ds objects
     133            self._set_dirty()
     134
    126135    def __destroy_cb(self, widget):
    127136        self._datastore_created_handler.remove()
    128137        self._datastore_updated_handler.remove()
  • src/jarabe/journal/model.py

    diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
    index 9521456..635781d 100644
    a b import os 
    1919from datetime import datetime
    2020import time
    2121import shutil
     22import tempfile
    2223from stat import S_IFMT, S_IFDIR, S_IFREG
    2324import traceback
    2425import re
    import gobject 
    2728import dbus
    2829import gconf
    2930import gio
     31import json
     32import base64
    3033
    3134from sugar import dispatch
    3235from sugar import mime
    class InplaceResultSet(BaseResultSet): 
    308311        files = self._file_list[offset:offset + limit]
    309312
    310313        entries = []
    311         for file_path, stat, mtime_ in files:
    312             metadata = _get_file_metadata(file_path, stat)
     314        for file_path, stat, mtime_, metadata in files:
     315            if metadata == False:
     316                # fallback - the find should fetch md
     317                metadata = _get_file_metadata(file_path, stat)
    313318            metadata['mountpoint'] = self._mount_point
    314319            entries.append(metadata)
    315320
    class InplaceResultSet(BaseResultSet): 
    334339                elif S_IFMT(stat.st_mode) == S_IFREG:
    335340                    add_to_list = True
    336341
    337                     if self._regex is not None and \
    338                             not self._regex.match(full_path):
     342                    md = _get_file_metadata_from_json(dir_path, entry,
     343                                                       preview=False)
     344                    if self._regex is not None:
    339345                        add_to_list = False
     346                        if self._regex.match(full_path):
     347                            add_to_list = True
     348                        elif md:
     349                            # match any of the text md fields
     350                            for f in ['fulltext', 'title', 'description', 'tags']:
     351                                if f in md and self._regex.match(md[f]):
     352                                    add_to_list = True
     353                                    break
    340354
    341355                    if None not in [self._date_start, self._date_end] and \
    342356                            (stat.st_mtime < self._date_start or
    class InplaceResultSet(BaseResultSet): 
    349363                            add_to_list = False
    350364
    351365                    if add_to_list:
    352                         file_info = (full_path, stat, int(stat.st_mtime))
     366                        file_info = (full_path, stat, int(stat.st_mtime), md)
    353367                        self._file_list.append(file_info)
    354368
    355369                    self.progress.send(self)
    class InplaceResultSet(BaseResultSet): 
    364378            self._pending_directories -= 1
    365379
    366380def _get_file_metadata(path, stat):
     381    # will sometimes be called for
     382    # files that do have a metatada file
     383    fname = os.path.basename(path)
     384    dir_path = os.path.dirname(path)
     385    md = _get_file_metadata_from_json(dir_path, fname, preview=True)
     386    if md:
     387        return md
     388
     389    # make up something for files w/o metadata
    367390    client = gconf.client_get_default()
    368391    return {'uid': path,
    369392            'title': os.path.basename(path),
    def _get_file_metadata(path, stat): 
    374397            'icon-color': client.get_string('/desktop/sugar/user/color'),
    375398            'description': path}
    376399
     400def _get_file_metadata_from_json(dir_path, fname, preview=False):
     401    md = None
     402    mdpath = os.path.join(dir_path,
     403                          '.'+fname+'.metadata')
     404    md = False
     405    if os.path.exists(mdpath):
     406        try:
     407            md = json.load(open(mdpath))
     408            md['uid'] = os.path.join(dir_path, fname)
     409        except:
     410            pass
     411    if preview:
     412        prpath = os.path.join(dir_path,
     413                              '.'+fname+'.preview')
     414        try:
     415            preview = base64.b64encode(open(prpath).read())
     416            md['preview'] = preview
     417        except:
     418            pass
     419    else:
     420        if md and 'preview' in md:
     421            del(md['preview'])
     422    return md
     423
    377424_datastore = None
    378425def _get_datastore():
    379426    global _datastore
    def delete(object_id): 
    460507    """
    461508    if os.path.exists(object_id):
    462509        os.unlink(object_id)
     510        dir_path = os.path.dirname(object_id)
     511        fname = os.path.basename(object_id)
     512        old_files = [ os.path.join(dir_path,
     513                                   '.'+fname+'.metadata'),
     514                      os.path.join(dir_path,
     515                                   '.'+fname+'.preview') ]
     516        for ofile in old_files:
     517            if os.path.exists(ofile):
     518                try:
     519                    os.unlink(ofile)
     520                except:
     521                    pass
    463522        deleted.send(None, object_id=object_id)
    464523    else:
    465524        _get_datastore().delete(object_id)
    def write(metadata, file_path='', update_mtime=True): 
    480539    """
    481540    logging.debug('model.write %r %r %r' % (metadata.get('uid', ''), file_path,
    482541                                            update_mtime))
     542
     543    rename = False
     544
    483545    if update_mtime:
    484546        metadata['mtime'] = datetime.now().isoformat()
    485547        metadata['timestamp'] = int(time.time())
    def write(metadata, file_path='', update_mtime=True): 
    495557                                                 file_path,
    496558                                                 True)
    497559    else:
    498         if not os.path.exists(file_path):
     560        if 'uid' in metadata and os.path.exists(metadata['uid']):
     561            file_path = metadata['uid']
     562
     563        if not file_path or not os.path.exists(file_path):
    499564            raise ValueError('Entries without a file cannot be copied to '
    500565                             'removable devices')
    501566
    def write(metadata, file_path='', update_mtime=True): 
    503568        file_name = _get_unique_file_name(metadata['mountpoint'], file_name)
    504569
    505570        destination_path = os.path.join(metadata['mountpoint'], file_name)
    506         shutil.copy(file_path, destination_path)
     571
     572        if os.path.dirname(destination_path) == os.path.dirname(file_path):
     573            ## FIXME: handle renames in a separate codepath
     574            ##        in a failsafe manner
     575            ##        (write new md, then rename and rm old)
     576            # rename on a removable disk - mv file, but let
     577            # the metadata&preview be written - at least md will
     578            # be different
     579            rename = True
     580            old_file_path = file_path
     581
     582            os.rename(file_path, destination_path)
     583            old_fname = os.path.basename(file_path)
     584            old_files = [ os.path.join(metadata['mountpoint'],
     585                                       '.'+old_fname+'.metadata'),
     586                          os.path.join(metadata['mountpoint'],
     587                                       '.'+old_fname+'.preview') ]
     588            for ofile in old_files:
     589                if os.path.exists(ofile):
     590                    try:
     591                        os.unlink(ofile)
     592                    except:
     593                        pass
     594
     595        else:
     596            shutil.copy(file_path, destination_path)
     597
     598        # issue our metadata massage on a copy
     599        md = metadata.copy()
     600        del md['mountpoint']
     601
     602        # if we ever write to places other than the root
     603        # keep in mind that the metadata file must be in the
     604        # same directory as the data file.
     605        md_path = os.path.join(metadata['mountpoint'],
     606                               '.'+file_name+'.metadata')
     607
     608        if 'preview' in md:
     609            preview = md['preview']
     610            preview_fname =  '.'+file_name+'.preview'
     611            preview_path = os.path.join(metadata['mountpoint'], preview_fname)
     612            md['preview'] = preview_fname
     613
     614            # Write preview atomically
     615            (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint'])
     616            os.write(fh, preview)
     617            os.close(fh)
     618            os.rename(fn, preview_path)
     619
     620        # Write metadata atomically (on FSs that support it)
     621        (fh, fn) = tempfile.mkstemp(dir=metadata['mountpoint'])
     622        os.write(fh, json.dumps(md))
     623        os.close(fh)
     624        os.rename(fn, md_path)
     625
    507626        object_id = destination_path
     627        metadata['uid'] = object_id
    508628        created.send(None, object_id=object_id)
    509629
    510630    return object_id
    def write(metadata, file_path='', update_mtime=True): 
    512632def _get_file_name(title, mime_type):
    513633    file_name = title
    514634
    515     extension = '.' + mime.get_primary_extension(mime_type)
    516     if not file_name.endswith(extension):
    517         file_name += extension
     635    mime_extension = mime.get_primary_extension(mime_type)
     636    if mime_extension:
     637        extension = '.' + mime_extension
     638        if not file_name.endswith(extension):
     639            file_name += extension
    518640
    519641    # Invalid characters in VFAT filenames. From
    520642    # http://en.wikipedia.org/wiki/File_Allocation_Table