Conversation
6d0d5a1 to
6457675
Compare
|
Hello, |
| "summary": "Access Odoo data as calendar or address book", | ||
| "website": "https://github.com/OCA/server-backend", | ||
| "depends": [ | ||
| "base", |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| if value is None or value is False: | ||
| continue | ||
| if isinstance(value, bool): | ||
| continue |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| 1. Go to Settings / WebDAV Collections and create or edit your | ||
| collections. There, you'll also see the URL to point your clients | ||
| to. | ||
|
|
There was a problem hiding this comment.
It would be cool to :
- add examples for mapping fields
- recommend using an API Key instead of the user password in the client
- add domain example (for example for personal calendar)
There was a problem hiding this comment.
It would be cool to :
- add examples for mapping fields
- recommend using an API Key instead of the user password in the client
- add domain example (for example for personal calendar)
@cvinh Good point, bit I'm not sure that these are the tasks we should do during module migration,
I suggest we create a new PR with revised documentation when we merge this PR.
| :rtype: odoo.models.BaseModel | ||
| """ | ||
| self.ensure_one() | ||
| return self.env[self.model_id.model].search(self._eval_domain()) |
There was a problem hiding this comment.
I have access rights problems when trying to call self.model_id (cannot read ir_model)...
Is the solution to use sudo() ?
| return self.env[self.model_id.model].search(self._eval_domain()) | |
| return self.env[self.sudo().model_id.model].search(self._eval_domain()) |
It happens also with self.field_uuid and self.field_id
There was a problem hiding this comment.
Thank you!
I've applied it everywhere needed
hbrunn
left a comment
There was a problem hiding this comment.
thank you, this looks very good on first sight.
In v18, you can reasonably test this against the caldav lib in a HttpCase, so please do so
| for every project and appointments are tasks, or a calendar for every | ||
| sales team and appointments are sale orders. Lots of possibilities. | ||
|
|
||
| Backporting this to \<=v10 will be tricky because radicale only supports |
There was a problem hiding this comment.
I don't think we need this any more
| :return: Access mode: | ||
| - "rw" for read/write | ||
| - "r" for read-only | ||
| - "" for no access |
| if key == "C:supported-calendar-component-set": | ||
| return "VTODO,VEVENT,VJOURNAL" | ||
| if key == "ICAL:calendar-color": | ||
| return "#48c9f4" |
There was a problem hiding this comment.
the TODO here is still valid I think
| @@ -1,2 +0,0 @@ | |||
| * Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_ | |||
| * All the actual work is done by `Radicale <https://radicale.org>`_ | |||
There was a problem hiding this comment.
I don't think this should go away
There was a problem hiding this comment.
Thanks for the feedback!
i deleted this file because I followed the migration documentation: https://arc.net/l/quote/bxjruewp
if you'd like it restored, just let me know and I'll revert it to its original state
There was a problem hiding this comment.
this is about sentences like "migration to version 42 financially supported by acme ltd", but here we refer to the project that does all of the heavy lifting, so you can just keep the file minus the icon because we don't mention this explicitly any more
|
Hi @tendil Thanks a lot for this PR. It comes at a very good timing for me, but it lacks some features I'm counting on. So base on your work, I started filling the gap:
It might be a good idea to align each other work, as this work is in progress. I'm also looking at improving some area like Here is my branch if you want to have a look: https://github.com/ikus060/server-backend/tree/base_dav-built-in-mapper |
|
@ikus060 this module is called base_dav, whatever you do with specific odoo models belongs into an addon depending on this one, and probably into another repository. It will be helpful if you review the migration as is with possibly an eye on extensibility to allow you doing what you want to do |
|
@hbrunn Thanks for your feedback.
Yes, I intend to split my changes into multiple modules in the future. But as I develop it's easier for me to place everything into the same module for now.
The current state is good enough for me. I will submit a new PR when I'm ready. |
Hi @ikus060 |
| ], | ||
| "external_dependencies": { | ||
| "python": ["radicale"], | ||
| }, |
There was a problem hiding this comment.
Missing
"installable" : True,
"application": False,
...?
There was a problem hiding this comment.
i didn't specify them on purpose because these are the default values - https://arc.net/l/quote/rentkjyl
Thanks! about the "hundreds of invitations" risk: the DAV proxy itself doesn’t send emails - it only forwards CalDAV/CardDAV requests to odoo create/write |
|
LGTM please squash commits and I'll approve |
973fc83 to
a902e02
Compare
I did it |
| { | ||
| "auth": {"type": "odoo.addons.base_dav.radicale.auth"}, | ||
| "storage": {"type": "odoo.addons.base_dav.radicale.collection"}, | ||
| "rights": {"type": "odoo.addons.base_dav.radicale.rights"}, | ||
| "web": {"type": "none"}, | ||
| "hook": {"type": "none"}, | ||
| }, |
There was a problem hiding this comment.
take this from a function on self to make it simple to override. it would be perfect if those classes would be AbstractModels, but I don't see a simple way to do that
|
|
||
| app = Application(configuration) | ||
|
|
||
| # Let's take WSGI environ from werkzeug/odoo |
There was a problem hiding this comment.
i find those llm-smelling comments somewhat jarring
| if method == "PROPFIND" and len(raw_body) == 0: | ||
| raw_body = ( | ||
| b'<?xml version="1.0" encoding="utf-8"?>' | ||
| b'<D:propfind xmlns:D="DAV:"><D:allprop/></D:propfind>' | ||
| ) |
There was a problem hiding this comment.
why do we need this? shouldn't radicale handle that? if we need it, it needs a comment why
|
|
||
| # Force Radicale to read the body we provide | ||
| environ["wsgi.input"] = io.BytesIO(raw_body) | ||
| environ["CONTENT_LENGTH"] = str(len(raw_body)) |
There was a problem hiding this comment.
why do you need to set this? should exist already
There was a problem hiding this comment.
wsgi.input is still set explicitly because radicale reads request bodies
from that wsgi key while in this odoo controller bridge it is not exposed in a usable form
if there's a better solution I'd appreciate any suggestions!
There was a problem hiding this comment.
do nothing and keep whatever content length the client has sent
| ) | ||
|
|
||
| # Force Radicale to read the body we provide | ||
| environ["wsgi.input"] = io.BytesIO(raw_body) |
There was a problem hiding this comment.
why not
| environ["wsgi.input"] = io.BytesIO(raw_body) | |
| environ["wsgi.input"] = request.httprequest._HTTPRequest__wrapped.stream |
There was a problem hiding this comment.
request.httprequest.stream is not exposed publicly here
rebuilding wsgi.input from get_data() gives radicale a plain readable stream with a matching CONTENT_LENGTH, independent of how odoo wraps the underlying werkzeug request
this is also the variant i verified locally while request.httprequest.stream is not exposed here
There was a problem hiding this comment.
as soon as this module fully supports files it will be needed because then we absolutely don't want to copy a potentially large upload here. will not press the point for now
| _logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def load(configuration): |
There was a problem hiding this comment.
thank you! confirmed: these module-level load() helpers are unused with the current radicale plugin loading flow
radicale loads the configured plugin module and instantiates the expected backend class directly, so these functions were dead code and have been removed
| return env | ||
|
|
||
|
|
||
| def _set_request_user(base_env, uid): |
There was a problem hiding this comment.
please avoid one line functions that are being used once and obfuscate stuff that's very standard Odoo
| } | ||
|
|
||
| try: | ||
| res = base_env["res.users"]._login( |
There was a problem hiding this comment.
going through login for every request slows down stuff considerably. i think it will be better to require apikeys and just do base_env['res.users.apikeys']._check_credentials. then you also don't need the synthesized user agent stuff.
also base_env is an odd name for a variable for an Odoo environment, that canonical name is env
There was a problem hiding this comment.
switched dav auth to _check_credentials()
i also keep validating that the resolved user login matches the basic auth login
| return "" | ||
|
|
||
| _set_request_user(base_env, uid) | ||
| return request.env["res.users"].sudo().browse(uid).login or login |
There was a problem hiding this comment.
the or login fallback is nonsense, remove
| vobj.add(mapping.name).value = value | ||
|
|
||
| if "uid" not in vobj.contents: | ||
| vobj.add("uid").value = f"{record._name},{record.id}" |
There was a problem hiding this comment.
I don't understand the logic to generate a UID as <model_name>,<id>. This is not symmetric with implementation found in def get_record() where we simply expect an "id".
So the right implementation here is to use id has the uid by default.
In addition, if the collection has a field_uuid, The mapper should use that too.
So the right implementation should read:
if "uid" not in vobj.contents:
vobj.add("uid").value = (
record[collection.field_uuid.name]
if collection.field_uuid
else str(record.id)
)
There was a problem hiding this comment.
agreed, thank you!
i changed the default uid generation to use the same identifier as the collection lookup
|
|
||
| # created_item.href can differ from requested href (Odoo assigns new id) | ||
| vobj = created_item.vobject_item | ||
| uid_value = "" |
There was a problem hiding this comment.
All of the following lines is non-sens too me.
vobj should be a vcalendar. We can directly get the uid using:
uid_value = vobj.contents['vevent'][0].uid
There was a problem hiding this comment.
agreed! i simplified the uid extraction accordingly
| } | ||
|
|
||
| if self._record.tag == "VCALENDAR": | ||
| metadata["C:supported-calendar-component-set"] = "VTODO,VEVENT,VJOURNAL" |
There was a problem hiding this comment.
I don't think it makes any send to have a collection that return VTODO and VEVENT.
As we would map either calendar event OR tasks.
What is the use case we are trying to covert here ?
I would recommand to change a bit the implementation so each collection return either VEVENT, VTODO, VJOURNAL or VCARD, etc.
Take note VJOURNAL is not really supported by anything.
There was a problem hiding this comment.
it no longer advertises mixed component support, so the collection is not misleading anymore and now exposes only VEVENT, which matches the current implementation
support other component types would require separate collection semantics and should be handled in a dedicated follow-up task
9b6a760 to
bed8559
Compare
| """ | ||
| configuration = radicale_config.load() | ||
| configuration.update(self._get_radicale_config(), "odoo") | ||
| app = Application(configuration) |
There was a problem hiding this comment.
@tendil I'm not sure this implementation is optimal. The application get re-created on every request. I think it should be turned into a single. What do you think ?
There was a problem hiding this comment.
agreed
i changed it to a lazily cached app instance at module level, so it is created once per odoo worker process instead of once per request
i used a module-level cache instead of controller state to keep the lifecycle explicit and make it easy to reset in tests
There was a problem hiding this comment.
@tendil Thanks for the update. I will test it soon.
I'm poking around because I'm currently working to implement CalDAV for calendar events on top of your work. 😄
|
Any update on this? |
Co-authored-by: Yannick Vaucher <yannick.vaucher@camptocamp.com>
Task: 5250
Task: 5250
Task: 5250
Task: 5250
6e6d5f2 to
14f10be
Compare
yes, the pr is ready for review; you can take a look however, after the rebase, there are issues with the tests in the |
e17c6e7 to
14f10be
Compare
Radicale re-registers logging level 25 as NOTICE during module import. Odoo uses the same numeric level for RUNBOT logs and expects it to be displayed as INFO. Because of this conflict, the standard "modules loaded..." log line is emitted as NOTICE and OCA checklog fails the pipeline even though tests pass. Restore the odoo mapping for RUNBOT after base_dav imports so level 25 is displayed as INFO again. Task: 5250
02aa0f5 to
ec62a45
Compare
|
@hbrunn, i would really appreciate it if you could review the code again! the test pipeline has been fixed |
Standard migration from 11.0