1 """custom storages for the system source""" |
1 """custom storages for the system source""" |
2 from os import unlink, path as osp |
2 from os import unlink, path as osp |
|
3 |
|
4 from yams.schema import role_name |
3 |
5 |
4 from cubicweb import Binary |
6 from cubicweb import Binary |
5 from cubicweb.server.hook import Operation |
7 from cubicweb.server.hook import Operation |
6 |
8 |
7 def set_attribute_storage(repo, etype, attr, storage): |
9 def set_attribute_storage(repo, etype, attr, storage): |
52 # TODO |
54 # TODO |
53 # * make it configurable without code |
55 # * make it configurable without code |
54 # * better file path attribution |
56 # * better file path attribution |
55 # * handle backup/restore |
57 # * handle backup/restore |
56 |
58 |
|
59 def uniquify_path(dirpath, basename): |
|
60 """return a unique file name for `basename` in `dirpath`, or None |
|
61 if all attemps failed. |
|
62 |
|
63 XXX subject to race condition. |
|
64 """ |
|
65 path = osp.join(dirpath, basename) |
|
66 if not osp.isfile(path): |
|
67 return path |
|
68 base, ext = osp.splitext(path) |
|
69 for i in xrange(1, 256): |
|
70 path = '%s%s%s' % (base, i, ext) |
|
71 if not osp.isfile(path): |
|
72 return path |
|
73 return None |
|
74 |
57 class BytesFileSystemStorage(Storage): |
75 class BytesFileSystemStorage(Storage): |
58 """store Bytes attribute value on the file system""" |
76 """store Bytes attribute value on the file system""" |
59 def __init__(self, defaultdir): |
77 def __init__(self, defaultdir): |
60 self.default_directory = defaultdir |
78 self.default_directory = defaultdir |
61 |
79 |
100 def entity_deleted(self, entity, attr): |
118 def entity_deleted(self, entity, attr): |
101 """an entity using this storage for attr has been deleted""" |
119 """an entity using this storage for attr has been deleted""" |
102 DeleteFileOp(entity._cw, filepath=self.current_fs_path(entity, attr)) |
120 DeleteFileOp(entity._cw, filepath=self.current_fs_path(entity, attr)) |
103 |
121 |
104 def new_fs_path(self, entity, attr): |
122 def new_fs_path(self, entity, attr): |
105 fspath = osp.join(self.default_directory, '%s_%s' % (entity.eid, attr)) |
123 basename = [str(entity.eid), attr] |
106 while osp.exists(fspath): |
124 # We try to get some hint about how to name the file using attributes |
107 fspath = '_' + fspath |
125 # metadata. Using the real file name and extension when available. |
|
126 # |
|
127 # Keeping the extension might be usefull for exemple in the case of PIL |
|
128 # processing that use filename extension to detect content-type. |
|
129 name = entity.attr_metadata(attr, 'name') |
|
130 if name is not None: |
|
131 basename.append(name.encode(entity._cw.encoding)) |
|
132 fspath = uniquify_path(self.default_directory, '_'.join(basename)) |
|
133 if fspath is None: |
|
134 msg = entity._cw._('failed to uniquify path (%s, %s)') % ( |
|
135 dirpath, basename) |
|
136 raise ValidationError(entity.eid, {role_name(attr, 'subject'): msg}) |
108 return fspath |
137 return fspath |
109 |
138 |
110 def current_fs_path(self, entity, attr): |
139 def current_fs_path(self, entity, attr): |
111 sysource = entity._cw.pool.source('system') |
140 sysource = entity._cw.pool.source('system') |
112 cu = sysource.doexec(entity._cw, |
141 cu = sysource.doexec(entity._cw, |
113 'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % ( |
142 'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % ( |
114 attr, entity.__regid__, entity.eid)) |
143 attr, entity.__regid__, entity.eid)) |
115 rawvalue = cu.fetchone()[0] |
144 rawvalue = cu.fetchone()[0] |
116 if rawvalue is None: # no previous value |
145 if rawvalue is None: # no previous value |
117 return self.new_fs_path(entity, attr) |
146 return self.new_fs_path(entity, attr) |
118 return sysource._process_value(rawvalue, cu.description[0], |
147 return sysource._process_value(rawvalue, cu.description[0], |
119 binarywrap=str) |
148 binarywrap=str) |