|
@ -7,6 +7,7 @@ from operator import itemgetter |
|
|
from urllib.parse import quote_plus |
|
|
from urllib.parse import quote_plus |
|
|
|
|
|
|
|
|
from odoo import api, fields, models, tools |
|
|
from odoo import api, fields, models, tools |
|
|
|
|
|
from odoo.tools.safe_eval import safe_eval |
|
|
import logging |
|
|
import logging |
|
|
|
|
|
|
|
|
import vobject |
|
|
import vobject |
|
@ -19,8 +20,8 @@ logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DavCollection(models.Model): |
|
|
class DavCollection(models.Model): |
|
|
_name = 'dav.collection' |
|
|
|
|
|
_description = 'A collection accessible via WebDAV' |
|
|
|
|
|
|
|
|
_name = "dav.collection" |
|
|
|
|
|
_description = "A collection accessible via WebDAV" |
|
|
|
|
|
|
|
|
name = fields.Char(required=True) |
|
|
name = fields.Char(required=True) |
|
|
rights = fields.Selection( |
|
|
rights = fields.Selection( |
|
@ -34,99 +35,94 @@ class DavCollection(models.Model): |
|
|
) |
|
|
) |
|
|
dav_type = fields.Selection( |
|
|
dav_type = fields.Selection( |
|
|
[ |
|
|
[ |
|
|
('calendar', 'Calendar'), |
|
|
|
|
|
('addressbook', 'Addressbook'), |
|
|
|
|
|
('files', 'Files'), |
|
|
|
|
|
|
|
|
("calendar", "Calendar"), |
|
|
|
|
|
("addressbook", "Addressbook"), |
|
|
|
|
|
("files", "Files"), |
|
|
], |
|
|
], |
|
|
string='Type', |
|
|
|
|
|
|
|
|
string="Type", |
|
|
required=True, |
|
|
required=True, |
|
|
default='calendar', |
|
|
|
|
|
|
|
|
default="calendar", |
|
|
) |
|
|
) |
|
|
tag = fields.Char(compute='_compute_tag') |
|
|
|
|
|
|
|
|
tag = fields.Char(compute="_compute_tag") |
|
|
model_id = fields.Many2one( |
|
|
model_id = fields.Many2one( |
|
|
'ir.model', |
|
|
|
|
|
string='Model', |
|
|
|
|
|
|
|
|
"ir.model", |
|
|
|
|
|
string="Model", |
|
|
required=True, |
|
|
required=True, |
|
|
domain=[('transient', '=', False)], |
|
|
|
|
|
|
|
|
ondelete="cascade", |
|
|
|
|
|
domain=[("transient", "=", False)], |
|
|
) |
|
|
) |
|
|
domain = fields.Char( |
|
|
domain = fields.Char( |
|
|
required=True, |
|
|
required=True, |
|
|
default='[]', |
|
|
|
|
|
|
|
|
default="[]", |
|
|
) |
|
|
) |
|
|
field_uuid = fields.Many2one('ir.model.fields') |
|
|
|
|
|
|
|
|
field_uuid = fields.Many2one("ir.model.fields") |
|
|
field_mapping_ids = fields.One2many( |
|
|
field_mapping_ids = fields.One2many( |
|
|
'dav.collection.field_mapping', |
|
|
|
|
|
'collection_id', |
|
|
|
|
|
string='Field mappings', |
|
|
|
|
|
|
|
|
"dav.collection.field_mapping", |
|
|
|
|
|
"collection_id", |
|
|
|
|
|
string="Field mappings", |
|
|
) |
|
|
) |
|
|
url = fields.Char(compute='_compute_url') |
|
|
|
|
|
|
|
|
url = fields.Char(compute="_compute_url") |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def _compute_tag(self): |
|
|
def _compute_tag(self): |
|
|
for this in self: |
|
|
for this in self: |
|
|
if this.dav_type == 'calendar': |
|
|
|
|
|
this.tag = 'VCALENDAR' |
|
|
|
|
|
elif this.dav_type == 'addressbook': |
|
|
|
|
|
this.tag = 'VADDRESSBOOK' |
|
|
|
|
|
|
|
|
if this.dav_type == "calendar": |
|
|
|
|
|
this.tag = "VCALENDAR" |
|
|
|
|
|
elif this.dav_type == "addressbook": |
|
|
|
|
|
this.tag = "VADDRESSBOOK" |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def _compute_url(self): |
|
|
def _compute_url(self): |
|
|
base_url = self.env['ir.config_parameter'].get_param('web.base.url') |
|
|
|
|
|
|
|
|
base_url = self.env["ir.config_parameter"].get_param("web.base.url") |
|
|
for this in self: |
|
|
for this in self: |
|
|
this.url = '%s%s/%s/%s' % ( |
|
|
|
|
|
|
|
|
this.url = "%s%s/%s/%s" % ( |
|
|
base_url, |
|
|
base_url, |
|
|
PREFIX, |
|
|
PREFIX, |
|
|
self.env.user.login, |
|
|
self.env.user.login, |
|
|
this.id, |
|
|
this.id, |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
@api.constrains('domain') |
|
|
|
|
|
|
|
|
@api.constrains("domain") |
|
|
def _check_domain(self): |
|
|
def _check_domain(self): |
|
|
self._eval_domain() |
|
|
self._eval_domain() |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def _eval_context(self): |
|
|
def _eval_context(self): |
|
|
return { |
|
|
return { |
|
|
'user': self.env.user, |
|
|
|
|
|
|
|
|
"user": self.env.user, |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def get_logger(self): |
|
|
def get_logger(self): |
|
|
return logger |
|
|
return logger |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def _eval_domain(self): |
|
|
def _eval_domain(self): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
return list(tools.safe_eval(self.domain, self._eval_context())) |
|
|
|
|
|
|
|
|
return list(safe_eval(self.domain, self._eval_context())) |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def eval(self): |
|
|
def eval(self): |
|
|
if not self: |
|
|
if not self: |
|
|
return self.env['unknown'] |
|
|
|
|
|
|
|
|
return self.env["unknown"] |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
return self.env[self.model_id.model].search(self._eval_domain()) |
|
|
return self.env[self.model_id.model].search(self._eval_domain()) |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def get_record(self, components): |
|
|
def get_record(self, components): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
collection_model = self.env[self.model_id.model] |
|
|
collection_model = self.env[self.model_id.model] |
|
|
|
|
|
|
|
|
field_name = self.field_uuid.name or "id" |
|
|
field_name = self.field_uuid.name or "id" |
|
|
domain = [(field_name, '=', components[-1])] + self._eval_domain() |
|
|
|
|
|
|
|
|
domain = [(field_name, "=", components[-1])] + self._eval_domain() |
|
|
return collection_model.search(domain, limit=1) |
|
|
return collection_model.search(domain, limit=1) |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def from_vobject(self, item): |
|
|
def from_vobject(self, item): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
|
|
|
|
|
|
result = {} |
|
|
result = {} |
|
|
if self.dav_type == 'calendar': |
|
|
|
|
|
if item.name != 'VCALENDAR': |
|
|
|
|
|
|
|
|
if self.dav_type == "calendar": |
|
|
|
|
|
if item.name != "VCALENDAR": |
|
|
return None |
|
|
return None |
|
|
if not hasattr(item, 'vevent'): |
|
|
|
|
|
|
|
|
if not hasattr(item, "vevent"): |
|
|
return None |
|
|
return None |
|
|
item = item.vevent |
|
|
item = item.vevent |
|
|
elif self.dav_type == 'addressbook' and item.name != 'VCARD': |
|
|
|
|
|
|
|
|
elif self.dav_type == "addressbook" and item.name != "VCARD": |
|
|
return None |
|
|
return None |
|
|
|
|
|
|
|
|
children = {c.name.lower(): c for c in item.getChildren()} |
|
|
children = {c.name.lower(): c for c in item.getChildren()} |
|
@ -142,15 +138,14 @@ class DavCollection(models.Model): |
|
|
|
|
|
|
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def to_vobject(self, record): |
|
|
def to_vobject(self, record): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
result = None |
|
|
result = None |
|
|
vobj = None |
|
|
vobj = None |
|
|
if self.dav_type == 'calendar': |
|
|
|
|
|
|
|
|
if self.dav_type == "calendar": |
|
|
result = vobject.iCalendar() |
|
|
result = vobject.iCalendar() |
|
|
vobj = result.add('vevent') |
|
|
|
|
|
if self.dav_type == 'addressbook': |
|
|
|
|
|
|
|
|
vobj = result.add("vevent") |
|
|
|
|
|
if self.dav_type == "addressbook": |
|
|
result = vobject.vCard() |
|
|
result = vobject.vCard() |
|
|
vobj = result |
|
|
vobj = result |
|
|
for mapping in self.field_mapping_ids: |
|
|
for mapping in self.field_mapping_ids: |
|
@ -158,50 +153,49 @@ class DavCollection(models.Model): |
|
|
if value: |
|
|
if value: |
|
|
vobj.add(mapping.name).value = value |
|
|
vobj.add(mapping.name).value = value |
|
|
|
|
|
|
|
|
if 'uid' not in vobj.contents: |
|
|
|
|
|
vobj.add('uid').value = '%s,%s' % (record._name, record.id) |
|
|
|
|
|
if 'rev' not in vobj.contents and 'write_date' in record._fields: |
|
|
|
|
|
vobj.add('rev').value = record.write_date.strftime('%Y-%m-%dT%H%M%SZ') |
|
|
|
|
|
|
|
|
if "uid" not in vobj.contents: |
|
|
|
|
|
vobj.add("uid").value = "%s,%s" % (record._name, record.id) |
|
|
|
|
|
if "rev" not in vobj.contents and "write_date" in record._fields: |
|
|
|
|
|
vobj.add("rev").value = record.write_date.strftime("%Y-%m-%dT%H%M%SZ") |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def _odoo_to_http_datetime(self, value): |
|
|
def _odoo_to_http_datetime(self, value): |
|
|
return value.strftime('%a, %d %b %Y %H:%M:%S GMT') |
|
|
|
|
|
|
|
|
return value.strftime("%a, %d %b %Y %H:%M:%S GMT") |
|
|
|
|
|
|
|
|
@api.model |
|
|
@api.model |
|
|
def _split_path(self, path): |
|
|
def _split_path(self, path): |
|
|
return list(filter( |
|
|
|
|
|
None, os.path.normpath(path or '').strip('/').split('/') |
|
|
|
|
|
)) |
|
|
|
|
|
|
|
|
return list(filter(None, os.path.normpath(path or "").strip("/").split("/"))) |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def dav_list(self, collection, path_components): |
|
|
def dav_list(self, collection, path_components): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
|
|
|
|
|
|
if self.dav_type == 'files': |
|
|
|
|
|
|
|
|
if self.dav_type == "files": |
|
|
if len(path_components) == 3: |
|
|
if len(path_components) == 3: |
|
|
collection_model = self.env[self.model_id.model] |
|
|
collection_model = self.env[self.model_id.model] |
|
|
record = collection_model.browse(map( |
|
|
|
|
|
itemgetter(0), |
|
|
|
|
|
collection_model.name_search( |
|
|
|
|
|
path_components[2], operator='=', limit=1, |
|
|
|
|
|
|
|
|
record = collection_model.browse( |
|
|
|
|
|
map( |
|
|
|
|
|
itemgetter(0), |
|
|
|
|
|
collection_model.name_search( |
|
|
|
|
|
path_components[2], |
|
|
|
|
|
operator="=", |
|
|
|
|
|
limit=1, |
|
|
|
|
|
), |
|
|
) |
|
|
) |
|
|
)) |
|
|
|
|
|
|
|
|
) |
|
|
return [ |
|
|
return [ |
|
|
'/' + '/'.join( |
|
|
|
|
|
path_components + [quote_plus(attachment.name)] |
|
|
|
|
|
|
|
|
"/" + "/".join(path_components + [quote_plus(attachment.name)]) |
|
|
|
|
|
for attachment in self.env["ir.attachment"].search( |
|
|
|
|
|
[ |
|
|
|
|
|
("type", "=", "binary"), |
|
|
|
|
|
("res_model", "=", record._name), |
|
|
|
|
|
("res_id", "=", record.id), |
|
|
|
|
|
] |
|
|
) |
|
|
) |
|
|
for attachment in self.env['ir.attachment'].search([ |
|
|
|
|
|
('type', '=', 'binary'), |
|
|
|
|
|
('res_model', '=', record._name), |
|
|
|
|
|
('res_id', '=', record.id), |
|
|
|
|
|
]) |
|
|
|
|
|
] |
|
|
] |
|
|
elif len(path_components) == 2: |
|
|
elif len(path_components) == 2: |
|
|
return [ |
|
|
return [ |
|
|
'/' + '/'.join( |
|
|
|
|
|
path_components + [quote_plus(record.display_name)] |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
"/" + "/".join(path_components + [quote_plus(record.display_name)]) |
|
|
for record in self.eval() |
|
|
for record in self.eval() |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
@ -214,11 +208,16 @@ class DavCollection(models.Model): |
|
|
uuid = record[self.field_uuid.name] |
|
|
uuid = record[self.field_uuid.name] |
|
|
else: |
|
|
else: |
|
|
uuid = str(record.id) |
|
|
uuid = str(record.id) |
|
|
href = '/' + '/'.join(path_components + [uuid]) |
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
href = "/" + "/".join(path_components + [str(uuid)]) |
|
|
|
|
|
except: |
|
|
|
|
|
import ipdb |
|
|
|
|
|
|
|
|
|
|
|
ipdb.set_trace() |
|
|
|
|
|
|
|
|
result.append(self.dav_get(collection, href)) |
|
|
result.append(self.dav_get(collection, href)) |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def dav_delete(self, collection, components): |
|
|
def dav_delete(self, collection, components): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
|
|
|
|
|
@ -228,13 +227,12 @@ class DavCollection(models.Model): |
|
|
else: |
|
|
else: |
|
|
self.get_record(components).unlink() |
|
|
self.get_record(components).unlink() |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def dav_upload(self, collection, href, item): |
|
|
def dav_upload(self, collection, href, item): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
|
|
|
|
|
|
components = self._split_path(href) |
|
|
components = self._split_path(href) |
|
|
collection_model = self.env[self.model_id.model] |
|
|
collection_model = self.env[self.model_id.model] |
|
|
if self.dav_type == 'files': |
|
|
|
|
|
|
|
|
if self.dav_type == "files": |
|
|
# TODO: Handle upload of attachments |
|
|
# TODO: Handle upload of attachments |
|
|
return None |
|
|
return None |
|
|
data = self.from_vobject(item) |
|
|
data = self.from_vobject(item) |
|
@ -257,37 +255,41 @@ class DavCollection(models.Model): |
|
|
last_modified=self._odoo_to_http_datetime(record.write_date), |
|
|
last_modified=self._odoo_to_http_datetime(record.write_date), |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
@api.multi |
|
|
|
|
|
def dav_get(self, collection, href): |
|
|
def dav_get(self, collection, href): |
|
|
self.ensure_one() |
|
|
self.ensure_one() |
|
|
|
|
|
|
|
|
components = self._split_path(href) |
|
|
components = self._split_path(href) |
|
|
collection_model = self.env[self.model_id.model] |
|
|
collection_model = self.env[self.model_id.model] |
|
|
if self.dav_type == 'files': |
|
|
|
|
|
|
|
|
if self.dav_type == "files": |
|
|
if len(components) == 3: |
|
|
if len(components) == 3: |
|
|
result = Collection(href) |
|
|
result = Collection(href) |
|
|
result.logger = self.logger |
|
|
result.logger = self.logger |
|
|
return result |
|
|
return result |
|
|
if len(components) == 4: |
|
|
if len(components) == 4: |
|
|
record = collection_model.browse(map( |
|
|
|
|
|
itemgetter(0), |
|
|
|
|
|
collection_model.name_search( |
|
|
|
|
|
components[2], operator='=', limit=1, |
|
|
|
|
|
|
|
|
record = collection_model.browse( |
|
|
|
|
|
map( |
|
|
|
|
|
itemgetter(0), |
|
|
|
|
|
collection_model.name_search( |
|
|
|
|
|
components[2], |
|
|
|
|
|
operator="=", |
|
|
|
|
|
limit=1, |
|
|
|
|
|
), |
|
|
) |
|
|
) |
|
|
)) |
|
|
|
|
|
attachment = self.env['ir.attachment'].search([ |
|
|
|
|
|
('type', '=', 'binary'), |
|
|
|
|
|
('res_model', '=', record._name), |
|
|
|
|
|
('res_id', '=', record.id), |
|
|
|
|
|
('name', '=', components[3]), |
|
|
|
|
|
], limit=1) |
|
|
|
|
|
|
|
|
) |
|
|
|
|
|
attachment = self.env["ir.attachment"].search( |
|
|
|
|
|
[ |
|
|
|
|
|
("type", "=", "binary"), |
|
|
|
|
|
("res_model", "=", record._name), |
|
|
|
|
|
("res_id", "=", record.id), |
|
|
|
|
|
("name", "=", components[3]), |
|
|
|
|
|
], |
|
|
|
|
|
limit=1, |
|
|
|
|
|
) |
|
|
return FileItem( |
|
|
return FileItem( |
|
|
collection, |
|
|
collection, |
|
|
item=attachment, |
|
|
item=attachment, |
|
|
href=href, |
|
|
href=href, |
|
|
last_modified=self._odoo_to_http_datetime( |
|
|
|
|
|
record.write_date |
|
|
|
|
|
), |
|
|
|
|
|
|
|
|
last_modified=self._odoo_to_http_datetime(record.write_date), |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
record = self.get_record(components) |
|
|
record = self.get_record(components) |
|
|