summaryrefslogtreecommitdiff
path: root/addons/document/document_storage.py
diff options
context:
space:
mode:
authorP. Christeas <p_christ@hol.gr>2010-06-29 17:05:30 +0300
committerP. Christeas <p_christ@hol.gr>2010-06-29 17:05:30 +0300
commitf3cb7cd41539de507f800f10826f003017a40c8a (patch)
tree7bc3d76a881e56ffa373506eb8348378a84e6104 /addons/document/document_storage.py
parent54f9817335817d9947372192ed17ed72b0c25805 (diff)
Document storage: implement node_descriptors.
bzr revid: p_christ@hol.gr-20100629140530-btusqmlz2lyadfbx
Diffstat (limited to 'addons/document/document_storage.py')
-rw-r--r--addons/document/document_storage.py102
1 files changed, 102 insertions, 0 deletions
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')