Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Available addons
----------------
addon | version | maintainers | summary
--- | --- | --- | ---
[base_dav](base_dav/) | 17.0.1.0.0 | | Access Odoo data as calendar or address book
[base_external_dbsource](base_external_dbsource/) | 17.0.1.0.0 | | External Database Sources
[base_external_dbsource_mysql](base_external_dbsource_mysql/) | 17.0.1.0.0 | | External Database Source - MySQL
[base_external_dbsource_sqlite](base_external_dbsource_sqlite/) | 17.0.1.0.0 | <a href='https://github.com/anddago78'><img src='https://github.com/anddago78.png' width='32' height='32' style='border-radius:50%;' alt='anddago78'/></a> | External Database Source - SQLite
Expand Down
129 changes: 129 additions & 0 deletions base_dav/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
==========================
CalDAV and CardDAV support
==========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:578689b63f845d289f6e066d4fc9b1aa0b82010191c91b2fc3518195109f55f1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--backend-lightgray.png?logo=github
:target: https://github.com/OCA/server-backend/tree/17.0/base_dav
:alt: OCA/server-backend
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-backend-17-0/server-backend-17-0-base_dav
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-backend&target_branch=17.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds WebDAV support to Odoo, specifically CalDAV and
CardDAV.

You can configure arbitrary objects as a calendar or an address book,
thus making arbitrary information accessible in external systems or your
mobile device.

**Table of contents**

.. contents::
:local:

Installation
============

This module requires the ``radicale`` and ``vobject`` Python libraries:

::

pip install radicale vobject python-dateutil

Requirements:

- radicale >= 3.0
- vobject
- python-dateutil

Configuration
=============

Go to Settings > WebDAV Collections to:

1. Create new collections
2. Select the model (``res.partner``, ``calendar.event``, etc.)
3. Define domain filters (e.g. user-specific data)
4. Configure field mappings for vCard/vCalendar export

Available access rights per collection:

- Owner Only: only the owner can read/write
- Owner Write Only: everyone can read, only the owner can write
- Authenticated: all authenticated users can read/write

Usage
=====

After installation, users can access their data via CardDAV/CalDAV using
their Odoo credentials:

- CardDAV (Contacts): ``/.dav/{username}/{collection_id}/``
- CalDAV (Calendar): ``/.dav/{username}/{collection_id}/``

Example URL for contacts:

::

https://your-odoo.com/.dav/admin/1/

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-backend/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-backend/issues/new?body=module:%20base_dav%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* initOS GmbH
* Therp BV

Contributors
------------

- `Therp BV <https://therp.nl>`__
- `initOS GmbH <https://initos.com>`__

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/server-backend <https://github.com/OCA/server-backend/tree/17.0/base_dav>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
5 changes: 5 additions & 0 deletions base_dav/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright 2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import models
from . import controllers
from . import radicale
33 changes: 33 additions & 0 deletions base_dav/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2018 Therp BV <https://therp.nl>
# Copyright 2019-2020 initOS GmbH <https://initos.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "CalDAV and CardDAV support",
"version": "17.0.1.0.0",
"author": "initOS GmbH,Therp BV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-backend",
"license": "AGPL-3",
"category": "Extra Tools",
"summary": "Access Odoo data as calendar or address book",
"development_status": "Beta",
"depends": [
"base",
],
"demo": [
"demo/dav_collection.xml",
],
"data": [
"views/dav_collection.xml",
"security/ir.model.access.csv",
],
"external_dependencies": {
"python": [
"radicale",
"vobject",
"dateutil",
],
},
"application": True,
"auto_install": False,
"installable": True,
}
3 changes: 3 additions & 0 deletions base_dav/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Copyright 2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from . import main
89 changes: 89 additions & 0 deletions base_dav/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2018 Therp BV <https://therp.nl>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

import logging
from configparser import RawConfigParser as ConfigParser

import werkzeug

from odoo import http
from odoo.http import request

try:
import radicale
except ImportError:
radicale = None

PREFIX = "/.dav"


class Main(http.Controller):
@http.route(
["/.well-known/carddav", "/.well-known/caldav", "/.well-known/webdav"],
type="http",
auth="none",
csrf=False,
methods=["GET", "PROPFIND", "REPORT", "PUT", "DELETE", "OPTIONS"],
)
def handle_well_known_request(self):
return werkzeug.utils.redirect(PREFIX, 301)

@http.route(
[
PREFIX,
PREFIX + "/<path:davpath>",
],
type="http",
auth="none",
csrf=False,
methods=[
"GET",
"POST",
"PROPFIND",
"PROPPATCH",
"PUT",
"DELETE",
"MKCALENDAR",
"REPORT",
"OPTIONS",
"PATCH",
],
)
def handle_dav_request(self, davpath=None):
if radicale is None:
return http.Response(
"Radicale is not installed. "
"Please install it with: pip install radicale",
status=500,
)

config = ConfigParser()
for section, values in radicale.config.INITIAL_CONFIG.items():
config.add_section(section)
for key, data in values.items():
config.set(section, key, data["value"])
config.set("auth", "type", "odoo.addons.base_dav.radicale.auth")
config.set("storage", "type", "odoo.addons.base_dav.radicale.collection")
config.set("rights", "type", "odoo.addons.base_dav.radicale.rights")
config.set("web", "type", "none")
application = radicale.Application(
config,
logging.getLogger("radicale"),
)

response = None

def start_response(status, headers):
nonlocal response
response = http.Response(status=status, headers=headers)

result = application(
dict(
request.httprequest.environ,
HTTP_X_SCRIPT_NAME=PREFIX,
PATH_INFO=davpath or "",
),
start_response,
)
response.stream.write(result and result[0] or b"")
return response
99 changes: 99 additions & 0 deletions base_dav/demo/dav_collection.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="collection_addressbook" model="dav.collection">
<field name="name">Addressbook</field>
<field name="dav_type">addressbook</field>
<field name="model_id" ref="base.model_res_partner" />
<field name="domain">[]</field>
</record>
<record id="field_mapping_addressbook_n" model="dav.collection.field_mapping">
<field name="name">N</field>
<field name="field_id" ref="base.field_res_partner__name" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_fn" model="dav.collection.field_mapping">
<field name="name">FN</field>
<field name="field_id" ref="base.field_res_partner__display_name" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_photo" model="dav.collection.field_mapping">
<field name="name">photo</field>
<field name="field_id" ref="base.field_res_partner__image_1920" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_email" model="dav.collection.field_mapping">
<field name="name">email</field>
<field name="field_id" ref="base.field_res_partner__email" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_tel" model="dav.collection.field_mapping">
<field name="name">tel</field>
<field name="field_id" ref="base.field_res_partner__phone" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_mobile" model="dav.collection.field_mapping">
<field name="name">mobile</field>
<field name="field_id" ref="base.field_res_partner__mobile" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_org" model="dav.collection.field_mapping">
<field name="name">ORG</field>
<field name="field_id" ref="base.field_res_partner__commercial_company_name" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_title" model="dav.collection.field_mapping">
<field name="name">TITLE</field>
<field name="field_id" ref="base.field_res_partner__function" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_url" model="dav.collection.field_mapping">
<field name="name">URL</field>
<field name="field_id" ref="base.field_res_partner__website" />
<field name="collection_id" ref="collection_addressbook" />
</record>
<record id="field_mapping_addressbook_street" model="dav.collection.field_mapping">
<field name="name">ADR</field>
<field name="field_id" ref="base.field_res_partner__street" />
<field name="collection_id" ref="collection_addressbook" />
</record>

<record id="collection_calendar" model="dav.collection">
<field name="name">Calendar</field>
<field name="dav_type">calendar</field>
<field name="model_id" ref="calendar.model_calendar_event" />
<field name="domain">[('user_id', '=', user.id)]</field>
</record>
<record id="field_mapping_calendar_summary" model="dav.collection.field_mapping">
<field name="name">SUMMARY</field>
<field name="field_id" ref="calendar.field_calendar_event__name" />
<field name="collection_id" ref="collection_calendar" />
</record>
<record
id="field_mapping_calendar_description"
model="dav.collection.field_mapping"
>
<field name="name">DESCRIPTION</field>
<field name="field_id" ref="calendar.field_calendar_event__description" />
<field name="collection_id" ref="collection_calendar" />
</record>
<record id="field_mapping_calendar_dtstart" model="dav.collection.field_mapping">
<field name="name">DTSTART</field>
<field name="field_id" ref="calendar.field_calendar_event__start" />
<field name="collection_id" ref="collection_calendar" />
</record>
<record id="field_mapping_calendar_dtend" model="dav.collection.field_mapping">
<field name="name">DTEND</field>
<field name="field_id" ref="calendar.field_calendar_event__stop" />
<field name="collection_id" ref="collection_calendar" />
</record>
<record id="field_mapping_calendar_location" model="dav.collection.field_mapping">
<field name="name">LOCATION</field>
<field name="field_id" ref="calendar.field_calendar_event__location" />
<field name="collection_id" ref="collection_calendar" />
</record>
<record id="field_mapping_calendar_uid" model="dav.collection.field_mapping">
<field name="name">UID</field>
<field name="field_id" ref="calendar.field_calendar_event__uid" />
<field name="collection_id" ref="collection_calendar" />
</record>
</odoo>
Loading
Loading