--- 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)