testing via the interface seems to be working, though some parameters are not set to sync yet.
This commit is contained in:
parent
9d2ea9a8fa
commit
957de3221c
3 changed files with 100 additions and 44 deletions
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from . import calendar_event
|
||||
from . import res_users
|
||||
from . import caldav_calendar
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
from odoo import models, fields
|
||||
|
||||
class CaldavCalendar(models.Model):
|
||||
_name = 'caldav.calendar'
|
||||
_description = 'CalDAV Calendar'
|
||||
|
||||
user_id = fields.Many2one('res.users', string='User', required=True, ondelete='cascade')
|
||||
name = fields.Char('Name', required=True)
|
||||
url = fields.Char('URL', required=True)
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import uuid
|
||||
from odoo import models, api, fields
|
||||
import caldav
|
||||
from caldav.elements import dav, cdav
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from icalendar import Calendar, Event
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -12,18 +14,35 @@ class CalendarEvent(models.Model):
|
|||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
if not vals.get('caldav_uid'):
|
||||
vals['caldav_uid'] = str(uuid.uuid4())
|
||||
event = super(CalendarEvent, self).create(vals)
|
||||
event.sync_to_caldav()
|
||||
if not self.env.context.get('caldav_no_sync'):
|
||||
try:
|
||||
_logger.debug(f"Creating event {event.name} in CalDAV")
|
||||
event.sync_create_to_caldav()
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to create event in CalDAV server: {e}")
|
||||
return event
|
||||
|
||||
def write(self, vals):
|
||||
res = super(CalendarEvent, self).write(vals)
|
||||
self.sync_to_caldav()
|
||||
if not self.env.context.get('caldav_no_sync'):
|
||||
try:
|
||||
_logger.debug(f"Updating event {self.name} in CalDAV")
|
||||
self.with_context(caldav_no_sync=True).sync_update_to_caldav()
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to update event in CalDAV server: {e}")
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
for event in self:
|
||||
event.remove_from_caldav()
|
||||
if not self.env.context.get('caldav_no_sync'):
|
||||
for event in self:
|
||||
try:
|
||||
_logger.debug(f"Removing event {event.name} from CalDAV")
|
||||
event.with_context(caldav_no_sync=True).sync_remove_from_caldav()
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to delete event from CalDAV server: {e}")
|
||||
return super(CalendarEvent, self).unlink()
|
||||
|
||||
def _is_caldav_enabled(self):
|
||||
|
|
@ -38,45 +57,68 @@ class CalendarEvent(models.Model):
|
|||
password=user.caldav_password
|
||||
)
|
||||
|
||||
def sync_to_caldav(self):
|
||||
def sync_create_to_caldav(self):
|
||||
if not self._is_caldav_enabled():
|
||||
return
|
||||
client = self._get_caldav_client()
|
||||
calendar = client.calendar(self.env.user.caldav_calendar_url)
|
||||
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
||||
for event in self:
|
||||
ical_event = event._get_icalendar()
|
||||
if event.caldav_uid:
|
||||
caldav_event = calendar.event_by_uid(event.caldav_uid)
|
||||
caldav_event.save(ical_event)
|
||||
else:
|
||||
try:
|
||||
_logger.debug(f"Creating new CalDAV event for {event.name}")
|
||||
caldav_event = calendar.add_event(ical_event)
|
||||
event.caldav_uid = caldav_event.id
|
||||
caldav_uid = caldav_event.vobject_instance.vevent.uid.value
|
||||
_logger.debug(f"New CalDAV UID: {caldav_uid}")
|
||||
event.with_context(caldav_no_sync=True).write({'caldav_uid': caldav_uid})
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to sync event to CalDAV server: {e}")
|
||||
|
||||
def remove_from_caldav(self):
|
||||
def sync_update_to_caldav(self):
|
||||
if not self._is_caldav_enabled():
|
||||
return
|
||||
client = self._get_caldav_client()
|
||||
calendar = client.calendar(self.env.user.caldav_calendar_url)
|
||||
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
||||
for event in self:
|
||||
ical_event = event._get_icalendar()
|
||||
try:
|
||||
_logger.debug(f"Updating existing CalDAV event {event.caldav_uid}")
|
||||
calendar.save_event(ical=ical_event)
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to sync event to CalDAV server: {e}")
|
||||
|
||||
def sync_remove_from_caldav(self):
|
||||
if not self._is_caldav_enabled():
|
||||
return
|
||||
client = self._get_caldav_client()
|
||||
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
||||
for event in self:
|
||||
if event.caldav_uid:
|
||||
caldav_event = calendar.event_by_uid(event.caldav_uid)
|
||||
caldav_event.delete()
|
||||
try:
|
||||
_logger.debug(f"Removing CalDAV event {event.caldav_uid}")
|
||||
caldav_event = calendar.event_by_uid(event.caldav_uid)
|
||||
caldav_event.delete()
|
||||
except caldav.error.NotFoundError:
|
||||
_logger.warning(f"CalDAV event {event.caldav_uid} not found on server.")
|
||||
except Exception as e:
|
||||
_logger.error(f"Failed to remove event from CalDAV server: {e}")
|
||||
|
||||
def _get_icalendar(self):
|
||||
from icalendar import Calendar, Event
|
||||
calendar = Calendar()
|
||||
calendar.add('prodid', '-//Odoo//mxm.dk//')
|
||||
calendar.add('version', '2.0')
|
||||
|
||||
for event in self:
|
||||
ical_event = Event()
|
||||
ical_event.add('uid', event.caldav_uid or '')
|
||||
ical_event.add('dtstamp', event.write_date)
|
||||
ical_event.add('dtstart', event.start)
|
||||
ical_event.add('dtend', event.stop)
|
||||
ical_event.add('summary', event.name)
|
||||
ical_event.add('description', event.description)
|
||||
ical_event.add('location', event.location)
|
||||
ical_event.add('uid', event.caldav_uid)
|
||||
ical_event.add('dtstamp', event.write_date.replace(tzinfo=None))
|
||||
ical_event.add('dtstart', event.start.replace(tzinfo=None))
|
||||
ical_event.add('dtend', event.stop.replace(tzinfo=None))
|
||||
if event.name:
|
||||
ical_event.add('summary', event.name)
|
||||
if event.description:
|
||||
ical_event.add('description', event.description)
|
||||
if event.location:
|
||||
ical_event.add('location', event.location)
|
||||
calendar.add_component(ical_event)
|
||||
|
||||
return calendar.to_ical()
|
||||
|
|
@ -94,32 +136,56 @@ class CalendarEvent(models.Model):
|
|||
if not self._is_caldav_enabled():
|
||||
return
|
||||
client = self._get_caldav_client()
|
||||
calendar = client.calendar(self.env.user.caldav_calendar_url)
|
||||
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
||||
events = calendar.events()
|
||||
caldav_uids = set()
|
||||
|
||||
_logger.info(f"Polling CalDAV server for user {self.env.user.name}")
|
||||
|
||||
for caldav_event in events:
|
||||
ical_event = caldav_event.icalendar_instance
|
||||
self.sync_event_from_ical(ical_event)
|
||||
ical_event = caldav_event.icalendar_instance
|
||||
self.sync_event_from_ical(ical_event)
|
||||
for component in ical_event.subcomponents:
|
||||
if isinstance(component, Event):
|
||||
uid = str(component.get('uid'))
|
||||
caldav_uids.add(uid)
|
||||
|
||||
_logger.info(f"CalDAV UIDs fetched: {caldav_uids}")
|
||||
|
||||
# Remove Odoo events that no longer exist on the CalDAV server
|
||||
odoo_events = self.search([('caldav_uid', '!=', False)])
|
||||
for event in odoo_events:
|
||||
if event.caldav_uid not in caldav_uids:
|
||||
_logger.info(f"Deleting orphan event {event.name} with UID {event.caldav_uid}")
|
||||
event.with_context(caldav_no_sync=True).unlink()
|
||||
|
||||
def sync_event_from_ical(self, ical_event):
|
||||
from icalendar import Event
|
||||
for component in ical_event.subcomponents:
|
||||
if isinstance(component, Event):
|
||||
uid = str(component.get('uid'))
|
||||
event = self.search([('caldav_uid', '=', uid)], limit=1)
|
||||
start = component.decoded('dtstart')
|
||||
stop = component.decoded('dtend')
|
||||
if isinstance(start, datetime):
|
||||
start = start.replace(tzinfo=None)
|
||||
if isinstance(stop, datetime):
|
||||
stop = stop.replace(tzinfo=None)
|
||||
if not event:
|
||||
self.create({
|
||||
_logger.info(f"Creating new event {str(component.get('summary'))} with UID {uid}")
|
||||
self.with_context({'caldav_no_sync': True}).create({
|
||||
'name': str(component.get('summary')),
|
||||
'start': component.decoded('dtstart'),
|
||||
'stop': component.decoded('dtend'),
|
||||
'start': start,
|
||||
'stop': stop,
|
||||
'description': str(component.get('description')),
|
||||
'location': str(component.get('location')),
|
||||
'caldav_uid': uid,
|
||||
})
|
||||
else:
|
||||
event.write({
|
||||
_logger.info(f"Updating existing event {event.name} with UID {uid}")
|
||||
event.with_context({'caldav_no_sync': True}).write({
|
||||
'name': str(component.get('summary')),
|
||||
'start': component.decoded('dtstart'),
|
||||
'stop': component.decoded('dtend'),
|
||||
'start': start,
|
||||
'stop': stop,
|
||||
'description': str(component.get('description')),
|
||||
'location': str(component.get('location')),
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue