server/sources/storages.py
branchstable
changeset 5218 aebd00a2d316
parent 5183 8d66003351f8
child 5219 35d44017c72b
--- a/server/sources/storages.py	Mon Apr 12 15:15:00 2010 +0200
+++ b/server/sources/storages.py	Mon Apr 12 15:21:08 2010 +0200
@@ -1,6 +1,8 @@
 """custom storages for the system source"""
 from os import unlink, path as osp
 
+from yams.schema import role_name
+
 from cubicweb import Binary
 from cubicweb.server.hook import Operation
 
@@ -54,6 +56,22 @@
 # * better file path attribution
 # * handle backup/restore
 
+def uniquify_path(dirpath, basename):
+    """return a unique file name for `basename` in `dirpath`, or None
+    if all attemps failed.
+
+    XXX subject to race condition.
+    """
+    path = osp.join(dirpath, basename)
+    if not osp.isfile(path):
+        return path
+    base, ext = osp.splitext(path)
+    for i in xrange(1, 256):
+        path = '%s%s%s' % (base, i, ext)
+        if not osp.isfile(path):
+            return path
+    return None
+
 class BytesFileSystemStorage(Storage):
     """store Bytes attribute value on the file system"""
     def __init__(self, defaultdir):
@@ -102,16 +120,27 @@
         DeleteFileOp(entity._cw, filepath=self.current_fs_path(entity, attr))
 
     def new_fs_path(self, entity, attr):
-        fspath = osp.join(self.default_directory, '%s_%s' % (entity.eid, attr))
-        while osp.exists(fspath):
-            fspath = '_' + fspath
+        basename = [str(entity.eid), attr]
+        # We try to get some hint about how to name the file using attributes
+        # metadata. Using the real file name and extension when available.
+        #
+        # Keeping the extension might be usefull for exemple in the case of PIL
+        # processing that use filename extension to detect content-type.
+        name = entity.attr_metadata(attr, 'name')
+        if name is not None:
+            basename.append(name.encode(entity._cw.encoding))
+        fspath = uniquify_path(self.default_directory, '_'.join(basename))
+        if fspath is None:
+            msg = entity._cw._('failed to uniquify path (%s, %s)') % (
+                dirpath, basename)
+            raise ValidationError(entity.eid, {role_name(attr, 'subject'): msg})
         return fspath
 
     def current_fs_path(self, entity, attr):
         sysource = entity._cw.pool.source('system')
         cu = sysource.doexec(entity._cw,
                              'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % (
-                                 attr, entity.__regid__, entity.eid))
+                             attr, entity.__regid__, entity.eid))
         rawvalue = cu.fetchone()[0]
         if rawvalue is None: # no previous value
             return self.new_fs_path(entity, attr)