You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
4.5 KiB

  1. # Copyright 2018 Therp BV <https://therp.nl>
  2. # Copyright 2019-2020 initOS GmbH <https://initos.com>
  3. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  4. import base64
  5. import os
  6. import time
  7. from odoo.http import request
  8. try:
  9. from radicale.storage import BaseCollection, BaseStorage
  10. from radicale.item import Item, get_etag
  11. from radicale import types
  12. except ImportError:
  13. BaseCollection = None
  14. Item = None
  15. get_etag = None
  16. class BytesPretendingToBeString(bytes):
  17. # radicale expects a string as file content, so we provide the str
  18. # functions needed
  19. def encode(self, encoding):
  20. return self
  21. class FileItem(Item):
  22. """this item tricks radicalev into serving a plain file"""
  23. @property
  24. def name(self):
  25. return 'VCARD'
  26. def serialize(self):
  27. return BytesPretendingToBeString(base64.b64decode(self.item.datas))
  28. @property
  29. def etag(self):
  30. return get_etag(self.item.datas.decode('ascii'))
  31. class Storage(BaseStorage):
  32. @classmethod
  33. @types.contextmanager
  34. def acquire_lock(cls, mode, user=None):
  35. """We have a database for that"""
  36. yield
  37. @classmethod
  38. def discover(cls, path, depth=None):
  39. depth = int(depth or "0")
  40. components = cls._split_path(path)
  41. collection = Collection(path)
  42. collection.logger = collection.collection.get_logger()
  43. if len(components) > 2:
  44. # TODO: this probably better should happen in some dav.collection
  45. # function
  46. if collection.collection.dav_type == 'files' and depth:
  47. for href in collection.list():
  48. yield collection.get(href)
  49. return
  50. yield collection.get(path)
  51. return
  52. yield collection
  53. if depth and len(components) == 1:
  54. for collection in request.env['dav.collection'].search([]):
  55. yield cls('/'.join(components + ['/%d' % collection.id]))
  56. if depth and len(components) == 2:
  57. for href in collection.list():
  58. yield collection.get(href)
  59. @classmethod
  60. def _split_path(cls, path):
  61. return list(filter(
  62. None, os.path.normpath(path or '').strip('/').split('/')
  63. ))
  64. @classmethod
  65. def create_collection(cls, href, collection=None, props=None):
  66. return Collection(href)
  67. class Collection(BaseCollection):
  68. @classmethod
  69. def _split_path(cls, path):
  70. return list(filter(
  71. None, os.path.normpath(path or '').strip('/').split('/')
  72. ))
  73. @property
  74. def env(self):
  75. return request.env
  76. @property
  77. def last_modified(self):
  78. return self._odoo_to_http_datetime(self.collection.create_date)
  79. @property
  80. def path(self):
  81. return '/'.join(self.path_components) or '/'
  82. def __init__(self, path):
  83. self.path_components = self._split_path(path)
  84. self._path = '/'.join(self.path_components) or '/'
  85. self.collection = self.env['dav.collection']
  86. if len(self.path_components) >= 2 and str(
  87. self.path_components[1]
  88. ).isdigit():
  89. self.collection = self.env['dav.collection'].browse(int(
  90. self.path_components[1]
  91. ))
  92. def _odoo_to_http_datetime(self, value):
  93. return time.strftime(
  94. '%a, %d %b %Y %H:%M:%S GMT',
  95. value.timetuple(),
  96. )
  97. def get_meta(self, key=None):
  98. if key is None:
  99. return {}
  100. elif key == 'tag':
  101. return self.collection.tag
  102. elif key == 'D:displayname':
  103. return self.collection.display_name
  104. elif key == 'C:supported-calendar-component-set':
  105. return 'VTODO,VEVENT,VJOURNAL'
  106. elif key == 'C:calendar-home-set':
  107. return None
  108. elif key == 'D:principal-URL':
  109. return None
  110. elif key == 'ICAL:calendar-color':
  111. # TODO: set in dav.collection
  112. return '#48c9f4'
  113. elif key == 'C:calendar-description':
  114. return self.collection.name
  115. self.logger.warning('unsupported metadata %s', key)
  116. def get_multi(self, hrefs):
  117. return [self.collection.dav_get(self, href) for href in hrefs]
  118. def upload(self, href, vobject_item):
  119. return self.collection.dav_upload(self, href, vobject_item)
  120. def delete(self, href):
  121. return self.collection.dav_delete(self, self._split_path(href))
  122. def get_all(self):
  123. return self.collection.dav_list(self, self.path_components)