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') 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 6eef5d7e360353c60a56d0b1ab4c3484f80059d8 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:09 +0300 Subject: Document: rearrange columns definition of document.file Note: *must* use the server as patched, for the 'url' and 'type' columns. bzr revid: p_christ@hol.gr-20100627201809-p5wy2w368gqoyy3h --- addons/document/document.py | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index aa893b617c3..c5315bf0574 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -72,33 +72,32 @@ class document_file(osv.osv): return res _columns = { + # Columns from ir.attachment: + 'create_date': fields.datetime('Date Created', readonly=True), + 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), + 'write_date': fields.datetime('Date Modified', readonly=True), + 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), + 'res_model': fields.char('Attached Model', size=64), # readonly? + 'res_id': fields.integer('Attached ID'), + + # If ir.attachment contained any data before document is installed, preserve + # the data, don't drop the column! + 'db_datas': fields.binary('Data', oldname='datas'), + 'datas': fields.function(_data_get, method=True, fnct_inv=_data_set, string='File Content', type="binary", nodrop=True), + + # Fields of document: 'user_id': fields.many2one('res.users', 'Owner', select=1), 'group_ids': fields.many2many('res.groups', 'document_group_rel', 'item_id', 'group_id', 'Groups'), # the directory id now is mandatory. It can still be computed automatically. 'parent_id': fields.many2one('document.directory', 'Directory', select=1), - 'file_size': fields.integer('File Size', required=True), - 'file_type': fields.char('Content Type', size=128), - # If ir.attachment contained any data before document is installed, preserve - # the data, don't drop the column! - 'db_datas': fields.binary('Data', oldname='datas'), 'index_content': fields.text('Indexed Content'), - 'write_date': fields.datetime('Date Modified', readonly=True), - 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), - 'create_date': fields.datetime('Date Created', readonly=True), - 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), - 'store_method': fields.selection([('db', 'Database'), ('fs', 'Filesystem'), ('link', 'Link')], "Storing Method"), - 'datas': fields.function(_data_get, method=True, fnct_inv=_data_set, string='File Content', type="binary", nodrop=True), - 'url': fields.char('File URL',size=64), - 'store_fname': fields.char('Stored Filename', size=200), - 'res_model': fields.char('Attached Model', size=64), #res_model - 'res_id': fields.integer('Attached ID'), #res_id 'partner_id':fields.many2one('res.partner', 'Partner', select=1), - 'type':fields.selection([ - ('url','URL'), - ('binary','Binary'), - - ],'Type', help="Type is used to separate URL and binary File"), 'company_id': fields.many2one('res.company', 'Company'), + 'file_size': fields.integer('File Size', required=True), + 'file_type': fields.char('Content Type', size=128), + + # fields used for file storage + 'store_fname': fields.char('Stored Filename', size=200), } def __get_def_directory(self, cr, uid, context=None): @@ -109,8 +108,6 @@ class document_file(osv.osv): 'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'ir.attachment', context=c), 'user_id': lambda self, cr, uid, ctx:uid, 'file_size': lambda self, cr, uid, ctx:0, - 'store_method': lambda *args: 'db', - 'type': 'binary', 'parent_id': __get_def_directory } _sql_constraints = [ -- cgit v1.2.3 From c11ad0047d1c6da4592c161e2e4a1ed6a5ad07c2 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:13 +0300 Subject: Document: files are required to have a parent directory Or else, there is no storage for them. bzr revid: p_christ@hol.gr-20100627201813-y5gatb8w39jnxeux --- addons/document/document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index c5315bf0574..dd48f7daf7f 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -89,7 +89,7 @@ class document_file(osv.osv): 'user_id': fields.many2one('res.users', 'Owner', select=1), 'group_ids': fields.many2many('res.groups', 'document_group_rel', 'item_id', 'group_id', 'Groups'), # the directory id now is mandatory. It can still be computed automatically. - 'parent_id': fields.many2one('document.directory', 'Directory', select=1), + 'parent_id': fields.many2one('document.directory', 'Directory', select=1, required=True), 'index_content': fields.text('Indexed Content'), 'partner_id':fields.many2one('res.partner', 'Partner', select=1), 'company_id': fields.many2one('res.company', 'Company'), -- cgit v1.2.3 From 823dc6d50d2a158c0c7428aa09e18a0a0d781a45 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:18 +0300 Subject: Document directory: cleanup fields, type, constraints. bzr revid: p_christ@hol.gr-20100627201818-wjlumw10u8g28g27 --- addons/document/document_directory.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index fee633b7169..d83d1b6b60e 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -39,16 +39,21 @@ class document_directory(osv.osv): 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), 'create_date': fields.datetime('Date Created', readonly=True), 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), - 'file_type': fields.char('Content Type', size=32), 'domain': fields.char('Domain', size=128, help="Use a domain if you want to apply an automatic filter on visible resources."), 'user_id': fields.many2one('res.users', 'Owner'), 'storage_id': fields.many2one('document.storage', 'Storage'), 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), - 'parent_id': fields.many2one('document.directory', 'Parent Item'), + 'parent_id': fields.many2one('document.directory', 'Parent Item', select=1), 'child_ids': fields.one2many('document.directory', 'parent_id', 'Children'), 'file_ids': fields.one2many('ir.attachment', 'parent_id', 'Files'), 'content_ids': fields.one2many('document.directory.content', 'directory_id', 'Virtual Files'), - 'type': fields.selection([('directory','Static Directory'),('ressource','Other Resources')], 'Type', required=True), + 'type': fields.selection([ + ('directory','Static Directory'), + ('ressource','Folders per resource'), + ('at_record','Single folder under resource')], + 'Type', required=True, select=1, + help="Defines directory's behaviour."), + 'ressource_type_id': fields.many2one('ir.model', 'Directories Mapped to Objects', help="Select an object here and Open ERP will create a mapping for each of these " \ "objects, using the given domain, when browsing through FTP."), @@ -60,7 +65,7 @@ class document_directory(osv.osv): 'ressource_tree': fields.boolean('Tree Structure', help="Check this if you want to use the same tree structure as the object selected in the system."), 'dctx_ids': fields.one2many('document.directory.dctx', 'dir_id', 'Context fields'), - 'company_id': fields.many2one('res.company', 'Company'), + 'company_id': fields.many2one('res.company', 'Company'), } @@ -102,7 +107,8 @@ class document_directory(osv.osv): } _sql_constraints = [ ('dirname_uniq', 'unique (name,parent_id,ressource_id,ressource_parent_type_id)', 'The directory name must be unique !'), - ('no_selfparent', 'check(parent_id <> id)', 'Directory cannot be parent of itself!') + ('no_selfparent', 'check(parent_id <> id)', 'Directory cannot be parent of itself!'), + ('dir_parented', 'check(parent_id IS NOT NULL OR storage_id IS NOT NULL)', 'Directory must have a parent or a storage') ] def name_get(self, cr, uid, ids, context={}): res = [] -- cgit v1.2.3 From 07d395df15c22037d64f0d7dceeec4d1328ce999 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:26 +0300 Subject: Document: cleanup the view (and help) of directory. bzr revid: p_christ@hol.gr-20100627201826-1873sa0et2q8vx62 --- addons/document/document_directory.py | 5 ++--- addons/document/document_view.xml | 29 ++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index d83d1b6b60e..2872491129d 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -54,9 +54,8 @@ class document_directory(osv.osv): 'Type', required=True, select=1, help="Defines directory's behaviour."), - 'ressource_type_id': fields.many2one('ir.model', 'Directories Mapped to Objects', - help="Select an object here and Open ERP will create a mapping for each of these " \ - "objects, using the given domain, when browsing through FTP."), + 'ressource_type_id': fields.many2one('ir.model', 'Resource model', + help="Select an object here and there will be one folder per record of that resource."), 'resource_field': fields.many2one('ir.model.fields', 'Name field', help='Field to be used as name on resource directories. If empty, the "name" will be used.'), 'ressource_parent_type_id': fields.many2one('ir.model', 'Parent Model', help="If you put an object here, this directory template will appear bellow all of these objects. " \ diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 714c6d2c107..36e89074d94 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -75,23 +75,31 @@
- + - - - - - - - + + + + + + + + + + + + + + - + + + -- cgit v1.2.3 From d885f0c85170dccda304173cdfc4393db0d54b45 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:35 +0300 Subject: Document: expand tabs at view, one wording improvement. bzr revid: p_christ@hol.gr-20100627201835-c3qzi1w0e8yxcc07 --- addons/document/document_view.xml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 36e89074d94..6f79188622d 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -84,19 +84,19 @@ - - - - + + + + - - - + + + - + @@ -121,7 +121,7 @@ - - -- cgit v1.2.3 From 653ed555b23db5aa4ec279c0a0d7e24332eec80a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:46 +0300 Subject: Document UI: improve the ir.attachment view for docs. Try to respect the layout of the bare ir.attachment view, simplify. bzr revid: p_christ@hol.gr-20100627201846-99a2ptcmnl6e1o62 --- addons/document/document_view.xml | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 6f79188622d..e088e51de3b 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -229,44 +229,44 @@ - - + + + + + - - + + - + - - - - + + + + + + - - - - - - - - - - + + + + + + + + + - - - - -- cgit v1.2.3 From daee2860fcff75ebfb9a993669440b80405221ed Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:49 +0300 Subject: Document: set the storage only for the root folder It is not needed to set the default storage for children folders, and that would break setups where the admin has overriden that storage. bzr revid: p_christ@hol.gr-20100627201849-uzemo5lcgytzmbnu --- addons/document/document_data.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_data.xml b/addons/document/document_data.xml index c2bef5de34f..b93ed4d7089 100644 --- a/addons/document/document_data.xml +++ b/addons/document/document_data.xml @@ -17,7 +17,7 @@ Documents - + 0 @@ -25,7 +25,6 @@ My Folder - 0 @@ -35,7 +34,6 @@ ressource 1 - 0 @@ -57,7 +55,6 @@ Personal Folders ressource - 0 -- cgit v1.2.3 From 85dc0b2fc6b86e54ad8449ff0da5e96550009b77 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:18:57 +0300 Subject: Document: remove the ir.attachment.group_ids column At the review, it was decided that per document permissions is too much. bzr revid: p_christ@hol.gr-20100627201857-e0sw2sun1ttvnj32 --- addons/document/document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index dd48f7daf7f..ec6c159f2b7 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -87,7 +87,7 @@ class document_file(osv.osv): # Fields of document: 'user_id': fields.many2one('res.users', 'Owner', select=1), - 'group_ids': fields.many2many('res.groups', 'document_group_rel', 'item_id', 'group_id', 'Groups'), + # 'group_ids': fields.many2many('res.groups', 'document_group_rel', 'item_id', 'group_id', 'Groups'), # the directory id now is mandatory. It can still be computed automatically. 'parent_id': fields.many2one('document.directory', 'Directory', select=1, required=True), 'index_content': fields.text('Indexed Content'), -- cgit v1.2.3 From e91d089e5b159f9a6e8767a408086306bd6170fc Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Sun, 27 Jun 2010 23:19:01 +0300 Subject: Document UI: revise the ir.attachment search, tree forms bzr revid: p_christ@hol.gr-20100627201901-op4tjd1vknnuq3el --- addons/document/document_view.xml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index e088e51de3b..3e14c09f8d8 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -285,11 +285,13 @@ search - + + + context="{'group_by':'partner_id'}" groups="base.group_extended"/> - + @@ -317,11 +319,12 @@ - - + + + -- cgit v1.2.3 From ef0272dd240c68ac9032b32c2c1caf25859470b5 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 28 Jun 2010 00:08:58 +0300 Subject: Document: fix context expr. that broke the Dashboard bzr revid: p_christ@hol.gr-20100627210858-9y9ncl76g1e4wjyy --- addons/document/report/document_report_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/report/document_report_view.xml b/addons/document/report/document_report_view.xml index 00743d768e7..630e3bb9f84 100644 --- a/addons/document/report/document_report_view.xml +++ b/addons/document/report/document_report_view.xml @@ -61,7 +61,7 @@ report.document.user form tree - {"search_default_user":uid} + {'search_default_user': 'user_id'} -- cgit v1.2.3 From 38a7613cb344fccbceb0f69b3077a969ea74695d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 28 Jun 2010 00:09:01 +0300 Subject: Document: expand tabs at board xml. bzr revid: p_christ@hol.gr-20100627210901-klb7bu4ojnhrpzbx --- addons/document/report/document_report_view.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'addons/document') diff --git a/addons/document/report/document_report_view.xml b/addons/document/report/document_report_view.xml index 630e3bb9f84..d0ba1303a39 100644 --- a/addons/document/report/document_report_view.xml +++ b/addons/document/report/document_report_view.xml @@ -33,22 +33,22 @@ - + report.document.user.search report.document.user search - + - - - - - - - - + + + + + + + + -- cgit v1.2.3 From e908e8cd75932ff2523c5fa5a78501613cb55804 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Mon, 28 Jun 2010 00:09:04 +0300 Subject: Document board: bring the menus inside the 'knowledge' They should not be under the general 'Dashboards' menu. bzr revid: p_christ@hol.gr-20100627210904-1397ab657saukag9 --- addons/document/board_document_view.xml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'addons/document') diff --git a/addons/document/board_document_view.xml b/addons/document/board_document_view.xml index 4d89f96cbfb..312c79d71ab 100644 --- a/addons/document/board_document_view.xml +++ b/addons/document/board_document_view.xml @@ -50,19 +50,18 @@ - - @@ -105,10 +104,10 @@ -- 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') 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') 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 694753824bdecab4fa8ae3b0df3e25379319d234 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:00:26 +0300 Subject: Document UI: rename the default storage entry. bzr revid: p_christ@hol.gr-20100629140026-01pahx5ue1ycaf3g --- addons/document/document_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document_data.xml b/addons/document/document_data.xml index b93ed4d7089..f13213034e8 100644 --- a/addons/document/document_data.xml +++ b/addons/document/document_data.xml @@ -9,7 +9,7 @@ - Default DB storage + Database storage db -- cgit v1.2.3 From e068962cca72f8936f34e2f81555ef93214e05fc Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:00:29 +0300 Subject: Document UI: rename the parent_id of a dir . bzr revid: p_christ@hol.gr-20100629140029-fnx876jkkezchc3j --- addons/document/document_directory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index 2872491129d..a464ca61247 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -43,7 +43,7 @@ class document_directory(osv.osv): 'user_id': fields.many2one('res.users', 'Owner'), 'storage_id': fields.many2one('document.storage', 'Storage'), 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), - 'parent_id': fields.many2one('document.directory', 'Parent Item', select=1), + 'parent_id': fields.many2one('document.directory', 'Parent Directory', select=1), 'child_ids': fields.one2many('document.directory', 'parent_id', 'Children'), 'file_ids': fields.one2many('ir.attachment', 'parent_id', 'Files'), 'content_ids': fields.one2many('document.directory.content', 'directory_id', 'Virtual Files'), -- cgit v1.2.3 From cf0e7cbfc6259e2ea23610d5a4e7911d28f6a6cb Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:00:32 +0300 Subject: Document UI: Revise document, directory forms as reviewed bzr revid: p_christ@hol.gr-20100629140032-kq39g7q5ef27xdty --- addons/document/document_view.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 3a178686561..7cbd01ad3eb 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -66,6 +66,7 @@ name="Storage Media" action="action_document_storage_form" id="menu_document_storage_media" + groups="base.group_extended" parent="menu_document_management_configuration"/> @@ -73,10 +74,10 @@ document.directory form - + - + @@ -250,10 +251,12 @@ - - - + + + + + -- cgit v1.2.3 From 514f433c59e6778afad469a55d032e1e3da435a0 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:00:40 +0300 Subject: Document: ir.attachment.res_model and res_id should be readonly for UI Changing them would require a wizard or something. bzr revid: p_christ@hol.gr-20100629140040-am61qng5pxfz2dxs --- addons/document/document.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index ec6c159f2b7..fb69b84e6a5 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -77,8 +77,8 @@ class document_file(osv.osv): 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), 'write_date': fields.datetime('Date Modified', readonly=True), 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), - 'res_model': fields.char('Attached Model', size=64), # readonly? - 'res_id': fields.integer('Attached ID'), + 'res_model': fields.char('Attached Model', size=64, readonly=True), + 'res_id': fields.integer('Attached ID', readonly=True), # If ir.attachment contained any data before document is installed, preserve # the data, don't drop the column! -- cgit v1.2.3 From 4d99b09045d55fc56bab863c7cbfbfe8145b25c0 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:00:49 +0300 Subject: Document, FTP: text skeleton of trivial yaml tests. bzr revid: p_christ@hol.gr-20100629140049-dowvybyjh6bncyrb --- addons/document/test/document_test2.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 addons/document/test/document_test2.yml (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml new file mode 100644 index 00000000000..5cd6547e687 --- /dev/null +++ b/addons/document/test/document_test2.yml @@ -0,0 +1,24 @@ +- + In order to test the document management +- + I make sure the default installation has some storage and root directory. +- + I create a "Testing" folder where all the test data will go. +- + I create an attachment into the root folder (w. empty fields, test that + defaults work) +- + I delete the attachment from the root folder +- + I create an attachment into the Testing folder. +- + I update the attachment with data, namely "abcd" +- + I rename the attachment. +- + I search the testing folder for attachments. +- + I create an attachment to a 3rd resource, eg. a res.country +- + I search for the res.country attachment +- -- 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') 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 74d60c861f8a8ea7bc04a1dd135db699a94bdbc3 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:05:19 +0300 Subject: Document: the node context shouldn't contain 'lang' Node context should be as neutral as possible, in order to help caching. bzr revid: p_christ@hol.gr-20100629140519-fgxcr2wm5ctg8ywl --- addons/document/document_directory.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index a464ca61247..1b62418106c 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -195,17 +195,8 @@ class document_directory(osv.osv): """ if not context: context = {} - lang = context.get('lang',False) - if not lang: - user = self.pool.get('res.users').browse(cr, uid, uid) - lang = user.context_lang - context['lang'] = lang - try: #just instrumentation - return nodes.get_node_context(cr, uid, context).get_uri(cr, uri) - except Exception,e: - print "exception: ",e - raise + return nodes.get_node_context(cr, uid, context).get_uri(cr, uri) def _locate_child(self, cr, uid, root_id, uri,nparent, ncontext): -- 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') 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') 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') 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 f3cb7cd41539de507f800f10826f003017a40c8a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 29 Jun 2010 17:05:30 +0300 Subject: Document storage: implement node_descriptors. bzr revid: p_christ@hol.gr-20100629140530-btusqmlz2lyadfbx --- addons/document/document_storage.py | 102 ++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 90be2bc47df..b2db4eb36a6 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -24,7 +24,9 @@ from osv import osv, fields import os import tools import base64 +import errno import logging +from StringIO import StringIO from tools.misc import ustr from tools.translate import _ @@ -34,6 +36,7 @@ from osv.orm import except_orm import random import string import netsvc +import nodes from content_index import cntIndex DMS_ROOT_PATH = tools.config.get('document_path', os.path.join(tools.config.get('root_path'), 'filestore')) @@ -84,6 +87,64 @@ def create_directory(path): os.makedirs(path) return dir_name +class nodefd_file(nodes.node_descriptor): + """ A descriptor to a real file + + Inheriting directly from file doesn't work, since file exports + some read-only attributes (like 'name') that we don't like. + """ + def __init__(self, parent, path, mode): + nodes.node_descriptor.__init__(self, parent) + self.__file = open(path, mode) + + for attr in ('closed', 'read', 'write', 'seek', 'tell'): + setattr(self,attr, getattr(self.__file, attr)) + + def close(self): + # TODO: locking in init, close() + self.__file.close() + + +class nodefd_db(StringIO, nodes.node_descriptor): + """ A descriptor to db data + """ + def __init__(self, parent, ira_browse, mode): + nodes.node_descriptor.__init__(self, parent) + if mode.endswith('b'): + mode = mode[:-1] + + if mode == 'r': + StringIO.__init__(self, ira_browse.db_datas) + elif mode == 'w': + StringIO.__init__(self, ira_browse.db_datas) + # 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.storage').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 + uid = self.__parent.context.uid + cr = pooler.get_db(self.__parent.context.dbname).cursor() + if mode == 'w': + out = self.getvalue() + cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s', + (out, len(out), self.__parent.file_id)) + elif mode == 'a': + out = self.getvalue() + cr.execute("UPDATE ir_attachment " \ + "SET db_datas = COALESCE(db_datas,'') || %s, " \ + " file_size = COALESCE(file_size, 0) + %d " \ + " WHERE id = %s", + (out, len(out), self.__parent.file_id)) + cr.commit() + cr.close() + StringIO.close(self) class document_storage(osv.osv): """ The primary object for data storage. @@ -147,6 +208,47 @@ class document_storage(osv.osv): ira = self.pool.get('ir.attachment').browse(cr, uid, file_node.file_id, context=context) return self.__get_data_3(cr, uid, boo, ira, context) + def get_file(self, cr, uid, id, file_node, mode, context=None): + if context is None: + context = {} + boo = self.browse(cr, uid, id, context) + if not boo.online: + raise RuntimeError('media offline') + + ira = self.pool.get('ir.attachment').browse(cr, uid, file_node.file_id, context=context) + if boo.type == 'filestore': + if not ira.store_fname: + # On a migrated db, some files may have the wrong storage type + # try to fix their directory. + if ira.file_size: + self._doclog.warning( "ir.attachment #%d does not have a filename, but is at filestore, fix it!" % ira.id) + raise IOError(errno.ENOENT, 'No file can be located') + fpath = os.path.join(boo.path, ira.store_fname) + if self._debug: + self._doclog.debug("Trying to read \"%s\".."% fpath) + return nodefd_file(file_node, path=fpath, mode=mode) + + elif boo.type == 'db': + # TODO: we need a better api for large files + if self._debug: + self._doclog.debug("Trying to obtain db_datas for ir.attachment[%d]", ira.id) + return nodefd_db(file_node, ira_browse=ira, mode=mode) + + elif boo.type == 'realstore': + if not ira.store_fname: + # On a migrated db, some files may have the wrong storage type + # try to fix their directory. + if ira.file_size: + self._doclog.warning("ir.attachment #%d does not have a filename, trying the name." %ira.id) + sfname = ira.name + fpath = os.path.join(boo.path,ira.store_fname or ira.name) + if not os.path.exists(fpath): + raise IOError("File not found: %s" % fpath) + return nodefd_file(file_node, path=fpath, mode=mode) + + else: + raise TypeError("No %s storage" % boo.type) + def __get_data_3(self, cr, uid, boo, ira, context): if not boo.online: raise RuntimeError('media offline') -- cgit v1.2.3 From 3f4d6dd95dc134db7d5b637f78584588d7bbf0eb Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:30 +0300 Subject: Document: fix menu parents (after root moved to knowledge) bzr revid: p_christ@hol.gr-20100701175130-5ya6cxp5s4xb3gyi --- addons/document/board_document_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/board_document_view.xml b/addons/document/board_document_view.xml index 312c79d71ab..c8b155f5a4d 100644 --- a/addons/document/board_document_view.xml +++ b/addons/document/board_document_view.xml @@ -52,7 +52,7 @@ -- cgit v1.2.3 From ad1253c96bb0f36c9ad50cce959cc5e1f1e3e07a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:30 +0300 Subject: Document: start some trivial tests. bzr revid: p_christ@hol.gr-20100701175130-bs2rx4w81ck6gh9l --- addons/document/test/document_test2.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index 5cd6547e687..e386db760c4 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -2,8 +2,18 @@ In order to test the document management - I make sure the default installation has some storage and root directory. +- + !assert {model: document.storage, id: storage_default }: + - id != False +- + !assert {model: document.directory, id: dir_root}: + - storage_id != False - I create a "Testing" folder where all the test data will go. +- + !record {model: document.directory, id: dir_tests }: + name: 'Testing' + parent_id: dir_root - I create an attachment into the root folder (w. empty fields, test that defaults work) -- cgit v1.2.3 From 6d64f88d5536ae14026556729433ea27b5eece61 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Document: fix content indexer, where no filename is supplied bzr revid: p_christ@hol.gr-20100701175131-pbp7lsswuh8muyou --- addons/document/content_index.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/content_index.py b/addons/document/content_index.py index e7f88834647..d6012e60091 100644 --- a/addons/document/content_index.py +++ b/addons/document/content_index.py @@ -153,7 +153,10 @@ class contentIndex(object): if realfname : fname = realfname else: - bname,ext = os.path.splitext(filename) + try: + bname,ext = os.path.splitext(filename or 'test.tmp') + except Exception: + bname, ext = filename, 'tmp' fd, fname = tempfile.mkstemp(suffix=ext) os.write(fd, content) os.close(fd) -- cgit v1.2.3 From 6aef03991d174112c5787502e825ee4895f52bf0 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Document: tests bzr revid: p_christ@hol.gr-20100701175131-5acgsuetw1qx0juu --- addons/document/test/document_test2.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index e386db760c4..637fc2d9e48 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -17,12 +17,29 @@ - I create an attachment into the root folder (w. empty fields, test that defaults work) +- + !record {model: ir.attachment, id: file_test1 }: + name: Test file - I delete the attachment from the root folder +- + !delete {model: ir.attachment, id: file_test1, search: } - I create an attachment into the Testing folder. +- + !record {model: ir.attachment, id: file_test2 }: + name: Test file 2 + parent_id: dir_tests - I update the attachment with data, namely "abcd" +- + !record {model: ir.attachment, id: file_test2 }: + datas: abcd +- + I test that the datas of the attachment are correct +- + !assert {model: ir.attachment, id: file_test2 }: + - datas == 'abcd' - I rename the attachment. - -- cgit v1.2.3 From 8307ff013916ffcaf8c7f0c8a796df77a64bfcd4 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Document: workaround case when text file consists of non-unicode. The content indexer would be trying to process the garbage of the file and fail. bzr revid: p_christ@hol.gr-20100701175131-b83jg498xjuosmd9 --- addons/document/document_storage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index b2db4eb36a6..26ba5dea772 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -378,16 +378,21 @@ class document_storage(osv.osv): self._doclog.debug('Cannot index file:', exc_info=True) pass + try: + icont_u = ustr(icont) + except UnicodeError: + icont_u = '' + # a hack: /assume/ that the calling write operation will not try # to write the fname and size, and update them in the db concurrently. # We cannot use a write() here, because we are already in one. cr.execute('UPDATE ir_attachment SET store_fname = %s, file_size = %s, index_content = %s, file_type = %s WHERE id = %s', - (store_fname, filesize, ustr(icont), mime, file_node.file_id)) + (store_fname, filesize, icont_u, mime, file_node.file_id)) file_node.content_length = filesize file_node.content_type = mime return True except Exception, e : - self._doclog.warning( "Couldn't save data:", exc_info=True) + self._doclog.warning("Couldn't save data:", exc_info=True) # should we really rollback once we have written the actual data? # at the db case (only), that rollback would be safe raise except_orm(_('Error at doc write!'), str(e)) -- cgit v1.2.3 From 1e99bfc52c55dd20114b9c5586a8e6fe0c921dd2 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Document: test cases bzr revid: p_christ@hol.gr-20100701175131-jltfosk4l5uvzo5l --- addons/document/test/document_test2.yml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index 637fc2d9e48..a8962ac1ae8 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -34,18 +34,35 @@ I update the attachment with data, namely "abcd" - !record {model: ir.attachment, id: file_test2 }: - datas: abcd + datas: "abcd" - I test that the datas of the attachment are correct - !assert {model: ir.attachment, id: file_test2 }: - - datas == 'abcd' + - datas == "abcd\n" - I rename the attachment. +- + !record {model: ir.attachment, id: file_test2 }: + name: Test renamed 2 - I search the testing folder for attachments. +- + !python {model: ir.attachment}: | + ids = self.search(cr, uid, [('parent_id.name','=', 'Testing'), ('name','=','Test renamed 2')]) + assert ids == [ ref("file_test2") ], ids - I create an attachment to a 3rd resource, eg. a res.country +- + !record {model: ir.attachment, id: attach_3rd }: + name: 'Res country attachment.txt' + datas: 'defg' + res_model: res.country + res_id: !eval ref("base.za") - I search for the res.country attachment +- + !python {model: ir.attachment}: | + ids = self.search(cr, uid, [('res_model', '=', 'res.country'), ('res_id', '=', ref("base.za"))]) + assert ids == [ ref("attach_3rd")], ids - -- cgit v1.2.3 From 28464cb07c33a759653abda9310fd9e3e99ea4ef Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:31 +0300 Subject: Document tests: need to remove each document created in tests There is commit commands inside document, to prevent from partially writting data (eg. filestore and the db). Thus, the atomicity of the transaction is broken. bzr revid: p_christ@hol.gr-20100701175131-8v8xyosvz8nly0pi --- addons/document/test/document_test2.yml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index a8962ac1ae8..3a3b573e4e9 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -65,4 +65,6 @@ !python {model: ir.attachment}: | ids = self.search(cr, uid, [('res_model', '=', 'res.country'), ('res_id', '=', ref("base.za"))]) assert ids == [ ref("attach_3rd")], ids +- + !delete {moel: ir.attachment, id: attach_3rd, search: } - -- 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/document.py | 26 +++++++++++++++++++++++++- addons/document/nodes.py | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index fb69b84e6a5..27bd9aa3520 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -28,6 +28,7 @@ import os import pooler import netsvc +from osv.orm import except_orm #import StringIO from psycopg2 import Binary @@ -153,7 +154,30 @@ class document_file(osv.osv): return False if not self._check_duplication(cr, uid, vals, ids, 'write'): raise osv.except_osv(_('ValidateError'), _('File name must be unique!')) - result = super(document_file, self).write(cr, uid, ids, vals, context=context) + if 'parent_id' in vals: + # perhaps this file is changing directory + nctx = nodes.get_node_context(cr,uid,context) + dirobj = self.pool.get('document.directory') + dbro = dirobj.browse(cr, uid, vals['parent_id'], context=context) + ids2 = [] + result = False + for fbro in self.browse(cr, uid, ids, context=context): + if fbro.parent_id != vals['parent_id']: + fnode = nodes.node_file(None,None,nctx,fbro) + res = fnode.move_to(cr, fbro, dbro, True) + if isinstance(res, dict): + vals2 = vals.copy() + vals2.update(res) + wid = res.get('id', fbro.id) + result = super(document_file,self).write(cr,uid,wid,vals2,context=context) + # TODO: how to handle/merge several results? + elif res == True: + ids2.append(fbro.id) + elif res == False: + pass + ids = ids2 + if len(ids): + result = super(document_file,self).write(cr, uid, ids, vals, context=context) cr.commit() return result 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 530932640babacde4973fd47f2a9632c1d70bd3d Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document: fix setting parent of document_file. There is some code that calls the 'move_to()' in case the document's parent (aka. directory) is changed. Fix notation there. bzr revid: p_christ@hol.gr-20100701175132-8ex94b3pdwkz4lq6 --- addons/document/document.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index 27bd9aa3520..428425be930 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -120,7 +120,7 @@ class document_file(osv.osv): res_model = vals.get('res_model', False) res_id = vals.get('res_id', 0) if op == 'write': - for file in self.browse(cr, uid, ids): + for file in self.browse(cr, uid, ids): # FIXME fields_only if not name: name = file.name if not parent_id: @@ -162,7 +162,7 @@ class document_file(osv.osv): ids2 = [] result = False for fbro in self.browse(cr, uid, ids, context=context): - if fbro.parent_id != vals['parent_id']: + if fbro.parent_id.id != vals['parent_id']: fnode = nodes.node_file(None,None,nctx,fbro) res = fnode.move_to(cr, fbro, dbro, True) if isinstance(res, dict): -- 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') 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/document_storage.py | 37 ++++++++++++++++++++++--------------- addons/document/nodes.py | 3 +++ 2 files changed, 25 insertions(+), 15 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 26ba5dea772..08d3d82893b 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -35,6 +35,7 @@ from osv.orm import except_orm import random import string +import pooler import netsvc import nodes from content_index import cntIndex @@ -129,21 +130,27 @@ class nodefd_db(StringIO, nodes.node_descriptor): def close(self): # we now open a *separate* cursor, to update the data. # FIXME: this may be improved, for concurrency handling - uid = self.__parent.context.uid - cr = pooler.get_db(self.__parent.context.dbname).cursor() - if mode == 'w': - out = self.getvalue() - cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s', - (out, len(out), self.__parent.file_id)) - elif mode == 'a': - out = self.getvalue() - cr.execute("UPDATE ir_attachment " \ - "SET db_datas = COALESCE(db_datas,'') || %s, " \ - " file_size = COALESCE(file_size, 0) + %d " \ - " WHERE id = %s", - (out, len(out), self.__parent.file_id)) - cr.commit() - cr.close() + par = self._get_parent() + uid = par.context.uid + cr = pooler.get_db(par.context.dbname).cursor() + try: + if self.mode == 'w': + out = self.getvalue() + cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s', + (out, len(out), par.file_id)) + elif self.mode == 'a': + out = self.getvalue() + cr.execute("UPDATE ir_attachment " \ + "SET db_datas = COALESCE(db_datas,'') || %s, " \ + " file_size = COALESCE(file_size, 0) + %d " \ + " WHERE id = %s", + (out, len(out), par.file_id)) + cr.commit() + except Exception, e: + logging.getLogger('document.storage').exception('Cannot update db file #%d for close:', par.file_id) + raise + finally: + cr.close() StringIO.close(self) class document_storage(osv.osv): 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 d52f451bcadd20d36532c837e0164569287764e1 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document: tests improvements. bzr revid: p_christ@hol.gr-20100701175132-ojsjqidcfegz9py8 --- addons/document/test/document_test2.yml | 3 +++ 1 file changed, 3 insertions(+) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index 3a3b573e4e9..fa5e98d3bdb 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -40,6 +40,8 @@ - !assert {model: ir.attachment, id: file_test2 }: - datas == "abcd\n" + - file_size == 5 + - file_type == 'text/plain' - I rename the attachment. - @@ -56,6 +58,7 @@ - !record {model: ir.attachment, id: attach_3rd }: name: 'Res country attachment.txt' + parent_id: dir_tests datas: 'defg' res_model: res.country res_id: !eval ref("base.za") -- 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') 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') 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 0153d03c825974985ad5486f7ae9229cf0814853 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:32 +0300 Subject: Document storage: support more file open modes. We may be called with 'w+', 'r+' modes, support them. However, we still don't support the 'a+' mode. bzr revid: p_christ@hol.gr-20100701175132-v21yzk2h335emum1 --- addons/document/document_storage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 08d3d82893b..606600e17cb 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -114,10 +114,10 @@ class nodefd_db(StringIO, nodes.node_descriptor): if mode.endswith('b'): mode = mode[:-1] - if mode == 'r': - StringIO.__init__(self, ira_browse.db_datas) - elif mode == 'w': + if mode in ('r', 'r+'): StringIO.__init__(self, ira_browse.db_datas) + 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': @@ -134,7 +134,7 @@ class nodefd_db(StringIO, nodes.node_descriptor): uid = par.context.uid cr = pooler.get_db(par.context.dbname).cursor() try: - if self.mode == 'w': + if self.mode in ('w', 'w+', 'r+'): out = self.getvalue() cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s', (out, len(out), par.file_id)) -- cgit v1.2.3 From 2c5082001e8d6379fa36dd34cf070575ffcb31b1 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document: use the new move_to() api, protect setting of file_size. bzr revid: p_christ@hol.gr-20100701175133-9prni6r0kspt27ku --- addons/document/document.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index 428425be930..221c9680aa7 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -159,12 +159,13 @@ class document_file(osv.osv): nctx = nodes.get_node_context(cr,uid,context) dirobj = self.pool.get('document.directory') dbro = dirobj.browse(cr, uid, vals['parent_id'], context=context) + dnode = nctx.get_dir_node(cr, dbro) ids2 = [] result = False for fbro in self.browse(cr, uid, ids, context=context): if fbro.parent_id.id != vals['parent_id']: - fnode = nodes.node_file(None,None,nctx,fbro) - res = fnode.move_to(cr, fbro, dbro, True) + fnode = nctx.get_file_node(cr, fbro) + res = fnode.move_to(cr, dnode, fbro, dbro, True) if isinstance(res, dict): vals2 = vals.copy() vals2.update(res) @@ -176,9 +177,11 @@ class document_file(osv.osv): elif res == False: pass ids = ids2 + if 'file_size' in vals: # only write that field using direct SQL calls + del vals['file_size'] if len(ids): result = super(document_file,self).write(cr, uid, ids, vals, context=context) - cr.commit() + cr.commit() # ? return result def create(self, cr, uid, vals, context=None): @@ -219,11 +222,15 @@ class document_file(osv.osv): else: datas = vals.get('datas', False) - vals['file_size'] = datas and len(datas) or 0 + if datas: + vals['file_size'] = len(datas) + else: + if vals.get('file_size'): + del vals['file_size'] if not self._check_duplication(cr, uid, vals): raise osv.except_osv(_('ValidateError'), _('File name must be unique!')) result = super(document_file, self).create(cr, uid, vals, context) - cr.commit() + cr.commit() # ? return result def unlink(self, cr, uid, ids, context={}): -- cgit v1.2.3 From 7c26eb67fb646e02848435e768fd9c08a63529c9 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document storage: the default db encoding is base64 in bytea The storage class has to consider that. Also, prepare a class for raw bytea storage. bzr revid: p_christ@hol.gr-20100701175133-xxgu3cdlff0u7086 --- addons/document/document_storage.py | 64 ++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 606600e17cb..290faac0229 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -115,7 +115,9 @@ class nodefd_db(StringIO, nodes.node_descriptor): mode = mode[:-1] if mode in ('r', 'r+'): - StringIO.__init__(self, ira_browse.db_datas) + cr.execute('SELECT db_datas FROM ir_attachment WHERE id = %s', ira_browse.id) + data = cr.fetchone()[0] + StringIO.__init__(self, data) elif mode in ('w', 'w+'): StringIO.__init__(self, None) # at write, we start at 0 (= overwrite), but have the original @@ -136,13 +138,13 @@ class nodefd_db(StringIO, nodes.node_descriptor): try: if self.mode in ('w', 'w+', 'r+'): out = self.getvalue() - cr.execute('UPDATE ir_attachment SET db_datas = %s, file_size=%d WHERE id = %s', + cr.execute("UPDATE ir_attachment SET db_datas = decode(%s,'escape'), file_size=%s WHERE id = %s", (out, len(out), par.file_id)) elif self.mode == 'a': out = self.getvalue() cr.execute("UPDATE ir_attachment " \ - "SET db_datas = COALESCE(db_datas,'') || %s, " \ - " file_size = COALESCE(file_size, 0) + %d " \ + "SET db_datas = COALESCE(db_datas,'') || decode(%s, 'escape'), " \ + " file_size = COALESCE(file_size, 0) + %s " \ " WHERE id = %s", (out, len(out), par.file_id)) cr.commit() @@ -153,6 +155,58 @@ class nodefd_db(StringIO, nodes.node_descriptor): cr.close() StringIO.close(self) +class nodefd_db64(StringIO, nodes.node_descriptor): + """ A descriptor to db data, base64 (the old way) + + It stores the data in base64 encoding at the db. Not optimal, but + the transparent compression of Postgres will save the day. + """ + def __init__(self, parent, ira_browse, mode): + nodes.node_descriptor.__init__(self, parent) + if mode.endswith('b'): + mode = mode[:-1] + + if mode in ('r', 'r+'): + StringIO.__init__(self, base64.decodestring(ira_browse.db_datas)) + 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.storage').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 + par = self._get_parent() + uid = par.context.uid + cr = pooler.get_db(par.context.dbname).cursor() + try: + if self.mode in ('w', 'w+', 'r+'): + out = self.getvalue() + cr.execute('UPDATE ir_attachment SET db_datas = %s::bytea, file_size=%s WHERE id = %s', + (base64.encodestring(out), len(out), par.file_id)) + elif self.mode == 'a': + out = self.getvalue() + # Yes, we're obviously using the wrong representation for storing our + # data as base64-in-bytea + cr.execute("UPDATE ir_attachment " \ + "SET db_datas = encode( (COALESCE(decode(encode(db_datas,'escape'),'base64'),'') || decode(%s, 'base64')),'base64')::bytea , " \ + " file_size = COALESCE(file_size, 0) + %s " \ + " WHERE id = %s", + (base64.encodestring(out), len(out), par.file_id)) + cr.commit() + except Exception, e: + logging.getLogger('document.storage').exception('Cannot update db file #%d for close:', par.file_id) + raise + finally: + cr.close() + StringIO.close(self) + class document_storage(osv.osv): """ The primary object for data storage. Each instance of this object is a storage media, in which our application @@ -239,7 +293,7 @@ class document_storage(osv.osv): # TODO: we need a better api for large files if self._debug: self._doclog.debug("Trying to obtain db_datas for ir.attachment[%d]", ira.id) - return nodefd_db(file_node, ira_browse=ira, mode=mode) + return nodefd_db64(file_node, ira_browse=ira, mode=mode) elif boo.type == 'realstore': if not ira.store_fname: -- cgit v1.2.3 From c9f313dc218d1164f7133af2b4e3a7b8261110c6 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document: isolate the partner_id auto-detection in a fn() When a document is created, some code tries to auto-detect the related partner_id from res_model. This should better be put in a function, and reused in other places. bzr revid: p_christ@hol.gr-20100701175133-6aq2tx9lkuskvedh --- addons/document/document.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index 221c9680aa7..dbd4ec3f5b0 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -194,26 +194,10 @@ class document_file(osv.osv): vals['res_id'] = context.get('default_res_id', False) if not vals.get('res_model', False) and context.get('default_res_model', False): vals['res_model'] = context.get('default_res_model', False) - if vals.get('res_id', False) and vals.get('res_model', False): - obj_model = self.pool.get(vals['res_model']) - result = obj_model.read(cr, uid, [vals['res_id']], ['name', 'partner_id', 'address_id'], context=context) - if len(result): - obj = result[0] - if obj_model._name == 'res.partner': - vals['partner_id'] = obj['id'] - elif obj.get('address_id', False): - if isinstance(obj['address_id'], tuple) or isinstance(obj['address_id'], list): - address_id = obj['address_id'][0] - else: - address_id = obj['address_id'] - address = self.pool.get('res.partner.address').read(cr, uid, [address_id], context=context) - if len(address): - vals['partner_id'] = address[0]['partner_id'][0] or False - elif obj.get('partner_id', False): - if isinstance(obj['partner_id'], tuple) or isinstance(obj['partner_id'], list): - vals['partner_id'] = obj['partner_id'][0] - else: - vals['partner_id'] = obj['partner_id'] + if vals.get('res_id', False) and vals.get('res_model', False) \ + and not vals.get('partner_id', False): + vals['partner_id'] = __get_partner_id(cr, uid, \ + vals['res_model'], vals['res_id'], context) datas = None if vals.get('link', False) : @@ -233,6 +217,23 @@ class document_file(osv.osv): cr.commit() # ? return result + def __get_partner_id(self, cr, uid, res_model, res_id, context): + """ A helper to retrieve the associated partner from any res_model+id + It is a hack that will try to discover if the mentioned record is + clearly associated with a partner record. + """ + if False: + obj_model = self.pool.get(vals['res_model']) + if obj_model._name == 'res.partner': + return res_id + elif 'partner_id' in obj_model._columns and obj_model._columns['partner_id']._obj == 'res.partner': + bro = obj_model.browse(self, cr, uid, res_id, context=context) + return bro.partner_id.id + elif 'address_id' in obj_model._columns and obj_model._columns['address_id']._obj == 'res.partner.address': + bro = obj_model.browse(self, cr, uid, res_id, context=context) + return bro.address_id.partner_id.id + return False + def unlink(self, cr, uid, ids, context={}): stor = self.pool.get('document.storage') unres = [] -- 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') 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') 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') 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') 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 49bf479c188533b93d74f1e0f873f8d71b6ebd75 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Thu, 1 Jul 2010 20:51:33 +0300 Subject: Document storage: implementation of simple rename Conflicts: document/document_storage.py bzr revid: p_christ@hol.gr-20100701175133-ti0itn58w9az88w6 --- addons/document/document_storage.py | 38 ++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 290faac0229..aeea5f99011 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -480,7 +480,7 @@ class document_storage(osv.osv): path = storage_bo.path return ( storage_bo.id, 'file', os.path.join(path, fname)) else: - raise TypeError("No %s storage" % boo.type) + raise TypeError("No %s storage" % storage_bo.type) def do_unlink(self, cr, uid, unres): for id, ktype, fname in unres: @@ -494,6 +494,42 @@ class document_storage(osv.osv): return True + def simple_rename(self, cr, uid, file_node, new_name, context=None): + """ A preparation for a file rename. + It will not affect the database, but merely check and perhaps + rename the realstore file. + + @return the dict of values that can safely be be stored in the db. + """ + sbro = self.browse(cr, uid, file_node.storage_id, context=context) + assert sbro, "The file #%d didn't provide storage" % file_node.file_id + + if sbro.type in ('filestore', 'db'): + # nothing to do for a rename, allow to change the db field + return { 'name': new_name, 'datas_fname': new_name } + elif sbro.type == 'realstore': + fname = fil_bo.store_fname + if not fname: + return ValueError("Tried to rename a non-stored file") + path = storage_bo.path + oldpath = os.path.join(path, fname) + + for ch in ('*', '|', "\\", '/', ':', '"', '<', '>', '?', '..'): + if ch in new_name: + raise ValueError("Invalid char %s in name %s" %(ch, new_name)) + + file_node.fix_ppath(cr, ira) + npath = file_node.full_path() or [] + dpath = [path,] + dpath.extend(npath[:-1]) + dpath.append(new_name) + newpath = os.path.join(*dpath) + # print "old, new paths:", oldpath, newpath + os.rename(oldpath, newpath) + return { 'name': new_name, 'datas_fname': new_name, 'store_fname': new_name } + else: + raise TypeError("No %s storage" % boo.type) + document_storage() -- cgit v1.2.3 From be2f5723c9edb0b8650f79c55219d3d66a177633 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 2 Jul 2010 11:45:35 +0300 Subject: Document, ftp: switch to new series of tests. bzr revid: p_christ@hol.gr-20100702084535-6yz0f5xfsvh4jl9b --- addons/document/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/__openerp__.py b/addons/document/__openerp__.py index a260a083d74..63b09121309 100644 --- a/addons/document/__openerp__.py +++ b/addons/document/__openerp__.py @@ -53,7 +53,7 @@ ], 'demo_xml': [ 'document_demo.xml','board_document_demo.xml'], 'test': [ - 'test/document_test.yml', + 'test/document_test2.yml', ], 'installable': True, 'active': False, -- 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') 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') 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') 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') 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 e8bf4a4f18fe70d52192f8893105ba9291abef4a Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 6 Jul 2010 15:10:45 +0300 Subject: Document: fix the get_partner_id functionality Was blocking document.create() bzr revid: p_christ@hol.gr-20100706121045-mblekyw8nlkiy1r0 --- addons/document/document.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document.py b/addons/document/document.py index dbd4ec3f5b0..3dc84529d8d 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -196,7 +196,7 @@ class document_file(osv.osv): vals['res_model'] = context.get('default_res_model', False) if vals.get('res_id', False) and vals.get('res_model', False) \ and not vals.get('partner_id', False): - vals['partner_id'] = __get_partner_id(cr, uid, \ + vals['partner_id'] = self.__get_partner_id(cr, uid, \ vals['res_model'], vals['res_id'], context) datas = None @@ -222,16 +222,15 @@ class document_file(osv.osv): It is a hack that will try to discover if the mentioned record is clearly associated with a partner record. """ - if False: - obj_model = self.pool.get(vals['res_model']) - if obj_model._name == 'res.partner': - return res_id - elif 'partner_id' in obj_model._columns and obj_model._columns['partner_id']._obj == 'res.partner': - bro = obj_model.browse(self, cr, uid, res_id, context=context) - return bro.partner_id.id - elif 'address_id' in obj_model._columns and obj_model._columns['address_id']._obj == 'res.partner.address': - bro = obj_model.browse(self, cr, uid, res_id, context=context) - return bro.address_id.partner_id.id + obj_model = self.pool.get(res_model) + if obj_model._name == 'res.partner': + return res_id + elif 'partner_id' in obj_model._columns and obj_model._columns['partner_id']._obj == 'res.partner': + bro = obj_model.browse(self, cr, uid, res_id, context=context) + return bro.partner_id.id + elif 'address_id' in obj_model._columns and obj_model._columns['address_id']._obj == 'res.partner.address': + bro = obj_model.browse(self, cr, uid, res_id, context=context) + return bro.address_id.partner_id.id return False def unlink(self, cr, uid, ids, context={}): -- cgit v1.2.3 From 67386ec78b3fec4bfde55135fa88a2322c3a4804 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Tue, 6 Jul 2010 15:10:47 +0300 Subject: Document: fix typo in test.yml bzr revid: p_christ@hol.gr-20100706121047-p2nex1m3g86nirt6 --- addons/document/test/document_test2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/document') diff --git a/addons/document/test/document_test2.yml b/addons/document/test/document_test2.yml index fa5e98d3bdb..a83cfa23bfe 100644 --- a/addons/document/test/document_test2.yml +++ b/addons/document/test/document_test2.yml @@ -69,5 +69,5 @@ ids = self.search(cr, uid, [('res_model', '=', 'res.country'), ('res_id', '=', ref("base.za"))]) assert ids == [ ref("attach_3rd")], ids - - !delete {moel: ir.attachment, id: attach_3rd, search: } + !delete {model: ir.attachment, id: attach_3rd, search: } - -- cgit v1.2.3 From 1927431e0bf256503fbdcf282008a7b45f449559 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 7 Jul 2010 02:27:00 +0300 Subject: Document: remove debug calls from trunk code. That code would only work in trunk-xrg. bzr revid: p_christ@hol.gr-20100706232700-x56f57gqhzupfphq --- addons/document/document_storage.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index aeea5f99011..809c3c83be8 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -285,14 +285,10 @@ class document_storage(osv.osv): self._doclog.warning( "ir.attachment #%d does not have a filename, but is at filestore, fix it!" % ira.id) raise IOError(errno.ENOENT, 'No file can be located') fpath = os.path.join(boo.path, ira.store_fname) - if self._debug: - self._doclog.debug("Trying to read \"%s\".."% fpath) return nodefd_file(file_node, path=fpath, mode=mode) elif boo.type == 'db': # TODO: we need a better api for large files - if self._debug: - self._doclog.debug("Trying to obtain db_datas for ir.attachment[%d]", ira.id) return nodefd_db64(file_node, ira_browse=ira, mode=mode) elif boo.type == 'realstore': -- cgit v1.2.3 From 03865bc917b6fa7ee3e3964411722ccdc75b52c2 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Fri, 9 Jul 2010 01:50:42 +0300 Subject: Document: Less dir, storage options, cleanup views. The 'virtual' storage is now hidden. An extra 'db64' type, hidden, is supported in the code, for v5-compatible databases (until we migrate the db_datas column of them). The 'at_record' directory type is gone. A module in extra-addons will permit the compatible behaviour, for those experts. bzr revid: p_christ@hol.gr-20100708225042-fp3zcwmw4zp3ee04 --- addons/document/document_directory.py | 2 +- addons/document/document_storage.py | 39 ++++++++++++++++++++++++++++++----- addons/document/document_view.xml | 9 ++++---- 3 files changed, 39 insertions(+), 11 deletions(-) (limited to 'addons/document') diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index 1b62418106c..9913946eee4 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -50,7 +50,7 @@ class document_directory(osv.osv): 'type': fields.selection([ ('directory','Static Directory'), ('ressource','Folders per resource'), - ('at_record','Single folder under resource')], + ], 'Type', required=True, select=1, help="Defines directory's behaviour."), diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py index 809c3c83be8..b1b884a9d98 100644 --- a/addons/document/document_storage.py +++ b/addons/document/document_storage.py @@ -232,7 +232,7 @@ class document_storage(osv.osv): 'group_ids': fields.many2many('res.groups', 'document_storage_group_rel', 'item_id', 'group_id', 'Groups'), 'dir_ids': fields.one2many('document.directory', 'parent_id', 'Directories'), 'type': fields.selection([('db', 'Database'), ('filestore', 'Internal File storage'), - ('realstore', 'External file storage'), ('virtual', 'Virtual storage')], 'Type', required=True), + ('realstore','External file storage'),], 'Type', required=True), 'path': fields.char('Path', size=250, select=1, help="For file storage, the root path of the storage"), 'online': fields.boolean('Online', help="If not checked, media is currently offline and its contents not available", required=True), 'readonly': fields.boolean('Read Only', help="If set, media is for reading only"), @@ -270,6 +270,8 @@ class document_storage(osv.osv): return self.__get_data_3(cr, uid, boo, ira, context) def get_file(self, cr, uid, id, file_node, mode, context=None): + """ Return a file-like object for the contents of some node + """ if context is None: context = {} boo = self.browse(cr, uid, id, context) @@ -289,6 +291,9 @@ class document_storage(osv.osv): elif boo.type == 'db': # TODO: we need a better api for large files + return nodefd_db(file_node, ira_browse=ira, mode=mode) + + elif boo.type == 'db64': return nodefd_db64(file_node, ira_browse=ira, mode=mode) elif boo.type == 'realstore': @@ -303,6 +308,9 @@ class document_storage(osv.osv): raise IOError("File not found: %s" % fpath) return nodefd_file(file_node, path=fpath, mode=mode) + elif boo.type == 'virtual': + raise ValueError('Virtual storage does not support static files') + else: raise TypeError("No %s storage" % boo.type) @@ -318,13 +326,21 @@ class document_storage(osv.osv): return None fpath = os.path.join(boo.path, ira.store_fname) return file(fpath, 'rb').read() - elif boo.type == 'db': + elif boo.type == 'db64': # TODO: we need a better api for large files if ira.db_datas: out = base64.decodestring(ira.db_datas) else: out = '' return out + elif boo.type == 'db': + # We do an explicit query, to avoid type transformations. + cr.execute('SELECT db_datas FROM ir_attachment WHERE id = %s', (ira.id,)) + res = cr.fetchone() + if res: + return res[0] + else: + return '' elif boo.type == 'realstore': if not ira.store_fname: # On a migrated db, some files may have the wrong storage type @@ -339,6 +355,10 @@ class document_storage(osv.osv): return None else: raise IOError("File not found: %s" % fpath) + + elif boo.type == 'virtual': + raise ValueError('Virtual storage does not support static files') + else: raise TypeError("No %s storage" % boo.type) @@ -387,7 +407,12 @@ class document_storage(osv.osv): raise except_orm(_('Error!'), str(e)) elif boo.type == 'db': filesize = len(data) - # will that work for huge data? TODO + # will that work for huge data? + cr.execute('UPDATE ir_attachment SET db_datas = %s WHERE id = %s', + (data, file_node.file_id)) + elif boo.type == 'db64': + filesize = len(data) + # will that work for huge data? out = base64.encodestring(data) cr.execute('UPDATE ir_attachment SET db_datas = %s WHERE id = %s', (out, file_node.file_id)) @@ -419,6 +444,10 @@ class document_storage(osv.osv): except Exception,e : self._doclog.warning("Couldn't save data:", exc_info=True) raise except_orm(_('Error!'), str(e)) + + elif boo.type == 'virtual': + raise ValueError('Virtual storage does not support static files') + else: raise TypeError("No %s storage" % boo.type) @@ -467,7 +496,7 @@ class document_storage(osv.osv): return None path = storage_bo.path return (storage_bo.id, 'file', os.path.join(path, fname)) - elif storage_bo.type == 'db': + elif storage_bo.type in ('db', 'db64'): return None elif storage_bo.type == 'realstore': fname = fil_bo.store_fname @@ -500,7 +529,7 @@ class document_storage(osv.osv): sbro = self.browse(cr, uid, file_node.storage_id, context=context) assert sbro, "The file #%d didn't provide storage" % file_node.file_id - if sbro.type in ('filestore', 'db'): + if sbro.type in ('filestore', 'db', 'db64'): # nothing to do for a rename, allow to change the db field return { 'name': new_name, 'datas_fname': new_name } elif sbro.type == 'realstore': diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index f342fb7cf56..4f72245ce40 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -16,7 +16,7 @@ - + @@ -85,15 +85,14 @@ + + - + - - -