Files
tryton/trytond/webdav/webdav.py
T
Cédric Krier fefd67c3a5 Improve Binary fields
* Support size format in context for Binary fields
* Use buffer for Binary fields
* No more store Binary fields in base64

review74006
2011-09-17 14:43:05 +02:00

609 lines
26 KiB
Python

#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import os
import time
from trytond.model import ModelView, ModelSQL, fields
from trytond.version import PACKAGE, VERSION, WEBSITE
from trytond.tools import reduce_ids, safe_eval
from trytond.transaction import Transaction
from trytond.pool import Pool
class Collection(ModelSQL, ModelView):
"Collection"
_name = "webdav.collection"
_description = __doc__
name = fields.Char('Name', required=True, select=1)
parent = fields.Many2One('webdav.collection', 'Parent',
ondelete='RESTRICT', domain=[('model', '=', False)])
childs = fields.One2Many('webdav.collection', 'parent', 'Children')
model = fields.Many2One('ir.model', 'Model')
domain = fields.Char('Domain')
def __init__(self):
super(Collection, self).__init__()
self._sql_constraints += [
('name_parent_uniq', 'UNIQUE (name, parent)',
'The collection name must be unique inside a collection!'),
]
self._constraints += [
('check_recursion', 'recursive_collections'),
('check_attachment', 'collection_file_name'),
]
self._error_messages.update({
'recursive_collections': 'You can not create recursive ' \
'collections!',
'collection_file_name': 'You can not create a collection\n' \
'in a collection with the name of an ' \
'existing file!',
})
self.ext2mime = {
'.png': 'image/png',
'.odt': 'application/vnd.oasis.opendocument.text',
'.pdf': 'application/pdf',
}
def default_domain(self):
return '[]'
def check_attachment(self, ids):
pool = Pool()
attachment_obj = pool.get('ir.attachment')
for collection in self.browse(ids):
if collection.parent:
attachment_ids = attachment_obj.search([
('resource', '=', '%s,%s' %
(self._name, collection.parent.id)),
])
for attachment in attachment_obj.browse(attachment_ids):
if attachment.name == collection.name:
return False
return True
def _uri2object(self, uri, object_name=_name, object_id=False, cache=None):
pool = Pool()
attachment_obj = pool.get('ir.attachment')
report_obj = pool.get('ir.action.report')
cache_uri = uri
if cache is not None:
cache.setdefault('_uri2object', {})
if cache_uri in cache['_uri2object']:
return cache['_uri2object'][cache_uri]
if not uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (self._name, False)
return self._name, False
name, uri = (uri.split('/', 1) + [None])[0:2]
if object_name == self._name:
collection_ids = None
if cache is not None:
cache.setdefault('_parent2collection_ids', {})
if object_id in cache['_parent2collection_ids']:
collection_ids = cache['_parent2collection_ids']\
[object_id].get(name, [])
if collection_ids is None:
collection_ids = self.search([
('parent', '=', object_id),
])
collections = self.browse(collection_ids)
collection_ids = []
if cache is not None:
cache['_parent2collection_ids'].setdefault(object_id, {})
for collection in collections:
if cache is not None:
cache['_parent2collection_ids'][object_id]\
.setdefault(collection.name, [])
cache['_parent2collection_ids'][object_id]\
[collection.name].append(collection.id)
cache.setdefault('_collection_name', {})
if collection.model and uri:
cache['_collection_name'][collection.id] = \
collection.model.model
else:
cache['_collection_name'][collection.id] = self._name
if collection.name == name:
collection_ids.append(collection.id)
if collection_ids:
object_id = collection_ids[0]
object_name2 = None
if cache is not None:
cache.setdefault('_collection_name', {})
if object_id in cache['_collection_name']:
object_name2 = cache['_collection_name'][object_id]
if object_name2 is None:
collection = self.browse(object_id)
if collection.model and uri:
object_name = collection.model.model
if cache is not None:
cache['_collection_name'][object_id] = object_name
else:
object_name = object_name2
else:
if uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (None, 0)
return None, 0
attachment_ids = None
if cache is not None:
cache.setdefault('_model&id2attachment_ids', {})
if (object_name, object_id) in \
cache['_model&id2attachment_ids']:
attachment_ids = cache['_model&id2attachment_ids']\
[(object_name, object_id)].get(name, [])
attachment_id = False
if attachment_ids is None:
attachment_ids = attachment_obj.search([
('resource', '=', '%s,%s' % (object_name, object_id)),
])
attachments = attachment_obj.browse(attachment_ids)
key = (object_name, object_id)
attachment_ids = []
if cache is not None:
cache['_model&id2attachment_ids'].setdefault(key, {})
for attachment in attachments:
if cache is not None:
cache.setdefault('_model&id&name2attachment_ids',
{})
cache['_model&id&name2attachment_ids'].setdefault(
key, {})
cache['_model&id&name2attachment_ids'][key]\
.setdefault(attachment.name, [])
cache['_model&id&name2attachment_ids'][key]\
[attachment.name].append(attachment.id)
if attachment.name == name:
attachment_id = attachment.id
else:
key = (object_name, object_id)
cache.setdefault('_model&id&name2attachment_ids', {})
cache['_model&id&name2attachment_ids'].setdefault(key, {})
attachment_id = cache['_model&id&name2attachment_ids']\
[key].get(name, [False])[0]
if attachment_id:
object_name = 'ir.attachment'
object_id = attachment_id
else:
object_name = None
object_id = False
else:
splitted_name = name.rsplit('-', 1)
if len(splitted_name) != 2:
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, 0)
return object_name, 0
object_id = int(splitted_name[1].strip())
if uri:
if '/' in uri:
if cache is not None:
cache['_uri2object'][cache_uri] = (None, 0)
return None, 0
report_ids = report_obj.search([
('model', '=', object_name),
])
reports = report_obj.browse(report_ids)
for report in reports:
report_name = report.name + '-' + str(report.id) \
+ '.' + report.extension
if uri == report_name:
if cache is not None:
cache['_uri2object'][cache_uri] = \
('ir.action.report', object_id)
return 'ir.action.report', object_id
name = uri
attachment_ids = None
if cache is not None:
cache.setdefault('_model&id2attachment_ids', {})
if (object_name, object_id) in \
cache['_model&id2attachment_ids']:
attachment_ids = cache['_model&id2attachment_ids']\
[(object_name, object_id)].get(name, [])
if attachment_ids is None:
attachment_ids = attachment_obj.search([
('resource', '=', '%s,%s' % (object_name, object_id)),
])
attachments = attachment_obj.browse(attachment_ids)
key = (object_name, object_id)
attachment_ids = []
if cache is not None:
cache['_model&id2attachment_ids'].setdefault(key, {})
for attachment in attachments:
if cache is not None:
cache['_model&id2attachment_ids'][key]\
.setdefault(attachment.name, [])
cache['_model&id2attachment_ids'][key]\
[attachment.name].append(attachment.id)
if attachment.name == name:
attachment_ids.append(attachment.id)
if attachment_ids:
object_name = 'ir.attachment'
object_id = attachment_ids[0]
else:
object_name = None
object_id = False
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, object_id)
return object_name, object_id
if uri:
res = self._uri2object(uri, object_name, object_id, cache=cache)
if cache is not None:
cache['_uri2object'][cache_uri] = res
return res
if cache is not None:
cache['_uri2object'][cache_uri] = (object_name, object_id)
return object_name, object_id
def get_childs(self, uri, filter=None, cache=None):
pool = Pool()
report_obj = pool.get('ir.action.report')
res = []
if filter:
return []
if not uri:
collection_ids = self.search([
('parent', '=', False),
])
for collection in self.browse(collection_ids):
if '/' in collection.name:
continue
res.append(collection.name)
if cache is not None:
cache.setdefault(self._name, {})
cache[self._name][collection.id] = {}
return res
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name == self._name and object_id:
collection = self.browse(object_id)
if collection.model:
model_obj = pool.get(collection.model.model)
if not model_obj:
return res
model_ids = model_obj.search(
safe_eval(collection.domain or "[]"))
for child in model_obj.browse(model_ids):
if '/' in child.rec_name:
continue
res.append(child.rec_name + '-' + str(child.id))
if cache is not None:
cache.setdefault(model_obj._name, {})
cache[model_obj._name][child.id] = {}
return res
else:
for child in collection.childs:
if '/' in child.name:
continue
res.append(child.name)
if cache is not None:
cache.setdefault(self._name, {})
cache[self._name][child.id] = {}
if object_name not in ('ir.attachment', 'ir.action.report'):
report_ids = report_obj.search([
('model', '=', object_name),
])
reports = report_obj.browse(report_ids)
for report in reports:
report_name = report.name + '-' + str(report.id) \
+ '.' + report.extension
if '/' in report_name:
continue
res.append(report_name)
if cache is not None:
cache.setdefault(report_obj._name, {})
cache[report_obj._name][report.id] = {}
attachment_obj = pool.get('ir.attachment')
attachment_ids = attachment_obj.search([
('resource', '=', '%s,%s' % (object_name, object_id)),
])
for attachment in attachment_obj.browse(attachment_ids):
if attachment.name and not attachment.link:
if '/' in attachment.name:
continue
res.append(attachment.name)
if cache is not None:
cache.setdefault(attachment_obj._name, {})
cache[attachment_obj._name][attachment.id] = {}
return res
def get_resourcetype(self, uri, cache=None):
from DAV.constants import COLLECTION, OBJECT
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name in ('ir.attachment', 'ir.action.report'):
return OBJECT
return COLLECTION
def get_displayname(self, uri, cache=None):
object_name, object_id = self._uri2object(uri, cache=cache)
model_obj = Pool().get(object_name)
return model_obj.browse(object_id).rec_name
def get_contentlength(self, uri, cache=None):
pool = Pool()
attachment_obj = pool.get('ir.attachment')
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name == 'ir.attachment':
if cache is not None:
cache.setdefault('ir.attachment', {})
ids = cache['ir.attachment'].keys()
if object_id not in ids:
ids.append(object_id)
elif 'contentlength' in cache['ir.attachment'][object_id]:
return cache['ir.attachment'][object_id]['contentlength']
else:
ids = [object_id]
attachments = attachment_obj.browse(ids)
res = '0'
for attachment in attachments:
size = '0'
try:
if attachment.data_size:
size = str(attachment.data_size)
except Exception:
pass
if attachment.id == object_id:
res = size
if cache is not None:
cache['ir.attachment'].setdefault(attachment.id, {})
cache['ir.attachment'][attachment.id]['contentlength'] = \
size
return res
return '0'
def get_contenttype(self, uri, cache=None):
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name in ('ir.attachment', 'ir.action.report'):
ext = os.path.splitext(uri)[1]
if not ext:
return "application/octet-stream"
return self.ext2mime.get(ext, 'application/octet-stream')
return "application/octet-stream"
def get_creationdate(self, uri, cache=None):
pool = Pool()
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name == 'ir.attachment':
model_obj = pool.get(object_name)
if object_id:
if cache is not None:
cache.setdefault(model_obj._name, {})
ids = cache[model_obj._name].keys()
if object_id not in ids:
ids.append(object_id)
elif 'creationdate' in cache[model_obj._name][object_id]:
return cache[model_obj._name][object_id]['creationdate']
else:
ids = [object_id]
res = None
cursor = Transaction().cursor
for i in range(0, len(ids), cursor.IN_MAX):
sub_ids = ids[i:i + cursor.IN_MAX]
red_sql, red_ids = reduce_ids('id', sub_ids)
cursor.execute('SELECT id, ' \
'EXTRACT(epoch FROM create_date) ' \
'FROM "' + model_obj._table +'" ' \
'WHERE ' + red_sql, red_ids)
for object_id2, date in cursor.fetchall():
if object_id2 == object_id:
res = date
if cache is not None:
cache[model_obj._name].setdefault(object_id2, {})
cache[model_obj._name][object_id2]['creationdate'] = \
date
if res is not None:
return res
return time.time()
def get_lastmodified(self, uri, cache=None):
pool = Pool()
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name == 'ir.attachment':
model_obj = pool.get(object_name)
if object_id:
if cache is not None:
cache.setdefault(model_obj._name, {})
ids = cache[model_obj._name].keys()
if object_id not in ids:
ids.append(object_id)
elif 'lastmodified' in cache[model_obj._name][object_id]:
return cache[model_obj._name][object_id]['lastmodified']
else:
ids = [object_id]
res = None
cursor = Transaction().cursor
for i in range(0, len(ids), cursor.IN_MAX):
sub_ids = ids[i:i + cursor.IN_MAX]
red_sql, red_ids = reduce_ids('id', sub_ids)
cursor.execute('SELECT id, ' \
'EXTRACT(epoch FROM ' \
'COALESCE(write_date, create_date)) ' \
'FROM "' + model_obj._table +'" ' \
'WHERE ' + red_sql, red_ids)
for object_id2, date in cursor.fetchall():
if object_id2 == object_id:
res = date
if cache is not None:
cache[model_obj._name].setdefault(object_id2, {})
cache[model_obj._name][object_id2]['lastmodified'] = \
date
if res is not None:
return res
return time.time()
def get_data(self, uri, cache=None):
from DAV.errors import DAV_NotFound
pool = Pool()
attachment_obj = pool.get('ir.attachment')
report_obj = pool.get('ir.action.report')
if uri:
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name == 'ir.attachment' and object_id:
if cache is not None:
cache.setdefault('ir.attachment', {})
ids = cache['ir.attachment'].keys()
if object_id not in ids:
ids.append(object_id)
elif 'data' in cache['ir.attachment'][object_id]:
res = cache['ir.attachment'][object_id]['data']
if res == DAV_NotFound:
raise DAV_NotFound
return res
else:
ids = [object_id]
attachments = attachment_obj.browse(ids)
res = DAV_NotFound
for attachment in attachments:
data = DAV_NotFound
try:
if attachment.data is not False:
data = attachment.data
except Exception:
pass
if attachment.id == object_id:
res = data
if cache is not None:
cache['ir.attachment'].setdefault(attachment.id, {})
cache['ir.attachment'][attachment.id]['data'] = data
if res == DAV_NotFound:
raise DAV_NotFound
return res
if object_name == 'ir.action.report' and object_id:
report_id = int(uri.rsplit('/', 1)[-1].rsplit('-',
1)[-1].rsplit('.', 1)[0])
report = report_obj.browse(report_id)
if report.report_name:
report_obj = pool.get(report.report_name,
type='report')
val = report_obj.execute([object_id],
{'id': object_id, 'ids': [object_id]})
return val[1]
raise DAV_NotFound
def put(self, uri, data, content_type, cache=None):
from DAV.errors import DAV_Forbidden
from DAV.utils import get_uriparentpath, get_urifilename
object_name, object_id = self._uri2object(get_uriparentpath(uri),
cache=cache)
if not object_name \
or object_name in ('ir.attachment') \
or not object_id:
raise DAV_Forbidden
pool = Pool()
attachment_obj = pool.get('ir.attachment')
object_name2, object_id2 = self._uri2object(uri, cache=cache)
if not object_id2:
name = get_urifilename(uri)
try:
attachment_obj.create({
'name': name,
'data': data,
'name': name,
'resource': '%s,%s' % (object_name, object_id),
})
except Exception:
raise DAV_Forbidden
else:
try:
attachment_obj.write(object_id2, {
'data': data,
})
except Exception:
raise DAV_Forbidden
return
def mkcol(self, uri, cache=None):
from DAV.errors import DAV_Forbidden
from DAV.utils import get_uriparentpath, get_urifilename
if uri[-1:] == '/':
uri = uri[:-1]
object_name, object_id = self._uri2object(get_uriparentpath(uri),
cache=cache)
if object_name != 'webdav.collection':
raise DAV_Forbidden
name = get_urifilename(uri)
try:
self.create({
'name': name,
'parent': object_id,
})
except Exception:
raise DAV_Forbidden
return 201
def rmcol(self, uri, cache=None):
from DAV.errors import DAV_Forbidden
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name != 'webdav.collection' \
or not object_id:
raise DAV_Forbidden
try:
self.delete(object_id)
except Exception:
raise DAV_Forbidden
return 200
def rm(self, uri, cache=None):
from DAV.errors import DAV_Forbidden
object_name, object_id = self._uri2object(uri, cache=cache)
if not object_name:
raise DAV_Forbidden
if object_name != 'ir.attachment' \
or not object_id:
raise DAV_Forbidden
pool = Pool()
model_obj = pool.get(object_name)
try:
model_obj.delete(object_id)
except Exception:
raise DAV_Forbidden
return 200
def exists(self, uri, cache=None):
object_name, object_id = self._uri2object(uri, cache=cache)
if object_name and object_id:
return 1
return None
def current_user_privilege_set(self, uri, cache=None):
return ['create', 'read', 'write', 'delete']
Collection()
class Attachment(ModelSQL, ModelView):
_name = 'ir.attachment'
def __init__(self):
super(Attachment, self).__init__()
self._constraints += [
('check_collection', 'collection_attachment_name'),
]
self._error_messages.update({
'collection_attachment_name': 'You can not create an attachment\n' \
'in a collection with the name\n' \
'of an existing child collection!',
})
def check_collection(self, ids):
pool = Pool()
collection_obj = pool.get('webdav.collection')
for attachment in self.browse(ids):
if attachment.resource:
model_name, record_id = attachment.resource.split(',')
if model_name == 'webdav.collection':
collection = collection_obj.browse(int(record_id))
for child in collection.childs:
if child.name == attachment.name:
return False
return True
Attachment()