From a95319ace39a4da7b2b2932b076b3582c824f99a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 25 Jun 2010 16:51:39 +0300 Subject: Document: do not allow nodes without storage. Every file must be linked to a storage, through its parent directory. If not, it is just a programming error. bzr revid: p_christ@hol.gr-20100625135139-u5r0y4fouz2i1av6 --- addons/document/nodes.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index dcd864e6db0..983ec2ac951 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -738,12 +738,9 @@ class node_file(node_class): 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) -- cgit v1.2.3 From 4fd7b06c3007b84fdbe743d42af91468df9a3ed8 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 16:56:32 +0300 Subject: Document: fix domain passing in node_res_dir Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100629135632-8d5cp645woc8g3l5 --- addons/document/nodes.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 983ec2ac951..932cab7a32d 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -417,7 +417,15 @@ 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)) -- cgit v1.2.3 From bba02abfddd63d40945a29f8ab31e0938506ad3a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 16:58:41 +0300 Subject: Document: fix the storage_id of node_file. Not all directories have a storage_id. Instead, we have to traverse the hierarchy up until we find one that has storage. bzr revid: p_christ@hol.gr-20100629135841-5myx5hyuijwv95si --- addons/document/nodes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 932cab7a32d..c28b6d07b93 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -684,10 +684,14 @@ class node_file(node_class): # 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 + 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 open(self, cr, mode=False): uid = self.context.uid -- cgit v1.2.3 From 60283953f8fb961d8c5d158bcf97be933a66ff8f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:03:03 +0300 Subject: Document: deprecate the node.open() functions These had been erroneously used by doc_ftp. The get_data() must be called instead. However, one day, a seekable object for the data should be returned. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100629140303-6lkfcz8lmvde79dt --- addons/document/nodes.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index c28b6d07b93..e59243de9d5 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -694,16 +694,7 @@ class node_file(node_class): 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 + raise DeprecationWarning("Who called this?") def rm(self, cr): uid = self.context.uid @@ -799,14 +790,7 @@ 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, -- cgit v1.2.3 From 2406e4a93d0654995bd89f44f28a4bac039a0a70 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:05:22 +0300 Subject: Document API: descriptors for data access. Trying to pass the full data of a node through get_data()/set_data() is sub-optimal and partially incompatible with some protocols. Instead, define a way to use transparent descriptors of the data. bzr revid: p_christ@hol.gr-20100629140522-8lot7kk0mm83u5c2 --- addons/document/nodes.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index e59243de9d5..d78fdc8af92 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -68,8 +68,42 @@ class node_context(object): duri = duri[1:] return ndir +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, **kwargs): + assert isinstance(parent, node_class) + self.name = parent.displayname + self.__parent = 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 @@ -123,6 +157,20 @@ class node_class(object): def get_data(self,cr): raise TypeError('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 TypeError('no data for %s' % self.type) def _get_storage(self,cr): raise RuntimeError("no storage for base class") -- cgit v1.2.3 From 075affaccccd021dd14bc2c4b7a08f3dbf7a8fa7 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:05:25 +0300 Subject: Document API: implement the node_file.open_data() bzr revid: p_christ@hol.gr-20100629140525-a54b2p51b8co2zsf --- addons/document/nodes.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index d78fdc8af92..75db03a2c1a 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -19,11 +19,11 @@ # ############################################################################## -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 @@ -741,8 +741,13 @@ class node_file(node_class): par = par.parent_id - def open(self, cr, mode=False): - raise DeprecationWarning("Who called this?") + 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 -- cgit v1.2.3 From 54f9817335817d9947372192ed17ed72b0c25805 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:05:27 +0300 Subject: Document API: don't accept extra args in node_descriptor. bzr revid: p_christ@hol.gr-20100629140527-sz2sjgrv4cjw18uk --- addons/document/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 75db03a2c1a..1c14e287118 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -82,7 +82,7 @@ class node_descriptor(object): provide the link to the parent /node/ object. """ - def __init__(self, parent, **kwargs): + def __init__(self, parent): assert isinstance(parent, node_class) self.name = parent.displayname self.__parent = parent -- cgit v1.2.3 From 11c5171326d8eba7d897843ab3182878ac3cdb8f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Draft api for document.write(parent_id: ...) If the parent directory for some document node changes, we may have a major change in the stored content, so an API is needed for that. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100701175131-ldt3b0er5a7tja0u --- addons/document/nodes.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 1c14e287118..4ed36a45024 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -209,6 +209,32 @@ class node_class(object): def get_dav_eprop(self, cr, ns, prop): return None + def move_to(self, cr, fil_obj, ndir_obj, in_write=False): + """ Move this node to a new parent directory. + fil_obj, can be None, is the browse object for the file, if already + available. + 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") -- cgit v1.2.3 From 4052a14c95a0c7019a572acbc3d5a76a264de671 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document: amend the API for the move_to(), allow rename It is better to have the same API for both move and rename. bzr revid: p_christ@hol.gr-20100701175132-qgbf7p4izkh844vg --- addons/document/nodes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 4ed36a45024..7c258ca0355 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -209,12 +209,14 @@ class node_class(object): def get_dav_eprop(self, cr, ns, prop): return None - def move_to(self, cr, fil_obj, ndir_obj, in_write=False): + def move_to(self, cr, fil_obj, ndir_obj, new_name=False, in_write=False): """ Move this node to a new parent directory. - fil_obj, can be None, is the browse object for the file, if already - available. - ndir_obj must be the browse object to the new doc.directory location, - where this node should be moved to. + @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. + @param new_name a name to rename this node to. If omitted, the old + name is preserved 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 -- cgit v1.2.3 From 4913a2f0dba67d082e225bafa97b4c576e4f8cb7 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document: fix nodefd_db.close() Some vars were missing/inaccessible, put the queries in a try block. bzr revid: p_christ@hol.gr-20100701175132-389l8frlqqg7hgr3 --- addons/document/nodes.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 7c258ca0355..1b516694aaf 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -87,6 +87,9 @@ class node_descriptor(object): self.name = parent.displayname self.__parent = parent + def _get_parent(self): + return self.__parent + def open(self, **kwargs): raise NotImplementedError -- cgit v1.2.3 From 493fe42f13f4ea99027af259462a54cd1dbddbd3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document: improve the API of move_to() bzr revid: p_christ@hol.gr-20100701175132-s9y160v1etxt1ugp --- addons/document/nodes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 1b516694aaf..14d8b16d987 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -212,14 +212,15 @@ class node_class(object): def get_dav_eprop(self, cr, ns, prop): return None - def move_to(self, cr, fil_obj, ndir_obj, new_name=False, in_write=False): + 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. - @param new_name a name to rename this node to. If omitted, the old - name is preserved 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 -- cgit v1.2.3 From a286f1ae6d7fa6e07ca44439f3c57bae7d39688a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document API: fns to get nodes from orm objects. ORM objects should not directly create nodes, but instead ask the context to provide them (because they could be cached). bzr revid: p_christ@hol.gr-20100701175132-hfnvj5czdwuaswwg --- addons/document/nodes.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 14d8b16d987..acbf4ed8dcd 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -67,6 +67,27 @@ class node_context(object): return False duri = duri[1:] return ndir + + def get_dir_node(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(fullparh, None, self, dbro) + else: + raise ValueError("dir node for %s type", dbro.type) + + def get_file_node(cr, fbro): + """ Create or locate a node for a static file + @param fbro a browse object of an ir.attachment + """ + # TODO: fill the parent + return node_file(None,None,self,fbro) + class node_descriptor(object): """A file-like interface to the data contents of a node. -- cgit v1.2.3 From 575889d33ed2932d32c67a5454758d2d00dcab7b Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document API: have equality operators for nodes, context These will be used for advanced operations (eg. move) of nodes. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100701175133-gbsdly2fwcwtr0dy --- addons/document/nodes.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index acbf4ed8dcd..e4f2a0bf615 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -57,6 +57,22 @@ 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 """ @@ -68,7 +84,7 @@ class node_context(object): duri = duri[1:] return ndir - def get_dir_node(cr, dbro): + def get_dir_node(self, cr, dbro): """Create (or locate) a node for a directory @param dbro a browse object of document.directory """ @@ -81,7 +97,7 @@ class node_context(object): else: raise ValueError("dir node for %s type", dbro.type) - def get_file_node(cr, fbro): + 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 """ @@ -152,6 +168,12 @@ class node_class(object): 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. @@ -369,7 +391,17 @@ class node_dir(node_database): print e pass - + def __eq__(self, other): + if not self.context == other.context: + return False + if type(self) != type(other): + 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): @@ -493,6 +525,17 @@ class node_res_dir(node_class): for dfld in dirr.dctx_ids: self.dctx_dict['dctx_' + dfld.field] = dfld.expr + def __eq__(self, other): + if not self.context == other.context: + return False + if type(self) != type(other): + 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) @@ -596,6 +639,21 @@ class node_res_obj(node_class): else: self.res_id = res_id + def __eq__(self, other): + if not self.context == other.context: + return False + if type(self) != type(other): + 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) @@ -793,6 +851,15 @@ class node_file(node_class): break par = par.parent_id + def __eq__(self, other): + if not self.context == other.context: + return False + if type(self) != type(other): + 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 -- cgit v1.2.3 From 1819f137e974f75cf746a672efd6942091f1179a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document: storage must be set before _set_data No attempt to use a default one. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100701175133-783ril5hg7rls246 --- addons/document/nodes.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index e4f2a0bf615..998ea7706b2 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -934,12 +934,7 @@ class node_file(node_class): 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) -- cgit v1.2.3 From 9cdbcbfee1d356ebaab68e17e75aa0bf1eb16b5d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document API: fix equality operators They must first check for the type of the 'other' side, or attribute errors may occur. bzr revid: p_christ@hol.gr-20100701175133-mjauo28qv6vkqfuq --- addons/document/nodes.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 998ea7706b2..639d0e9b9b8 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -392,10 +392,10 @@ class node_dir(node_database): pass def __eq__(self, other): - if not self.context == other.context: - return False 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: @@ -526,10 +526,10 @@ class node_res_dir(node_class): self.dctx_dict['dctx_' + dfld.field] = dfld.expr def __eq__(self, other): - if not self.context == other.context: - return False 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: @@ -640,10 +640,10 @@ class node_res_obj(node_class): self.res_id = res_id def __eq__(self, other): - if not self.context == other.context: - return False 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: @@ -852,10 +852,10 @@ class node_file(node_class): par = par.parent_id def __eq__(self, other): - if not self.context == other.context: - return False 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 -- cgit v1.2.3 From 54b9e746ac4dad02e17d98c8febf5c899baf2ab0 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document API: trivial implementation of simple file rename bzr revid: p_christ@hol.gr-20100701175133-k0gnyan62wo2gs7n --- addons/document/nodes.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 639d0e9b9b8..a77c35b4cdf 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -941,6 +941,44 @@ class node_file(node_class): 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 + + 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) + + if self.parent != ndir_node: + logger.debug('Cannot move file %r from %r to %r', self, self.parent, ndir_node) + raise NotImplementedError('Cannot move file to another dir') + + ret = {} + if new_name and (new_name != dbro.name): + 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): -- cgit v1.2.3 From a72a73acdd69ae0f21dfd3b795e9fe3111181676 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 2 Jul 2010 11:54:01 +0300 Subject: Document: remove trailing whitespace bzr revid: p_christ@hol.gr-20100702085401-8c8hwv9831jcwzts --- addons/document/nodes.py | 274 +++++++++++++++++++++++------------------------ 1 file changed, 137 insertions(+), 137 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index a77c35b4cdf..472823a2386 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 (). # @@ -15,7 +15,7 @@ # 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 . +# along with this program. If not, see . # ############################################################################## @@ -49,7 +49,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 @@ -69,21 +69,21 @@ class node_context(object): 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 @@ -107,7 +107,7 @@ class node_context(object): 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 @@ -123,13 +123,13 @@ class node_descriptor(object): 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 @@ -147,7 +147,7 @@ class node_descriptor(object): 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' @@ -167,7 +167,7 @@ class node_class(object): if parent: self.dctx = parent.dctx.copy() self.displayname = 'Object' - + def __eq__(self, other): return NotImplemented @@ -176,7 +176,7 @@ class node_class(object): 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: @@ -200,15 +200,15 @@ class node_class(object): def path_get(self): print "node_class.path_get()" return False - + def get_data(self,cr): raise TypeError('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 @@ -223,7 +223,7 @@ class node_class(object): 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) @@ -233,14 +233,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 """ @@ -260,25 +260,25 @@ class node_class(object): @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, + @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 + @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 + 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. """ @@ -295,17 +295,17 @@ 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) 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] @@ -319,7 +319,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: @@ -327,16 +327,16 @@ 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): @@ -360,7 +360,7 @@ class node_database(node_class): def _get_ttag(self,cr): return 'db-%s' % cr.dbname - + class node_dir(node_database): our_type = 'collection' @@ -396,7 +396,7 @@ class node_dir(node_database): return False if not self.context == other.context: return False - # Two directory nodes, for the same document.directory, may have a + # Two directory nodes, for the same document.directory, may have a # different context! (dynamic folders) if self.dctx != other.dctx: return False @@ -404,26 +404,26 @@ class node_dir(node_database): 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) + return super(node_dir,self)._file_get(cr, nodename, self.dir_id) + - 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.') @@ -433,12 +433,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.') @@ -448,11 +448,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 @@ -487,8 +487,8 @@ 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 @@ -513,13 +513,13 @@ class node_res_dir(node_class): 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: @@ -530,7 +530,7 @@ class node_res_dir(node_class): return False if not self.context == other.context: return False - # Two nodes, for the same document.directory, may have a + # Two nodes, for the same document.directory, may have a # different context! (dynamic folders) if self.dctx != other.dctx: return False @@ -539,7 +539,7 @@ class node_res_dir(node_class): 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] @@ -548,7 +548,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 """ @@ -572,28 +572,28 @@ class node_res_dir(node_class): 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 @@ -610,7 +610,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' @@ -620,15 +620,15 @@ class node_res_obj(node_class): self.content_length = 0 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) @@ -654,11 +654,11 @@ class node_res_obj(node_class): return False return self.dir_id == other.dir_id - def children(self, cr, domain=None): + 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) @@ -713,7 +713,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() @@ -721,38 +721,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 @@ -760,20 +760,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): @@ -781,26 +781,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 @@ -840,7 +840,7 @@ 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. self.storage_id = None @@ -873,17 +873,17 @@ class node_file(node_class): 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 @@ -892,7 +892,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') @@ -901,14 +901,14 @@ 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. """ @@ -928,7 +928,7 @@ 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. """ @@ -944,7 +944,7 @@ class node_file(node_class): 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) @@ -954,29 +954,29 @@ class node_file(node_class): 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) - + if self.parent != ndir_node: logger.debug('Cannot move file %r from %r to %r', self, self.parent, ndir_node) raise NotImplementedError('Cannot move file to another dir') - + ret = {} if new_name and (new_name != dbro.name): 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): @@ -997,14 +997,14 @@ class node_content(node_class): def open(self, cr, mode=False): 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() -- cgit v1.2.3 From 6ebec0292d26e73742cb62203b9a0ccf649d76d1 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 2 Jul 2010 12:27:32 +0300 Subject: Document API: fix node_dir._file_get() Directory _file_get was in the wrong place, causing the root folder to fail, once it was associated with dynamic context. bzr revid: p_christ@hol.gr-20100702092732-jx8tisemq7iqxtb3 --- addons/document/nodes.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 472823a2386..ca223bb9486 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -343,19 +343,8 @@ class node_database(node_class): 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): @@ -408,12 +397,21 @@ class node_dir(node_database): 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) -- cgit v1.2.3 From 3f4c75919c7b2b6a5b2a7b72a80b37adc39ec1d3 Mon Sep 17 00:00:00 2001 From: "Harry (OpenERP)" Date: Sat, 3 Jul 2010 00:50:58 +0300 Subject: Document: fix creating of node_res_obj without data . bzr revid: p_christ@hol.gr-20100702215058-5h49fw2dzox3022h --- addons/document/nodes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index ca223bb9486..d9ec0a3b607 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -820,7 +820,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): -- cgit v1.2.3 From 50070dfa133163638a0b3a48a4eb8aabd7225643 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sat, 3 Jul 2010 00:51:02 +0300 Subject: Document API: have node.get_uri(), unix permissions and user/group These attributes will help the FTP display the files correctly. bzr revid: p_christ@hol.gr-20100702215102-0yx9y2psp32ha33u --- addons/document/nodes.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index d9ec0a3b607..3599b75a15f 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -93,7 +93,7 @@ class node_context(object): return node_dir(fullpath, None ,self, dbro) elif dbro.type == 'ressource': assert dbro.ressource_parent_type_id == False - return node_res_dir(fullparh, None, self, dbro) + return node_res_dir(fullpath, None, self, dbro) else: raise ValueError("dir node for %s type", dbro.type) @@ -161,6 +161,9 @@ 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 = {} @@ -197,6 +200,16 @@ 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 @@ -300,6 +313,7 @@ class node_database(node_class): 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) @@ -365,6 +379,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 @@ -507,6 +522,7 @@ 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' @@ -616,6 +632,7 @@ 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 @@ -986,6 +1003,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. -- cgit v1.2.3 From e55d772ed1e2de9ae7d93449993a6b2e78a13e04 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 01:51:41 +0300 Subject: Document API: meaningful error, when asking for directory data content. Instead of the TypeError, which would be ignored by the FTP implementation, raise an IOError. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100708225141-opsfz22wx6c762nh --- addons/document/nodes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 3599b75a15f..a97419cb52a 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -27,6 +27,7 @@ from osv.orm import except_orm import pooler from tools.safe_eval import safe_eval +import errno import os import time @@ -215,7 +216,7 @@ class node_class(object): 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. @@ -229,7 +230,7 @@ class node_class(object): For this class, there is no data, so no implementation. Each child class that has data should override this. """ - raise TypeError('no data for %s' % self.type) + raise IOError(errno.EINVAL, 'no data for %s' % self.type) def _get_storage(self,cr): raise RuntimeError("no storage for base class") -- cgit v1.2.3 From 7cd4d1db7a6e197acf4b5584a9ada54277a8f1c6 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 01:52:29 +0300 Subject: Document API: support for simple directory rename bzr revid: p_christ@hol.gr-20100708225229-tzodkbf0h0ma080d --- addons/document/nodes.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index a97419cb52a..39347e920a9 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -506,6 +506,48 @@ class node_dir(node_database): 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): + """ 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. @@ -975,6 +1017,9 @@ class node_file(node_class): 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. if self.parent != ndir_node: logger.debug('Cannot move file %r from %r to %r', self, self.parent, ndir_node) -- cgit v1.2.3 From 3580708a49a60def7d61747274f7515047f344d3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 01:53:27 +0300 Subject: Document API: minor cleanup, better debugging of nodes. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100708225327-v1j77d8yh348dv4s --- addons/document/nodes.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 39347e920a9..bc249d5d119 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -152,6 +152,7 @@ class node_class(object): 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) @@ -192,6 +193,9 @@ class node_class(object): 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()" @@ -507,8 +511,10 @@ class node_dir(node_database): 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): - """ Note /may/ be called with ndir_node = None, to rename the document root. - """ + """ 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") @@ -547,7 +553,7 @@ class node_dir(node_database): 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. -- cgit v1.2.3 From 0d6e0a9a0286a0b240e77f47f3b8b36aaaa2b1b9 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 11:23:16 +0300 Subject: Document: support Moving of files. bzr revid: p_christ@hol.gr-20100709082316-d8qedrjabckypt88 --- addons/document/nodes.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index bc249d5d119..8899f66f1c4 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -102,8 +102,11 @@ class node_context(object): """ Create or locate a node for a static file @param fbro a browse object of an ir.attachment """ - # TODO: fill the parent - return node_file(None,None,self,fbro) + 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): @@ -190,6 +193,8 @@ class node_class(object): s = [] if isinstance(self.path,list): s+=self.path + elif path is None: + s.append('') else: s.append(self.path) return s #map(lambda x: '/' +x, s) @@ -1015,7 +1020,7 @@ class node_file(node_class): 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 + 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) @@ -1027,12 +1032,27 @@ class node_file(node_class): # TODO: test if parent is writable. - if self.parent != ndir_node: - logger.debug('Cannot move file %r from %r to %r', self, self.parent, ndir_node) - raise NotImplementedError('Cannot move file to another dir') 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) -- cgit v1.2.3 From d6f1e1f29f8110c363d8a413da8dcf17284cc205 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 11:26:45 +0300 Subject: Document API: fix typo from last commit. bzr revid: p_christ@hol.gr-20100709082645-nlkq6j9tadhnj1xy --- addons/document/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 8899f66f1c4..8988437e6aa 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -193,7 +193,7 @@ class node_class(object): s = [] if isinstance(self.path,list): s+=self.path - elif path is None: + elif self.path is None: s.append('') else: s.append(self.path) -- cgit v1.2.3 From 661a6818d89151b476c93af50be975da49e1459f Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 11 Jul 2010 17:25:27 +0300 Subject: Document API: framework for permisssion bits. The nodes may have extra permission attributes, rather than the ir.rule system. Store those bits in a 'uidperms' attribute of each node. bzr revid: p_christ@hol.gr-20100711142527-5o4v68yeuhgqvog4 --- addons/document/nodes.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 8988437e6aa..c57b3a31259 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -163,6 +163,7 @@ class node_class(object): self.context = context self.type=self.our_type self.parent = parent + self.uidperms = 5 # computed permissions for our uid, in unix bits self.mimetype = 'application/octet-stream' self.create_date = None self.write_date = None @@ -316,6 +317,33 @@ class node_class(object): def get_domain(self, cr, filters): return [] + def check_perms(self, perms): + """ Check the permissions of the current node. + + @param perms either an integers of the bits to check, or + a string with the permission letters + + Permissions of nodes are (in a unix way): + 1, x : allow descend into dir + 2, w : allow write into file, or modification to dir + 4, r : allow read of file, or listing of dir contents + 8, u : allow remove (unlink) + """ + + if isinstance(perms, str): + pe2 = 0 + chars = { 'x': 1, 'w': 2, 'r': 4, 'u': 8 } + for c in perms: + pe2 = pe2 | chars[c] + perms = pe2 + elif isinstance(perms, int): + if perms < 0 or perms > 15: + raise ValueError("Invalid permission bits") + else: + raise ValueError("Invalid permission attribute") + + return ((self.uidperms & perms) == perms) + class node_database(node_class): """ A node representing the database directory @@ -324,6 +352,7 @@ class node_database(node_class): def __init__(self, path=[], parent=False, context=None): super(node_database,self).__init__(path, parent, context) self.unixperms = 040750 + self.uidperms = 5 def children(self, cr, domain=None): res = self._child_get(cr, domain=domain) + self._file_get(cr) @@ -374,6 +403,16 @@ class node_database(node_class): def _get_ttag(self,cr): return 'db-%s' % cr.dbname +def mkdosname(company_name, default='noname'): + """ convert a string to a dos-like name""" + if not company_name: + return default + badchars = ' !@#$%^`~*()+={}[];:\'"/?.<>' + n = '' + for c in company_name[:8]: + n += (c in badchars and '_') or c + return n + class node_dir(node_database): our_type = 'collection' @@ -390,6 +429,9 @@ class node_dir(node_database): self.write_date = dirr and (dirr.write_date or dirr.create_date) or False self.content_length = 0 self.unixperms = 040750 + self.uuser = (dirr.user_id and dirr.user_id.login) or 'nobody' + self.ugroup = mkdosname(dirr.company_id and dirr.company_id.name, default='nogroup') + self.uidperms = dirr.get_dir_permissions() if dctx: self.dctx.update(dctx) dc2 = self.context.context -- cgit v1.2.3 From a6010e22a2faba53f41d22068776360ac1545c55 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 11 Jul 2010 17:26:12 +0300 Subject: Document: implement permission checks based on group_ids. Conflicts: document/nodes.py bzr revid: p_christ@hol.gr-20100711142612-2erl7ia2j9fke1e0 --- addons/document/nodes.py | 100 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 8 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index c57b3a31259..d710db37c15 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -375,6 +375,13 @@ class node_database(node_class): where = [('parent_id','=',parent_id)] if name: where.append(('name','=',name)) + is_allowed = self.check_perms(1) + else: + is_allowed = self.check_perms(5) + + if not is_allowed: + raise IOError(errno.EPERM, "Permission into directory denied") + if not domain: domain = [] @@ -459,14 +466,18 @@ class node_dir(node_database): return self.dir_id == other.dir_id def get_data(self, cr): - res = '' - for child in self.children(cr): - res += child.get_data(cr) - return res + #res = '' + #for child in self.children(cr): + # res += child.get_data(cr) + return None def _file_get(self, cr, nodename=False): res = super(node_dir,self)._file_get(cr, nodename) + is_allowed = self.check_perms(nodename and 1 or 5) + if not is_allowed: + raise IOError(errno.EPERM, "Permission into directory denied") + cntobj = self.context._dirobj.pool.get('document.directory.content') uid = self.context.uid ctx = self.context.context.copy() @@ -479,7 +490,7 @@ class node_dir(node_database): 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) @@ -489,6 +500,9 @@ class node_dir(node_database): res = False if not directory: raise OSError(2, 'Not such file or directory.') + if not self.check_perms('u'): + raise IOError(errno.EPERM,"Permission denied") + if directory._table_name=='document.directory': if self.children(cr): raise OSError(39, 'Directory not empty.') @@ -499,6 +513,9 @@ class node_dir(node_database): def create_child_collection(self, cr, objname): object2 = False + if not self.check_perms(2): + raise IOError(errno.EPERM,"Permission denied") + dirobj = self.context._dirobj uid = self.context.uid ctx = self.context.context.copy() @@ -522,6 +539,9 @@ class node_dir(node_database): """ API function to create a child file object and node Return the node_* created """ + if not self.check_perms(2): + raise IOError(errno.EPERM,"Permission denied") + dirobj = self.context._dirobj uid = self.context.uid ctx = self.context.context.copy() @@ -565,6 +585,9 @@ class node_dir(node_database): if ndir_node and (ndir_node.context != self.context): raise NotImplementedError("Cannot move directories between contexts") + if (not self.check_perms('u')) or (not ndir_node.check_perms('w')): + raise IOError(errno.EPERM,"Permission denied") + dir_obj = self.context._dirobj if not fil_obj: dbro = dir_obj.browse(cr, self.context.uid, self.dir_id, context=self.context.context) @@ -619,6 +642,9 @@ class node_res_dir(node_class): self.write_date = dirr.write_date or dirr.create_date self.content_length = 0 self.unixperms = 040750 + self.uuser = (dirr.user_id and dirr.user_id.login) or 'nobody' + self.ugroup = mkdosname(dirr.company_id and dirr.company_id.name, default='nogroup') + self.uidperms = dirr.get_dir_permissions() 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' @@ -685,6 +711,12 @@ class node_res_dir(node_class): if name: where.append((self.namefield,'=',name)) + is_allowed = self.check_perms(1) + else: + is_allowed = self.check_perms(5) + + if not is_allowed: + raise IOError(errno.EPERM,"Permission denied") # print "Where clause for %s" % self.res_model, where if self.ressource_tree: @@ -729,6 +761,9 @@ class node_res_obj(node_class): self.write_date = parent.write_date self.content_length = 0 self.unixperms = 040750 + self.uidperms = parent.uidperms & 0x15 + self.uuser = parent.uuser + self.ugroup = parent.ugroup self.res_model = res_model self.domain = parent.domain self.displayname = path @@ -779,6 +814,10 @@ class node_res_obj(node_class): def _file_get(self,cr, nodename=False): res = [] + is_allowed = self.check_perms((nodename and 1) or 5) + if not is_allowed: + raise IOError(errno.EPERM,"Permission denied") + cntobj = self.context._dirobj.pool.get('document.directory.content') uid = self.context.uid ctx = self.context.context.copy() @@ -826,6 +865,11 @@ class node_res_obj(node_class): def _child_get(self, cr, name=None, domain=None): dirobj = self.context._dirobj + + is_allowed = self.check_perms((name and 1) or 5) + if not is_allowed: + raise IOError(errno.EPERM,"Permission denied") + uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) @@ -889,6 +933,10 @@ class node_res_obj(node_class): def create_child_collection(self, cr, objname): dirobj = self.context._dirobj + is_allowed = self.check_perms(2) + if not is_allowed: + raise IOError(errno.EPERM,"Permission denied") + uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) @@ -916,6 +964,10 @@ class node_res_obj(node_class): """ API function to create a child file object and node Return the node_* created """ + is_allowed = self.check_perms(2) + if not is_allowed: + raise IOError(errno.EPERM,"Permission denied") + dirobj = self.context._dirobj uid = self.context.uid ctx = self.context.context.copy() @@ -952,6 +1004,16 @@ 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 + + self.uidperms = 14 + if parent: + if not parent.check_perms('x'): + self.uidperms = 0 + elif not parent.check_perms('w'): + self.uidperms = 4 + + self.uuser = (fil.user_id and fil.user_id.login) or 'nobody' + self.ugroup = mkdosname(fil.company_id and fil.company_id.name, default='nogroup') # This only propagates the problem to get_data. Better # fix those files to point to the root dir. @@ -976,6 +1038,9 @@ class node_file(node_class): def open_data(self, cr, mode): stor = self.storage_id assert stor, "No storage for file #%s" % self.file_id + if not self.check_perms(4): + raise IOError(errno.EPERM, "Permission denied") + # 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') @@ -983,6 +1048,8 @@ class node_file(node_class): def rm(self, cr): uid = self.context.uid + if not self.check_perms(8): + raise IOError(errno.EPERM, "Permission denied") document_obj = self.context._dirobj.pool.get('ir.attachment') if self.type in ('collection','database'): return False @@ -1027,6 +1094,9 @@ class node_file(node_class): # this is where storage kicks in.. stor = self.storage_id assert stor, "No storage for file #%s" % self.file_id + if not self.check_perms(4): + raise IOError(errno.EPERM, "Permission denied") + # 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') @@ -1047,6 +1117,9 @@ class node_file(node_class): # this is where storage kicks in.. stor = self.storage_id assert stor, "No storage for file #%s" % self.file_id + if not self.check_perms(2): + raise IOError(errno.EPERM, "Permission denied") + stobj = self.context._dirobj.pool.get('document.storage') return stobj.set_data(cr, self.context.uid,stor, self, data, self.context.context, fil_obj) @@ -1057,6 +1130,9 @@ class node_file(node_class): if ndir_node.context != self.context: raise NotImplementedError("Cannot move files between contexts") + if (not self.check_perms(8)) and ndir_node.check_perms(2): + raise IOError(errno.EPERM, "Permission denied") + 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) @@ -1072,9 +1148,6 @@ class node_file(node_class): 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)): @@ -1118,6 +1191,11 @@ class node_content(node_class): self.write_date = False self.content_length = False self.unixperms = 0640 + if parent: + self.uidperms = parent.uidperms & 14 + self.uuser = parent.uuser + self.ugroup = parent.ugroup + self.extension = cnt.extension self.report_id = cnt.report_id and cnt.report_id.id #self.mimetype = cnt.extension. @@ -1145,6 +1223,9 @@ class node_content(node_class): def get_data(self, cr, fil_obj = None): cntobj = self.context._dirobj.pool.get('document.directory.content') + if not self.check_perms(4): + raise IOError(errno.EPERM, "Permission denied") + ctx = self.context.context.copy() ctx.update(self.dctx) data = cntobj.process_read(cr, self.context.uid, self, ctx) @@ -1159,6 +1240,9 @@ class node_content(node_class): def set_data(self, cr, data, fil_obj = None): cntobj = self.context._dirobj.pool.get('document.directory.content') + if not self.check_perms(2): + raise IOError(errno.EPERM, "Permission denied") + ctx = self.context.context.copy() ctx.update(self.dctx) return cntobj.process_write(cr, self.context.uid, self, data, ctx) -- cgit v1.2.3 From 385aaa2467e8b3f355adba2c5d192a45e33b78ba Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 12 Jul 2010 22:28:08 +0300 Subject: Document API: define node_class.create_child() and collection Allow clients to call those fn()s for the wrong nodes, just to get an NotImplementedError. bzr revid: p_christ@hol.gr-20100712192808-d0x2qud1p5q26fb8 --- addons/document/nodes.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index d710db37c15..d40886846aa 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -306,7 +306,17 @@ class node_class(object): Move operations, as instructed from APIs (eg. request from DAV) could use this function. """ - raise NotImplementedError + raise NotImplementedError() + + def create_child(self, cr, path, data=None): + """ Create a regular file under this node + """ + raise NotImplementedError() + + def create_child_collection(self, cr, objname): + """ Create a child collection (directory) under self + """ + raise NotImplementedError() def rm(self, cr): raise RuntimeError("Not Implemented") -- cgit v1.2.3 From 8dc0cc8a32f7ee38e21f8e65b38cf320190ed155 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 12 Jul 2010 22:29:49 +0300 Subject: Document API: a little better messages, allow create_child() w/o data. bzr revid: p_christ@hol.gr-20100712192949-nre9wzvx6utz3b3l --- addons/document/nodes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index d40886846aa..345a104f806 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -306,23 +306,23 @@ class node_class(object): Move operations, as instructed from APIs (eg. request from DAV) could use this function. """ - raise NotImplementedError() + raise NotImplementedError(repr(self)) def create_child(self, cr, path, data=None): """ Create a regular file under this node """ - raise NotImplementedError() + raise NotImplementedError(repr(self)) def create_child_collection(self, cr, objname): """ Create a child collection (directory) under self """ - raise NotImplementedError() + raise NotImplementedError(repr(self)) def rm(self, cr): - raise RuntimeError("Not Implemented") + raise NotImplementedError(repr(self)) def rmcol(self, cr): - raise RuntimeError("Not Implemented") + raise NotImplementedError(repr(self)) def get_domain(self, cr, filters): return [] @@ -545,7 +545,7 @@ class node_dir(node_database): return dirobj.create(cr, uid, val) - def create_child(self, cr, path, data): + def create_child(self, cr, path, data=None): """ API function to create a child file object and node Return the node_* created """ @@ -970,7 +970,7 @@ class node_res_obj(node_class): return dirobj.create(cr, uid, val) - def create_child(self, cr, path, data): + def create_child(self, cr, path, data=None): """ API function to create a child file object and node Return the node_* created """ -- cgit v1.2.3 From e6cb0e4cf5c6ec58267fe6287750b5fed10700bb Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 13:27:44 +0300 Subject: Revert "Document API: meaningful error, when asking for directory data content." This reverts commit 9e6e98fbf132f1c7ef795dee9d0eb3193332a5ee. bzr revid: p_christ@hol.gr-20100713102744-rnu3vq3a2wjqmbyf --- addons/document/nodes.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 345a104f806..0669ece73aa 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -27,7 +27,6 @@ from osv.orm import except_orm import pooler from tools.safe_eval import safe_eval -import errno import os import time @@ -226,7 +225,7 @@ class node_class(object): return False def get_data(self,cr): - raise IOError(errno.EINVAL, 'no data for %s' % self.type) + raise TypeError('no data for %s'% self.type) def open_data(self, cr, mode): """ Open a node_descriptor object for this node. @@ -240,7 +239,7 @@ class node_class(object): 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) + raise TypeError('no data for %s' % self.type) def _get_storage(self,cr): raise RuntimeError("no storage for base class") -- cgit v1.2.3 From 6586f41062e8f5d521adeeacb577a4f6850e7870 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 13:28:00 +0300 Subject: Document nodes: import the errno, as in 9e6e98fbf132f1 bzr revid: p_christ@hol.gr-20100713102800-kfdmkfnvj0uxduo0 --- addons/document/nodes.py | 1 + 1 file changed, 1 insertion(+) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 0669ece73aa..c1a11459c0a 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -27,6 +27,7 @@ from osv.orm import except_orm import pooler from tools.safe_eval import safe_eval +import errno import os import time -- cgit v1.2.3 From 2d3d861a3e4317a71fb58307ac9d8a2866c6999c Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 13:29:58 +0300 Subject: Document directory: "find_all" field for resource dirs. A (mis-)feature of the previous code was that in resource directories, it would only list attachments that would have those dirs as parents. This means that we couldn't have another categorisation, that would yield some of the same attachments. bzr revid: p_christ@hol.gr-20100713102958-nk40nckc41uxyzri --- addons/document/nodes.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index c1a11459c0a..0def3995d91 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -657,6 +657,7 @@ class node_res_dir(node_class): self.uidperms = dirr.get_dir_permissions() self.res_model = dirr.ressource_type_id and dirr.ressource_type_id.model or False self.resm_id = dirr.ressource_id + self.res_find_all = dirr.resource_find_all self.namefield = dirr.resource_field.name or 'name' self.displayname = dirr.name # Important: the domain is evaluated using the *parent* dctx! @@ -771,13 +772,14 @@ class node_res_obj(node_class): self.write_date = parent.write_date self.content_length = 0 self.unixperms = 040750 - self.uidperms = parent.uidperms & 0x15 + self.uidperms = parent.uidperms & 15 self.uuser = parent.uuser self.ugroup = parent.ugroup self.res_model = res_model self.domain = parent.domain self.displayname = path self.dctx_dict = parent.dctx_dict + self.res_find_all = parent.res_find_all if res_bo: self.res_id = res_bo.id dc2 = self.context.context @@ -806,6 +808,8 @@ class node_res_obj(node_class): return False if self.domain != other.domain: return False + if self.res_find_all != other.res_find_all: + return False if self.dctx != other.dctx: return False return self.dir_id == other.dir_id @@ -919,6 +923,8 @@ class node_res_obj(node_class): fil_obj = dirobj.pool.get('ir.attachment') + if self.res_find_all: + where2 = where where3 = where2 + [('res_model', '=', self.res_model), ('res_id','=',self.res_id)] # print "where clause for dir_obj", where2 ids = fil_obj.search(cr, uid, where3, context=ctx) @@ -986,11 +992,12 @@ class node_res_obj(node_class): val = { 'name': path, 'datas_fname': path, - 'parent_id': self.dir_id, 'res_model': self.res_model, 'res_id': self.res_id, # Datas are not set here } + if not self.res_find_all: + val['parent_id'] = self.dir_id fil_id = fil_obj.create(cr, uid, val, context=ctx) fil = fil_obj.browse(cr, uid, fil_id, context=ctx) -- cgit v1.2.3 From a7eaf3bfc955fee567f858f570a09eaadf38ee1b Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 13:30:30 +0300 Subject: Document: fix the context logic of content nodes Content nodes should have meaningful context and perhaps an active id 'act_id', which will be used in the report or other generation fn(). The dctx mechanism of nodes was built for that, use it. bzr revid: p_christ@hol.gr-20100713103030-irueg85zizlp42ju --- addons/document/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 0def3995d91..58d830a169a 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -782,7 +782,7 @@ class node_res_obj(node_class): self.res_find_all = parent.res_find_all if res_bo: self.res_id = res_bo.id - dc2 = self.context.context + dc2 = self.context.context.copy() dc2.update(self.dctx) dc2['res_model'] = res_model dc2['res_id'] = res_bo.id -- cgit v1.2.3 From 6eebca04d623827763cd328616d3cb133f151ce9 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 13:30:53 +0300 Subject: Document API: node_content.open_data(), for FTP A crude way to get the data of a content node, using StringIO. bzr revid: p_christ@hol.gr-20100713103053-hklth9rugddm6ajl --- addons/document/nodes.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 58d830a169a..e368c6fc5a0 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -31,6 +31,8 @@ import errno import os import time +from StringIO import StringIO + # # An object that represent an uri # path: the uri of the object @@ -1221,9 +1223,6 @@ class node_content(node_class): self.dctx.update(dctx) self.act_id = act_id - def open(self, cr, mode=False): - raise DeprecationWarning() - def fill_fields(self, cr, dctx = None): """ Try to read the object and fill missing fields, like mimetype, dates etc. @@ -1250,7 +1249,29 @@ class node_content(node_class): self.content_length = len(data) return data + def open_data(self, cr, mode): + if mode.endswith('b'): + mode = mode[:-1] + if mode in ('r', 'w'): + cperms = mode[:1] + elif mode in ('r+', 'w+'): + cperms = 'rw' + else: + raise IOError(errno.EINVAL, "Cannot open at mode %s" % mode) + + if not self.check_perms(cperms): + raise IOError(errno.EPERM, "Permission denied") + + ctx = self.context.context.copy() + ctx.update(self.dctx) + + return nodefd_content(self, cr, mode, ctx) + def get_data_len(self, cr, fil_obj = None): + # FIXME : here, we actually generate the content twice!! + # we should have cached the generated content, but it is + # not advisable to do keep it in memory, until we have a cache + # expiration logic. if not self.content_length: self.get_data(cr,fil_obj) return self.content_length @@ -1266,3 +1287,53 @@ class node_content(node_class): def _get_ttag(self,cr): return 'cnt-%d%s' % (self.cnt_id,(self.act_id and ('-' + str(self.act_id))) or '') + + +class nodefd_content(StringIO, node_descriptor): + """ A descriptor to content nodes + """ + def __init__(self, parent, cr, mode, ctx): + node_descriptor.__init__(self, parent) + self._context=ctx + + if mode in ('r', 'r+'): + cntobj = parent.context._dirobj.pool.get('document.directory.content') + data = cntobj.process_read(cr, parent.context.uid, parent, ctx) + if data: + parent.content_length = len(data) + StringIO.__init__(self, data) + elif mode in ('w', 'w+'): + StringIO.__init__(self, None) + # at write, we start at 0 (= overwrite), but have the original + # data available, in case of a seek() + elif mode == 'a': + StringIO.__init__(self, None) + else: + logging.getLogger('document.content').error("Incorrect mode %s specified", mode) + raise IOError(errno.EINVAL, "Invalid file mode") + self.mode = mode + + def close(self): + # we now open a *separate* cursor, to update the data. + # FIXME: this may be improved, for concurrency handling + if self.mode == 'r': + StringIO.close(self) + return + + par = self._get_parent() + uid = par.context.uid + cr = pooler.get_db(par.context.dbname).cursor() + try: + if self.mode in ('w', 'w+', 'r+'): + data = self.getvalue() + cntobj = par.context._dirobj.pool.get('document.directory.content') + cntobj.process_write(cr, uid, parent, data, ctx) + elif self.mode == 'a': + raise NotImplementedError + cr.commit() + except Exception, e: + logging.getLogger('document.content').exception('Cannot update db content #%d for close:', par.cnt_id) + raise + finally: + cr.close() + StringIO.close(self) -- cgit v1.2.3 From 766fd336d4f8f0e17a05741542bf0a6e440e06bc Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 13 Jul 2010 23:53:59 +0300 Subject: Document: fix realstore with open/close, rename. Put the path computation of realstore in a helper function, and use that in all places where a file is opened/created/moved. bzr revid: p_christ@hol.gr-20100713205359-jn0bo6fcw9lwdyge --- addons/document/nodes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index e368c6fc5a0..594f200152b 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -631,7 +631,9 @@ class node_dir(node_database): 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) + ctx = self.context.context.copy() + ctx['__from_node'] = True + dir_obj.write(cr, self.context.uid, [self.dir_id,], ret, ctx) ret = True return ret @@ -1196,7 +1198,9 @@ class node_file(node_class): 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) + ctx = self.context.context.copy() + ctx['__from_node'] = True + doc_obj.write(cr, self.context.uid, [self.file_id,], ret, ctx ) ret = True return ret -- cgit v1.2.3 From 6cb5fa01edee3434f59654c2bf6365586702830b Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 14 Jul 2010 15:52:32 +0300 Subject: Document, FTP: handle case of move into existing name. Unlike unix 'mv', we do not allow move to point to an existing 'new_name'. bzr revid: p_christ@hol.gr-20100714125232-fmm9sm1nc30nz92k --- addons/document/nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'addons/document/nodes.py') diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 594f200152b..f1ac35c333c 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -615,8 +615,6 @@ class node_dir(node_database): 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) @@ -624,6 +622,8 @@ class node_dir(node_database): ret = {} if new_name and (new_name != dbro.name): + if ndir_node.child(cr, new_name): + raise IOError(errno.EEXIST, "Destination path already exists") ret['name'] = new_name del dbro -- cgit v1.2.3