19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:32:34 +01:00
parent 5faf7397c5
commit 2696f14ed7
721 changed files with 220375 additions and 91221 deletions

View file

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import calendar_provider_config
from . import calendar_popover_delete_wizard
from . import mail_activity_schedule

View file

@ -0,0 +1,93 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class CalendarPopoverDeleteWizard(models.TransientModel):
_name = 'calendar.popover.delete.wizard'
_inherit = ['mail.composer.mixin']
_description = 'Calendar Popover Delete Wizard'
calendar_event_id = fields.Many2one('calendar.event', 'Calendar Event')
delete = fields.Selection([('one', 'Delete this event'), ('next', 'Delete this and following events'), ('all', 'Delete all the events')], default='one')
recipient_ids = fields.Many2many(
'res.partner',
string="Recipients",
compute='_compute_recipient_ids',
readonly=False,
)
def close(self):
# Return if there are multiple attendees or if the organizer's partner_id differs
if self.calendar_event_id.attendees_count != 1 or self.calendar_event_id.user_id.partner_id != self.calendar_event_id.partner_ids:
return self.calendar_event_id.action_unlink_event(self.calendar_event_id.partner_id.id, self.delete)
if not self.calendar_event_id or not self.delete:
pass
elif self.delete == 'one':
self.calendar_event_id.unlink()
else:
switch = {
'next': 'future_events',
'all': 'all_events'
}
self.calendar_event_id.action_mass_deletion(switch.get(self.delete, ''))
@api.depends('calendar_event_id')
def _compute_recipient_ids(self):
""" Compute the recipients by combining the record's partner and attendees partners. """
for wizard in self:
wizard.recipient_ids = wizard.calendar_event_id.partner_id | wizard.calendar_event_id.attendee_ids.partner_id
@api.depends('calendar_event_id')
def _compute_subject(self):
""" Compute the subject by rendering the template's subject field based on the event. """
for wizard in self.filtered('template_id'):
wizard.subject = wizard.template_id._render_field(
'subject',
[wizard.calendar_event_id.id],
compute_lang=True,
options={'post_process': True},
)[wizard.calendar_event_id.id]
@api.depends('calendar_event_id')
def _compute_body(self):
""" Compute the body by rendering the template's body HTML field based on the event. """
for wizard in self.filtered('template_id'):
wizard.body = wizard.template_id._render_field(
'body_html',
[wizard.calendar_event_id.id],
compute_lang=True,
options={'post_process': True},
)[wizard.calendar_event_id.id]
def action_delete(self):
"""
Delete the event based on the specified deletion type.
:return: Action URL to redirect to the calendar view
"""
self.ensure_one()
event = self.calendar_event_id
deletion_type = self.env.context.get('default_recurrence')
# Unlink recurrent events.
if event.recurrency:
if deletion_type in ['one', 'self_only']:
event.unlink()
elif deletion_type in ['next', 'all']:
event.action_mass_deletion('future_events' if deletion_type == 'next' else 'all_events')
else:
event.unlink()
return {
'type': 'ir.actions.act_url',
'target': 'self',
'url': '/odoo/calendar'
}
def action_send_mail_and_delete(self):
""" Send email notification and delete the event based on the specified deletion type. """
self.env.ref('calendar.calendar_template_delete_event').send_mail(
self.calendar_event_id.id, email_layout_xmlid='mail.mail_notification_light', force_send=True
)
return self.action_delete()

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="calendar_popover_delete_view" model="ir.ui.view">
<field name="name">calendar.popover.delete.wizard.view.form</field>
<field name="model">calendar.popover.delete.wizard</field>
<field name="arch" type="xml">
<form string="Delete Event">
<field name="delete" widget="radio"/>
<footer>
<button name="close" string="Submit" type="object" class="btn-primary" data-hotkey="q"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x"/>
</footer>
</form>
</field>
</record>
<record id="view_event_delete_wizard_form" model="ir.ui.view">
<field name="name">calendar.popover.delete.wizard.form</field>
<field name="model">calendar.popover.delete.wizard</field>
<field name="arch" type="xml">
<form string="Delete Event">
<field name="calendar_event_id" invisible="1"/>
<div col="2" class="alert alert-warning" role="alert">
<span>Are you sure you want to delete this event? <br/></span>
</div>
<group col="2">
<field name="recipient_ids"
widget="many2many_tags_email"
context="{'force_email': True,' show_email': True, 'no_create_edit': True}"
/>
</group>
<group col="2">
<field name="subject" placeholder="Subject"/>
</group>
<field name="body"
class="oe-bordered-editor"
options="{'style-inline': true}"/>
<footer>
<button string="Send and delete"
name="action_send_mail_and_delete"
type="object"
class="btn-primary o_cw_send_notify_delete"/>
<button string="Delete"
name="action_delete"
type="object"
class="btn-primary mx-1"/>
<button string="Discard"
class="btn-secondary"
special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_event_delete_wizard" model="ir.actions.act_window">
<field name="name">Event Cancel Wizard</field>
<field name="res_model">calendar.popover.delete.wizard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_event_delete_wizard_form"/>
<field name="target">new</field>
<field name="context">{}</field>
</record>
</odoo>

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
from odoo.addons.base.models.ir_module import assert_log_admin_access
from odoo.tools import str2bool
class CalendarProviderConfig(models.TransientModel):
@ -21,12 +21,18 @@ class CalendarProviderConfig(models.TransientModel):
cal_client_secret = fields.Char(
"Google Client_key",
default=lambda self: self.env['ir.config_parameter'].get_param('google_calendar_client_secret'))
cal_sync_paused = fields.Boolean(
"Google Synchronization Paused",
default=lambda self: str2bool(self.env['ir.config_parameter'].get_param('google_calendar_sync_paused'), default=False))
microsoft_outlook_client_identifier = fields.Char(
"Outlook Client Id",
default=lambda self: self.env['ir.config_parameter'].get_param('microsoft_calendar_client_id'))
microsoft_outlook_client_secret = fields.Char(
"Outlook Client Secret",
default=lambda self: self.env['ir.config_parameter'].get_param('microsoft_calendar_client_secret'))
microsoft_outlook_sync_paused = fields.Boolean(
"Outlook Synchronization Paused",
default=lambda self: str2bool(self.env['ir.config_parameter'].get_param('microsoft_calendar_sync_paused'), default=False))
@assert_log_admin_access
def action_calendar_prepare_external_provider_sync(self):
@ -45,6 +51,8 @@ class CalendarProviderConfig(models.TransientModel):
if self.external_calendar_provider == 'google':
self.env['ir.config_parameter'].set_param('google_calendar_client_id', self.cal_client_id)
self.env['ir.config_parameter'].set_param('google_calendar_client_secret', self.cal_client_secret)
self.env['ir.config_parameter'].set_param('google_calendar_sync_paused', self.cal_sync_paused)
elif self.external_calendar_provider == 'microsoft':
self.env['ir.config_parameter'].set_param('microsoft_calendar_client_id', self.microsoft_outlook_client_identifier)
self.env['ir.config_parameter'].set_param('microsoft_calendar_client_secret', self.microsoft_outlook_client_secret)
self.env['ir.config_parameter'].set_param('microsoft_calendar_sync_paused', self.microsoft_outlook_sync_paused)

View file

@ -4,34 +4,36 @@
<field name="name">calendar.provider.config.view.form</field>
<field name="model">calendar.provider.config</field>
<field name="arch" type="xml">
<form js_class="calendar_provider_config_form">
<form>
<field name="external_calendar_provider" widget="radio" options="{'horizontal': true}"/>
<div attrs="{'invisible': [('external_calendar_provider', '!=', 'google')]}">
<div invisible="external_calendar_provider != 'google'">
<img alt="Google Calendar icon" src="/calendar/static/src/img/google_calendar_40.png" style="height: 40px; margin-right: 5px"/>
<span class="me-1 o_form_label">Google Calendar</span>
<a href="https://www.odoo.com/documentation/16.0/applications/productivity/calendar/google.html" title="Read More" class="o_doc_link" target="_blank"></a>
<widget name="documentation_link" path="/applications/productivity/calendar/google.html" icon="fa-question-circle"/>
<div class="text-muted mt-2">
Synchronize your calendar with Google Calendar
</div>
<group>
<field name="cal_client_id" attrs="{'required': [('external_calendar_provider', '=', 'google')]}"/>
<field name="cal_client_secret" password="True" attrs="{'required': [('external_calendar_provider', '=', 'google')]}"/>
<field name="cal_client_id" string="Client ID" required="external_calendar_provider == 'google'"/>
<field name="cal_client_secret" string="Client Secret" password="True" required="external_calendar_provider == 'google'"/>
<field name="cal_sync_paused" required="external_calendar_provider == 'google'"/>
</group>
</div>
<div attrs="{'invisible': [('external_calendar_provider', '!=', 'microsoft')]}">
<div invisible="external_calendar_provider != 'microsoft'">
<img alt="Microsoft Outlook icon" src="/calendar/static/src/img/microsoft_calendar_40.png" style="height: 40px; margin-right: 5px"/>
<span class="me-1 o_form_label">Outlook Calendar</span>
<a href="https://www.odoo.com/documentation/16.0/applications/productivity/calendar/outlook.html" title="Read More" class="o_doc_link" target="_blank"></a>
<widget name="documentation_link" path="/applications/productivity/calendar/outlook.html" icon="fa-question-circle"/>
<div class="text-muted mt-2">
Synchronize your calendar with Outlook
</div>
<group>
<field name="microsoft_outlook_client_identifier" attrs="{'required': [('external_calendar_provider', '=', 'microsoft')]}"/>
<field name="microsoft_outlook_client_secret" password="True" attrs="{'required': [('external_calendar_provider', '=', 'microsoft')]}"/>
<field name="microsoft_outlook_client_identifier" string="Client ID" required="external_calendar_provider == 'microsoft'"/>
<field name="microsoft_outlook_client_secret" string="Client Secret" password="True" required="external_calendar_provider == 'microsoft'"/>
<field name="microsoft_outlook_sync_paused" required="external_calendar_provider == 'microsoft'"/>
</group>
</div>
<footer>
<a role="button" title="Connect" class="o_calendar_activate_external_cal btn btn-primary" t-on-click="onConnect">Connect</a>
<widget name="calendar_connect_provider"/>
<button string="Cancel" class="btn btn-secondary" special="cancel"/>
</footer>
</form>

View file

@ -0,0 +1,20 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
from odoo.exceptions import UserError
from odoo.tools.translate import _
class MailActivitySchedule(models.TransientModel):
_inherit = 'mail.activity.schedule'
def action_create_calendar_event(self):
self.ensure_one()
if self.is_batch_mode:
raise UserError(_("Scheduling an activity using the calendar is not possible on more than one record."))
if not self.res_model:
return self._action_schedule_activities_personal().action_create_calendar_event()
return self.with_context({
'default_res_model': self.res_model or False,
'default_res_id': self._evaluate_res_ids()[0],
})._action_schedule_activities().action_create_calendar_event()

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="mail_activity_schedule_view_form" model="ir.ui.view">
<field name="name">mail.activity.schedule.inherit.calendar</field>
<field name="model">mail.activity.schedule</field>
<field name="inherit_id" ref="mail.mail_activity_schedule_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='date_deadline']" position="attributes">
<attribute name="invisible">activity_category == 'meeting'</attribute>
</xpath>
<xpath expr="//field[@name='activity_user_id']" position="attributes">
<attribute name="invisible">activity_category == 'meeting'</attribute>
</xpath>
<xpath expr="//button[@name='action_schedule_activities']" position="attributes">
<attribute name="invisible" add="activity_category == 'meeting' or id" separator="or"/>
</xpath>
<xpath expr="//field[@name='note']" position="attributes">
<attribute name="invisible">activity_category == 'meeting'</attribute>
</xpath>
<xpath expr="//group[@name='summary_group']|//field[@name='summary']" position="attributes">
<attribute name="invisible">activity_category == 'meeting'</attribute>
</xpath>
<xpath expr="//field[@name='note']" position="before">
<div invisible="activity_category != 'meeting'" class="text-muted text-center w-100">
<div>
<i class="fa fa-9x fa-calendar p-3" title="Calendar" aria-hidden="true"/>
</div>
<p>Schedule a meeting in your calendar</p>
</div>
</xpath>
<xpath expr="//button[@name='action_schedule_activities_done']" position="attributes">
<attribute name="invisible" add="activity_category == 'meeting'" separator="or"/>
</xpath>
<xpath expr="//button[@name='action_schedule_activities']" position="before">
<button string="Schedule"
invisible="activity_category != 'meeting'"
name="action_create_calendar_event"
type="object"
class="btn-primary"/>
</xpath>
</field>
</record>
</data>
</odoo>