diff options
| author | Harry (OpenERP) <hmo@tinyerp.com> | 2010-07-11 11:36:46 +0530 |
|---|---|---|
| committer | Harry (OpenERP) <hmo@tinyerp.com> | 2010-07-11 11:36:46 +0530 |
| commit | fad9edc19eb7e0ac32aab96f6f299e328b6574f2 (patch) | |
| tree | 6279c86757be0f9754e2c33d0a1d6388778084d4 /addons/document/nodes.py | |
| parent | 6ccff1aab8992bd56a9e0ac45ad5988b549ab530 (diff) | |
| parent | d6f1e1f29f8110c363d8a413da8dcf17284cc205 (diff) | |
[MERGE]
bzr revid: hmo@tinyerp.com-20100711060646-gwp6039vsln1x30a
Diffstat (limited to 'addons/document/nodes.py')
| -rw-r--r-- | addons/document/nodes.py | 628 |
1 files changed, 458 insertions, 170 deletions
diff --git a/addons/document/nodes.py b/addons/document/nodes.py index dcd864e6db0..8988437e6aa 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ############################################################################## -# +# # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). # @@ -15,18 +15,19 @@ # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## -import base64 -import StringIO +# import base64 +# import StringIO from osv import osv, fields from osv.orm import except_orm -import urlparse +# import urlparse import pooler from tools.safe_eval import safe_eval +import errno import os import time @@ -49,7 +50,7 @@ class node_context(object): context """ cached_roots = {} - def __init__(self, cr, uid, context=None): + def __init__(self, cr, uid, context=None): self.dbname = cr.dbname self.uid = uid self.context = context @@ -57,26 +58,104 @@ class node_context(object): assert self._dirobj self.rootdir = False #self._dirobj._get_root_directory(cr,uid,context) + def __eq__(self, other): + if not type(other) == node_context: + return False + if self.dbname != other.dbname: + return False + if self.uid != other.uid: + return False + if self.context != other.context: + return False + if self.rootdir != other.rootdir: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + def get_uri(self, cr, uri): """ Although this fn passes back to doc.dir, it is needed since - it is a potential caching point """ - (ndir, duri) = self._dirobj._locate_child(cr, self.uid, self.rootdir, uri, None, self) - while duri: - ndir = ndir.child(cr, duri[0]) + it is a potential caching point """ + (ndir, duri) = self._dirobj._locate_child(cr, self.uid, self.rootdir, uri, None, self) + while duri: + ndir = ndir.child(cr, duri[0]) if not ndir: - return False - duri = duri[1:] + return False + duri = duri[1:] return ndir + def get_dir_node(self, cr, dbro): + """Create (or locate) a node for a directory + @param dbro a browse object of document.directory + """ + fullpath = self._dirobj.get_full_path(cr, self.uid, dbro.id, self.context) + if dbro.type == 'directory': + return node_dir(fullpath, None ,self, dbro) + elif dbro.type == 'ressource': + assert dbro.ressource_parent_type_id == False + return node_res_dir(fullpath, None, self, dbro) + else: + raise ValueError("dir node for %s type", dbro.type) + + def get_file_node(self, cr, fbro): + """ Create or locate a node for a static file + @param fbro a browse object of an ir.attachment + """ + parent = None + if fbro.parent_id: + parent = self.get_dir_node(cr, fbro.parent_id) + + return node_file(fbro.name,parent,self,fbro) + + +class node_descriptor(object): + """A file-like interface to the data contents of a node. + + This class is NOT a node, but an /open descriptor/ for some + node. It can hold references to a cursor or a file object, + because the life of a node_descriptor will be the open period + of the data. + It should also take care of locking, with any native mechanism + or using the db. + For the implementation, it would be OK just to wrap around file, + StringIO or similar class. The node_descriptor is only needed to + provide the link to the parent /node/ object. + """ + def __init__(self, parent): + assert isinstance(parent, node_class) + self.name = parent.displayname + self.__parent = parent + def _get_parent(self): + return self.__parent + + def open(self, **kwargs): + raise NotImplementedError + + def close(self): + raise NotImplementedError + + def read(self, size=None): + raise NotImplementedError + + def seek(self, offset, whence=None): + raise NotImplementedError + + def tell(self): + raise NotImplementedError + + def write(self, str): + raise NotImplementedError class node_class(object): """ this is a superclass for our inodes - It is an API for all code that wants to access the document files. + It is an API for all code that wants to access the document files. Nodes have attributes which contain usual file properties """ our_type = 'baseclass' + def __init__(self, path, parent, context): assert isinstance(context,node_context) assert (not parent ) or isinstance(parent,node_class) @@ -87,16 +166,25 @@ class node_class(object): self.mimetype = 'application/octet-stream' self.create_date = None self.write_date = None + self.unixperms = 0660 + self.uuser = 'user' + self.ugroup = 'group' self.content_length = 0 # dynamic context: self.dctx = {} if parent: self.dctx = parent.dctx.copy() self.displayname = 'Object' - + + def __eq__(self, other): + return NotImplemented + + def __ne__(self, other): + return not self.__eq__(other) + def full_path(self): """ Return the components of the full path for some - node. + node. The returned list only contains the names of nodes. """ if self.parent: @@ -105,9 +193,14 @@ class node_class(object): s = [] if isinstance(self.path,list): s+=self.path + elif self.path is None: + s.append('') else: s.append(self.path) return s #map(lambda x: '/' +x, s) + + def __repr__(self): + return "%s@/%s" % (self.our_type, '/'.join(self.full_path())) def children(self, cr, domain=None): print "node_class.children()" @@ -117,19 +210,43 @@ class node_class(object): print "node_class.child()" return None + def get_uri(self, cr, uri): + duri = uri + ndir = self + while duri: + ndir = ndir.child(cr, duri[0]) + if not ndir: + return False + duri = duri[1:] + return ndir + def path_get(self): print "node_class.path_get()" return False - + def get_data(self,cr): - raise TypeError('no data for %s'% self.type) + raise IOError(errno.EINVAL, 'no data for %s' % self.type) + + def open_data(self, cr, mode): + """ Open a node_descriptor object for this node. + + @param the mode of open, eg 'r', 'w', 'a', like file.open() + + This operation may lock the data for this node (and accross + other node hierarchies), until the descriptor is close()d. If + the node is locked, subsequent opens (depending on mode) may + immediately fail with an exception (which?). + For this class, there is no data, so no implementation. Each + child class that has data should override this. + """ + raise IOError(errno.EINVAL, 'no data for %s' % self.type) def _get_storage(self,cr): raise RuntimeError("no storage for base class") def get_etag(self,cr): """ Get a tag, unique per object + modification. - + see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """ return self._get_ttag(cr) + ':' + self._get_wtag(cr) @@ -139,14 +256,14 @@ class node_class(object): wtime = time.mktime(time.strptime(self.write_date,'%Y-%m-%d %H:%M:%S')) else: wtime = time.time() return str(wtime) - + def _get_ttag(self,cr): """ Get a unique tag for this type/id of object. Must be overriden, so that each node is uniquely identified. """ print "node_class.get_ttag()",self raise RuntimeError("get_etag stub()") - + def get_dav_props(self, cr): """ If this class has special behaviour for GroupDAV etc, export its capabilities """ @@ -161,6 +278,35 @@ class node_class(object): def get_dav_eprop(self, cr, ns, prop): return None + def move_to(self, cr, ndir_node, new_name=False, fil_obj=None, ndir_obj=None, in_write=False): + """ Move this node to a new parent directory. + @param ndir_node the collection that this node should be moved under + @param new_name a name to rename this node to. If omitted, the old + name is preserved + @param fil_obj, can be None, is the browse object for the file, + if already available. + @param ndir_obj must be the browse object to the new doc.directory + location, where this node should be moved to. + in_write: When called by write(), we shouldn't attempt to write the + object, but instead return the dict of vals (avoid re-entrance). + If false, we should write all data to the object, here, as if the + caller won't do anything after calling move_to() + + Return value: + True: the node is moved, the caller can update other values, too. + False: the node is either removed or fully updated, the caller + must discard the fil_obj, not attempt to write any more to it. + dict: values to write back to the object. *May* contain a new id! + + Depending on src and target storage, implementations of this function + could do various things. + Should also consider node<->content, dir<->dir moves etc. + + Move operations, as instructed from APIs (eg. request from DAV) could + use this function. + """ + raise NotImplementedError + def rm(self, cr): raise RuntimeError("Not Implemented") @@ -172,17 +318,18 @@ class node_class(object): class node_database(node_class): """ A node representing the database directory - + """ our_type = 'database' def __init__(self, path=[], parent=False, context=None): super(node_database,self).__init__(path, parent, context) + self.unixperms = 040750 def children(self, cr, domain=None): - res = self._child_get(cr, domain=domain) + self._file_get(cr) + res = self._child_get(cr, domain=domain) + self._file_get(cr) return res - def child(self, cr, name, domain=None): + def child(self, cr, name, domain=None): res = self._child_get(cr, name, domain=None) if res: return res[0] @@ -196,7 +343,7 @@ class node_database(node_class): uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) - where = [('parent_id','=',parent_id)] + where = [('parent_id','=',parent_id)] if name: where.append(('name','=',name)) if not domain: @@ -204,40 +351,29 @@ class node_database(node_class): where2 = where + domain + [('type', '=', 'directory')] ids = dirobj.search(cr, uid, where2, context=ctx) - res = [] - for dirr in dirobj.browse(cr, uid, ids, context=ctx): + res = [] + for dirr in dirobj.browse(cr, uid, ids, context=ctx): res.append(node_dir(dirr.name, self, self.context,dirr)) - + where2 = where + domain + [('type', '=', 'ressource'), ('ressource_parent_type_id','=',False)] - ids = dirobj.search(cr, uid, where2, context=ctx) - for dirr in dirobj.browse(cr, uid, ids, context=ctx): + ids = dirobj.search(cr, uid, where2, context=ctx) + for dirr in dirobj.browse(cr, uid, ids, context=ctx): res.append(node_res_dir(dirr.name, self, self.context, dirr)) - - fil_obj = dirobj.pool.get('ir.attachment') + + fil_obj = dirobj.pool.get('ir.attachment') ids = fil_obj.search(cr, uid, where, context=ctx) if ids: for fil in fil_obj.browse(cr, uid, ids, context=ctx): res.append(node_file(fil.name, self, self.context, fil)) return res - def _file_get(self,cr, nodename=False, directory_id=False): + def _file_get(self,cr, nodename=False): res = [] - cntobj = self.context._dirobj.pool.get('document.directory.content') - uid = self.context.uid - ctx = self.context.context.copy() - ctx.update(self.dctx) - where = [('directory_id','=',directory_id) ] - ids = cntobj.search(cr, uid, where, context=ctx) - for content in cntobj.browse(cr, uid, ids, context=ctx): - res3 = cntobj._file_get(cr, self, nodename, content) - if res3: - res.extend(res3) - return res def _get_ttag(self,cr): return 'db-%s' % cr.dbname - + class node_dir(node_database): our_type = 'collection' @@ -253,6 +389,7 @@ class node_dir(node_database): # TODO: the write date should be MAX(file.write).. self.write_date = dirr and (dirr.write_date or dirr.create_date) or False self.content_length = 0 + self.unixperms = 040750 if dctx: self.dctx.update(dctx) dc2 = self.context.context @@ -268,29 +405,48 @@ class node_dir(node_database): print e pass - + def __eq__(self, other): + if type(self) != type(other): + return False + if not self.context == other.context: + return False + # Two directory nodes, for the same document.directory, may have a + # different context! (dynamic folders) + if self.dctx != other.dctx: + return False + return self.dir_id == other.dir_id + def get_data(self, cr): res = '' - for child in self.children(cr): - res += child.get_data(cr) + for child in self.children(cr): + res += child.get_data(cr) return res - - def _file_get(self, cr, nodename=False): - return super(node_dir,self)._file_get(cr, nodename, self.dir_id) + res = super(node_dir,self)._file_get(cr, nodename) + + cntobj = self.context._dirobj.pool.get('document.directory.content') + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + where = [('directory_id','=',self.dir_id) ] + ids = cntobj.search(cr, uid, where, context=ctx) + for content in cntobj.browse(cr, uid, ids, context=ctx): + res3 = cntobj._file_get(cr, self, nodename, content) + if res3: + res.extend(res3) - + return res def _child_get(self, cr, name=None, domain=None): return super(node_dir,self)._child_get(cr, name, self.dir_id, domain=domain) def rmcol(self, cr): uid = self.context.uid - directory = self.context._dirobj.browse(cr, uid, self.dir_id) - res = False + directory = self.context._dirobj.browse(cr, uid, self.dir_id) + res = False if not directory: - raise OSError(2, 'Not such file or directory.') + raise OSError(2, 'Not such file or directory.') if directory._table_name=='document.directory': if self.children(cr): raise OSError(39, 'Directory not empty.') @@ -300,12 +456,12 @@ class node_dir(node_database): return res def create_child_collection(self, cr, objname): - object2 = False + object2 = False dirobj = self.context._dirobj uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) - obj = dirobj.browse(cr, uid, self.dir_id) + obj = dirobj.browse(cr, uid, self.dir_id) if obj and (obj.type == 'ressource') and not object2: raise OSError(1, 'Operation not permited.') @@ -315,11 +471,11 @@ class node_dir(node_database): 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False, 'ressource_id': object2 and object2.id or False, 'parent_id' : obj and obj.id or False - } - + } + return dirobj.create(cr, uid, val) - - + + def create_child(self, cr, path, data): """ API function to create a child file object and node Return the node_* created @@ -354,11 +510,55 @@ class node_dir(node_database): if self.write_date: wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S')) else: wtime = time.time() - return str(wtime) - + return str(wtime) + def _get_ttag(self,cr): return 'dir-%d' % self.dir_id + def move_to(self, cr, ndir_node, new_name=False, fil_obj=None, ndir_obj=None, in_write=False): + """ Move directory. This operation is simple, since the present node is + only used for static, simple directories. + Note /may/ be called with ndir_node = None, to rename the document root. + """ + if ndir_node and (ndir_node.context != self.context): + raise NotImplementedError("Cannot move directories between contexts") + + dir_obj = self.context._dirobj + if not fil_obj: + dbro = dir_obj.browse(cr, self.context.uid, self.dir_id, context=self.context.context) + else: + dbro = dir_obj + assert dbro.id == self.dir_id + + if not dbro: + raise IndexError("Cannot locate dir %d", self.dir_id) + + if (not self.parent) and ndir_node: + if not dbro.parent_id: + raise IOError(errno.EPERM, "Cannot move the root directory!") + self.parent = self.context.get_dir_node(cr, dbro.parent_id.id) + assert self.parent + + # TODO: test if parent is writable. + + if self.parent != ndir_node: + logger.debug('Cannot move dir %r from %r to %r', self, self.parent, ndir_node) + raise NotImplementedError('Cannot move dir to another dir') + + ret = {} + if new_name and (new_name != dbro.name): + ret['name'] = new_name + + del dbro + + if not in_write: + # We have to update the data ourselves + if ret: + dir_obj.write(cr, self.context.uid, [self.dir_id,], ret, self.context.context) + ret = True + + return ret + class node_res_dir(node_class): """ A special sibling to node_dir, which does only contain dynamically created folders foreach resource in the foreign model. @@ -376,26 +576,38 @@ class node_res_dir(node_class): # TODO: the write date should be MAX(file.write).. self.write_date = dirr.write_date or dirr.create_date self.content_length = 0 + self.unixperms = 040750 self.res_model = dirr.ressource_type_id and dirr.ressource_type_id.model or False self.resm_id = dirr.ressource_id self.namefield = dirr.resource_field.name or 'name' self.displayname = dirr.name - # Important: the domain is evaluated using the *parent* dctx! + # Important: the domain is evaluated using the *parent* dctx! self.domain = dirr.domain self.ressource_tree = dirr.ressource_tree # and then, we add our own vars in the dctx: if dctx: self.dctx.update(dctx) - + # and then, we prepare a dctx dict, for deferred evaluation: self.dctx_dict = {} for dfld in dirr.dctx_ids: self.dctx_dict['dctx_' + dfld.field] = dfld.expr + def __eq__(self, other): + if type(self) != type(other): + return False + if not self.context == other.context: + return False + # Two nodes, for the same document.directory, may have a + # different context! (dynamic folders) + if self.dctx != other.dctx: + return False + return self.dir_id == other.dir_id + def children(self, cr, domain=None): return self._child_get(cr, domain=domain) - def child(self,cr, name, domain=None): + def child(self,cr, name, domain=None): res = self._child_get(cr, name, domain=domain) if res: return res[0] @@ -404,7 +616,7 @@ class node_res_dir(node_class): def _child_get(self, cr, name = None, domain=None): """ return virtual children of resource, based on the foreign object. - + Note that many objects use NULL for a name, so we should better call the name_search(),name_get() set of methods """ @@ -417,31 +629,39 @@ class node_res_dir(node_class): ctx.update(self.dctx) where = [] if self.domain: - where += safe_eval(self.domain, self.dctx) + app = safe_eval(self.domain, self.dctx) + if not app: + pass + elif isinstance(app, list): + where.extend(app) + elif isinstance(app, tuple): + where.append(app) + else: + raise RuntimeError("incorrect domain expr: %s" % self.domain) if self.resm_id: where.append(('id','=',self.resm_id)) - + if name: where.append((self.namefield,'=',name)) - + # print "Where clause for %s" % self.res_model, where if self.ressource_tree: object2 = False if self.resm_id: - object2 = dirobj.pool.get(self.res_model).browse(cr, uid, self.resm_id) or False - if obj._parent_name in obj.fields_get(cr, uid): + object2 = dirobj.pool.get(self.res_model).browse(cr, uid, self.resm_id) or False + if obj._parent_name in obj.fields_get(cr, uid): where.append((obj._parent_name,'=',object2 and object2.id or False)) - + resids = obj.search(cr, uid, where, context=ctx) - res = [] + res = [] for bo in obj.browse(cr, uid, resids, context=ctx): if not bo: continue name = getattr(bo, self.namefield) if not name: continue - # Yes! we can't do better but skip nameless records. - + # Yes! we can't do better but skip nameless records. + res.append(node_res_obj(name, self.dir_id, self, self.context, self.res_model, bo)) return res @@ -458,7 +678,7 @@ class node_res_obj(node_class): def __init__(self, path, dir_id, parent, context, res_model, res_bo, res_id = None): super(node_res_obj,self).__init__(path, parent,context) assert parent - #todo: more info from dirr + #todo: more info from dirr self.dir_id = dir_id self.mimetype = 'application/x-directory' # 'httpd/unix-directory' @@ -466,17 +686,18 @@ class node_res_obj(node_class): # TODO: the write date should be MAX(file.write).. self.write_date = parent.write_date self.content_length = 0 + self.unixperms = 040750 self.res_model = res_model self.domain = parent.domain - self.displayname = path - self.dctx_dict = parent.dctx_dict - if res_bo: - self.res_id = res_bo.id + self.displayname = path + self.dctx_dict = parent.dctx_dict + if res_bo: + self.res_id = res_bo.id dc2 = self.context.context dc2.update(self.dctx) dc2['res_model'] = res_model dc2['res_id'] = res_bo.id - dc2['this'] = res_bo + dc2['this'] = res_bo for fld,expr in self.dctx_dict.items(): try: self.dctx[fld] = safe_eval(expr, dc2) @@ -487,11 +708,26 @@ class node_res_obj(node_class): else: self.res_id = res_id - def children(self, cr, domain=None): + def __eq__(self, other): + if type(self) != type(other): + return False + if not self.context == other.context: + return False + if not self.res_model == other.res_model: + return False + if not self.res_id == other.res_id: + return False + if self.domain != other.domain: + return False + if self.dctx != other.dctx: + return False + return self.dir_id == other.dir_id + + def children(self, cr, domain=None): return self._child_get(cr, domain=domain) + self._file_get(cr) - def child(self, cr, name, domain=None): - res = self._child_get(cr, name, domain=domain) + def child(self, cr, name, domain=None): + res = self._child_get(cr, name, domain=domain) if res: return res[0] res = self._file_get(cr, name) @@ -546,7 +782,7 @@ class node_res_obj(node_class): return ('vevent-collection','http://groupdav.org/') return None - def _child_get(self, cr, name=None, domain=None): + def _child_get(self, cr, name=None, domain=None): dirobj = self.context._dirobj uid = self.context.uid ctx = self.context.context.copy() @@ -554,38 +790,38 @@ class node_res_obj(node_class): directory = dirobj.browse(cr, uid, self.dir_id) obj = dirobj.pool.get(self.res_model) where = [] - res = [] + res = [] if name: - where.append(('name','=',name)) - + where.append(('name','=',name)) + # Directory Structure display in tree structure - if self.res_id and directory.ressource_tree: - where1 = [] - if obj._parent_name in obj.fields_get(cr, uid): + if self.res_id and directory.ressource_tree: + where1 = [] + if obj._parent_name in obj.fields_get(cr, uid): where1 = where + [(obj._parent_name, '=', self.res_id)] - resids = obj.search(cr, uid, where1, context=ctx) + resids = obj.search(cr, uid, where1, context=ctx) for bo in obj.browse(cr, uid, resids, context=ctx): namefield = directory.resource_field.name or 'name' if not bo: continue res_name = getattr(bo, namefield) if not res_name: - continue - res.append(node_res_obj(res_name, self.dir_id, self, self.context, self.res_model, res_bo = bo)) - - - where2 = where + [('parent_id','=',self.dir_id) ] - ids = dirobj.search(cr, uid, where2, context=ctx) + continue + res.append(node_res_obj(res_name, self.dir_id, self, self.context, self.res_model, res_bo = bo)) + + + where2 = where + [('parent_id','=',self.dir_id) ] + ids = dirobj.search(cr, uid, where2, context=ctx) for dirr in dirobj.browse(cr, uid, ids, context=ctx): if dirr.type == 'directory': res.append(node_res_obj(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id)) elif dirr.type == 'ressource': # child resources can be controlled by properly set dctx res.append(node_res_dir(dirr.name,self,self.context, dirr, {'active_id': self.res_id})) - - - + + + fil_obj = dirobj.pool.get('ir.attachment') where3 = where2 + [('res_model', '=', self.res_model), ('res_id','=',self.res_id)] # print "where clause for dir_obj", where2 @@ -593,20 +829,20 @@ class node_res_obj(node_class): if ids: for fil in fil_obj.browse(cr, uid, ids, context=ctx): res.append(node_file(fil.name, self, self.context, fil)) - - - # Get Child Ressource Directories - if directory.ressource_type_id and directory.ressource_type_id.id: - where4 = where + [('ressource_parent_type_id','=',directory.ressource_type_id.id)] + + + # Get Child Ressource Directories + if directory.ressource_type_id and directory.ressource_type_id.id: + where4 = where + [('ressource_parent_type_id','=',directory.ressource_type_id.id)] where5 = where4 + [('ressource_id','=',0)] - dirids = dirobj.search(cr,uid, where5) + dirids = dirobj.search(cr,uid, where5) where5 = where4 + [('ressource_id','=',self.res_id)] - dirids = dirids + dirobj.search(cr,uid, where5) + dirids = dirids + dirobj.search(cr,uid, where5) for dirr in dirobj.browse(cr, uid, dirids, context=ctx): if dirr.type == 'directory' and not dirr.parent_id: res.append(node_res_obj(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id)) if dirr.type == 'ressource': - res.append(node_res_dir(dirr.name, self, self.context, dirr, {'active_id': self.res_id})) + res.append(node_res_dir(dirr.name, self, self.context, dirr, {'active_id': self.res_id})) return res def create_child_collection(self, cr, objname): @@ -614,26 +850,26 @@ class node_res_obj(node_class): uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) - res_obj = dirobj.pool.get(self.context.context['res_model']) - - object2 = res_obj.browse(cr, uid, self.context.context['res_id']) or False - - obj = dirobj.browse(cr, uid, self.dir_id) + res_obj = dirobj.pool.get(self.context.context['res_model']) + + object2 = res_obj.browse(cr, uid, self.context.context['res_id']) or False + + obj = dirobj.browse(cr, uid, self.dir_id) if obj and (obj.type == 'ressource') and not object2: raise OSError(1, 'Operation not permited.') - + val = { 'name': objname, 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False, 'ressource_id': object2 and object2.id or False, 'parent_id' : False - } - if (obj and (obj.type in ('directory'))) or not object2: - val['parent_id'] = obj and obj.id or False - + } + if (obj and (obj.type in ('directory'))) or not object2: + val['parent_id'] = obj and obj.id or False + return dirobj.create(cr, uid, val) - + def create_child(self, cr, path, data): """ API function to create a child file object and node Return the node_* created @@ -655,7 +891,8 @@ class node_res_obj(node_class): fil_id = fil_obj.create(cr, uid, val, context=ctx) fil = fil_obj.browse(cr, uid, fil_id, context=ctx) fnode = node_file(path, self, self.context, fil) - fnode.set_data(cr, data, fil) + if data is not None: + fnode.set_data(cr, data, fil) return fnode def _get_ttag(self,cr): @@ -673,41 +910,50 @@ class node_file(node_class): self.write_date = fil.write_date or fil.create_date self.content_length = fil.file_size self.displayname = fil.name - + # This only propagates the problem to get_data. Better # fix those files to point to the root dir. - if fil.parent_id: - self.storage_id = fil.parent_id.storage_id.id - else: - self.storage_id = None - - def open(self, cr, mode=False): - uid = self.context.uid - if self.type in ('collection','database'): - return False - fobj = self.context._dirobj.pool.get('ir.attachment').browse(cr, uid, self.file_id, context=self.context.context) - if fobj.store_method and fobj.store_method== 'fs' : - s = StringIO.StringIO(self.get_data(cr, fobj)) - else: - s = StringIO.StringIO(base64.decodestring(fobj.db_datas or '')) - s.name = self - return s + self.storage_id = None + par = fil.parent_id + while par: + if par.storage_id and par.storage_id.id: + self.storage_id = par.storage_id.id + break + par = par.parent_id + + def __eq__(self, other): + if type(self) != type(other): + return False + if not self.context == other.context: + return False + if self.dctx != other.dctx: + return False + return self.file_id == other.file_id + + + def open_data(self, cr, mode): + stor = self.storage_id + assert stor, "No storage for file #%s" % self.file_id + # If storage is not set properly, we are just screwed here, don't + # try to get it from default. + stobj = self.context._dirobj.pool.get('document.storage') + return stobj.get_file(cr, self.context.uid, stor, self, mode=mode, context=self.context.context) def rm(self, cr): uid = self.context.uid document_obj = self.context._dirobj.pool.get('ir.attachment') if self.type in ('collection','database'): - return False - document = document_obj.browse(cr, uid, self.file_id, context=self.context.context) + return False + document = document_obj.browse(cr, uid, self.file_id, context=self.context.context) res = False if document and document._table_name == 'ir.attachment': res = document_obj.unlink(cr, uid, [document.id]) - return res + return res def fix_ppath(self, cr, fbro): """Sometimes we may init this w/o path, parent. This function fills the missing path from the file browse object - + Note: this may be an expensive operation, do on demand. However, once caching is in, we might want to do that at init time and keep this object anyway @@ -716,7 +962,7 @@ class node_file(node_class): return assert fbro uid = self.context.uid - + dirpath = [] if fbro.parent_id: dirobj = self.context._dirobj.pool.get('document.directory') @@ -725,25 +971,22 @@ class node_file(node_class): dirpath.append(fbro.datas_fname) else: dirpath.append(fbro.name) - + if len(dirpath)>1: self.path = dirpath else: - self.path = dirpath[0] - + self.path = dirpath[0] + def get_data(self, cr, fil_obj = None): - """ Retrieve the data for some file. + """ Retrieve the data for some file. fil_obj may optionally be specified, and should be a browse object for the file. This is useful when the caller has already initiated the browse object. """ # this is where storage kicks in.. stor = self.storage_id - if not stor: - data_obj = self.context._dirobj.pool.get('ir.model.data') - data_id = data_obj._get_id(cr, self.context.uid, 'document', 'storage_db') - if data_id: - stor = data_obj.browse(cr, self.context.uid, data_id, context=self.context.context).res_id - assert stor + assert stor, "No storage for file #%s" % self.file_id + # If storage is not set properly, we are just screwed here, don't + # try to get it from default. stobj = self.context._dirobj.pool.get('document.storage') return stobj.get_data(cr, self.context.uid,stor, self,self.context.context, fil_obj) @@ -755,24 +998,75 @@ class node_file(node_class): return self.content_length def set_data(self, cr, data, fil_obj = None): - """ Store data at some file. + """ Store data at some file. fil_obj may optionally be specified, and should be a browse object for the file. This is useful when the caller has already initiated the browse object. """ # this is where storage kicks in.. stor = self.storage_id - if not stor: - data_obj = self.context._dirobj.pool.get('ir.model.data') - data_id = data_obj._get_id(cr, self.context.uid, 'document', 'storage_db') - if data_id: - stor = data_obj.browse(cr, self.context.uid, data_id, context=self.context.context).res_id - assert stor + assert stor, "No storage for file #%s" % self.file_id stobj = self.context._dirobj.pool.get('document.storage') return stobj.set_data(cr, self.context.uid,stor, self, data, self.context.context, fil_obj) def _get_ttag(self,cr): return 'file-%d' % self.file_id + def move_to(self, cr, ndir_node, new_name=False, fil_obj=None, ndir_obj=None, in_write=False): + if ndir_node.context != self.context: + raise NotImplementedError("Cannot move files between contexts") + + doc_obj = self.context._dirobj.pool.get('ir.attachment') + if not fil_obj: + dbro = doc_obj.browse(cr, self.context.uid, self.file_id, context=self.context.context) + else: + dbro = fil_obj + assert dbro.id == self.file_id, "%s != %s for %r" % (dbro.id, self.file_id, self) + + if not dbro: + raise IndexError("Cannot locate doc %d", self.file_id) + + if (not self.parent): + # there *must* be a parent node for this one + self.parent = self.context.get_dir_node(cr, dbro.parent_id.id) + assert self.parent + + # TODO: test if parent is writable. + + + ret = {} + if self.parent != ndir_node: + if not (isinstance(self.parent, node_dir) and isinstance(ndir_node, node_dir)): + logger.debug('Cannot move file %r from %r to %r', self, self.parent, ndir_node) + raise NotImplementedError('Cannot move files between dynamic folders') + + if not ndir_obj: + ndir_obj = self.context._dirobj.browse(cr, self.context.uid, \ + ndir_node.dir_id, context=self.context.context) + + assert ndir_obj.id == ndir_node.dir_id + + stobj = self.context._dirobj.pool.get('document.storage') + r2 = stobj.simple_move(cr, self.context.uid, self, ndir_obj, \ + context=self.context.context) + ret.update(r2) + + if new_name and (new_name != dbro.name): + if len(ret): + raise NotImplementedError("Cannot rename and move") # TODO + stobj = self.context._dirobj.pool.get('document.storage') + r2 = stobj.simple_rename(cr, self.context.uid, self, new_name, self.context.context) + ret.update(r2) + + del dbro + + if not in_write: + # We have to update the data ourselves + if ret: + doc_obj.write(cr, self.context.uid, [self.file_id,], ret, self.context.context) + ret = True + + return ret + class node_content(node_class): our_type = 'content' def __init__(self, path, parent, context, cnt, dctx = None, act_id=None): @@ -781,6 +1075,7 @@ class node_content(node_class): self.create_date = False self.write_date = False self.content_length = False + self.unixperms = 0640 self.extension = cnt.extension self.report_id = cnt.report_id and cnt.report_id.id #self.mimetype = cnt.extension. @@ -790,22 +1085,15 @@ class node_content(node_class): self.act_id = act_id def open(self, cr, mode=False): - uid = self.context.uid - if self.type in ('collection','database'): - return False - pool = self.context._dirobj.pool - res = getattr(pool.get('document.directory.content'), 'process_read')(cr, uid, self) - res = StringIO.StringIO(res) - res.name = self - return res - + raise DeprecationWarning() + def fill_fields(self, cr, dctx = None): """ Try to read the object and fill missing fields, like mimetype, dates etc. This function must be different from the constructor, because it uses the db cursor. """ - + cr.execute('SELECT DISTINCT mimetype FROM document_directory_content_type WHERE active AND code = %s;', (self.extension,)) res = cr.fetchall() |
