diff -r 08e7fa906cdb -r aebd00a2d316 server/sources/storages.py --- 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)