diff --git a/bemade_mailcow_blacklist/__manifest__.py b/bemade_mailcow_blacklist/__manifest__.py index e77fa93..58b576d 100644 --- a/bemade_mailcow_blacklist/__manifest__.py +++ b/bemade_mailcow_blacklist/__manifest__.py @@ -21,7 +21,7 @@ Main Features: 'license': 'OPL-1', 'author': 'BeMade', 'website': 'https://www.bemade.org', - 'depends': ['mail', 'bemade_user_password_bundle'], + 'depends': ['hr', 'mail', 'bemade_user_password_bundle'], 'data': [ 'security/ir.model.access.csv', 'views/res_config_settings_views.xml', @@ -31,6 +31,11 @@ Main Features: 'views/res_users_views.xml', ], + "assets": { + "web.assets_backend": [ + "bemade_mailcow_blacklist/static/src/js/mailcow_mailbox.js" + ] + }, 'demo': [], 'installable': True, 'application': False, diff --git a/bemade_mailcow_blacklist/models/mail_alias.py b/bemade_mailcow_blacklist/models/mail_alias.py index bdb7105..0bec8ce 100644 --- a/bemade_mailcow_blacklist/models/mail_alias.py +++ b/bemade_mailcow_blacklist/models/mail_alias.py @@ -6,15 +6,25 @@ class MailAlias(models.Model): mailcow_id = fields.One2many('mail.mailcow.alias', 'alias_id') + @api.model def create(self, vals): alias = super(MailAlias, self).create(vals) - mailcow_alias = self.env['mail.mailcow.alias'].search([('address', '=', alias.alias_name + '@' + alias.alias_domain)]) + + alias_domain = self.env["ir.config_parameter"].sudo().get_param("mail.catchall.domain"), + catchall_alias = self.env["ir.config_parameter"].sudo().get_param("mail.catchall.alias"), + + alias_domain = alias_domain[0] + catchall_alias = catchall_alias[0] + + if not alias_domain: + raise ValidationError(_("No catchall domain is set in the system parameters. Please set one and try again.")) + mailcow_alias = self.env['mail.mailcow.alias'].search([('address', '=', alias.alias_name + '@' + alias_domain)]) if mailcow_alias: mailcow_alias.write({'active': True}) else: self.env['mail.mailcow.alias'].create({ - 'address': alias.alias_name + '@' + alias.alias_domain, - 'goto': alias.alias_defaults.get('email_from', False), + 'address': alias.alias_name + '@' + alias_domain, + 'goto': catchall_alias + '@' + alias_domain, 'alias_id': alias.id, }) return alias diff --git a/bemade_mailcow_blacklist/models/mailcow.py b/bemade_mailcow_blacklist/models/mailcow.py index ca0624d..009d1eb 100644 --- a/bemade_mailcow_blacklist/models/mailcow.py +++ b/bemade_mailcow_blacklist/models/mailcow.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- -from odoo import models, fields, api, exceptions +from odoo import models, fields, api, _ import requests import logging +from odoo.exceptions import ValidationError + _logger = logging.getLogger(__name__) @@ -10,15 +12,29 @@ class MailMailcow(models.AbstractModel): _name = 'mail.mailcow' _description = 'Mailcow API' + @property def get_credentials(self): params = self.env['ir.config_parameter'].sudo() - return { - 'base_url': params.get_param('mailcow.base_url'), - 'api_key': params.get_param('mailcow.api_key'), - } + + base_url = params.get_param('mailcow.base_url'), + base_url = base_url[0] + api_key = params.get_param('mailcow.api_key'), + api_key = api_key[0] + + if not base_url or not api_key: + _logger.error('No API key or base URL is set in the system parameters') + raise ValidationError(_("No API key or base URL is set in the system parameters. Please set one and try again.")) + # return False + else: + return { + 'base_url': base_url, + 'api_key': api_key + } def api_request(self, endpoint, method='GET', data=None): - creds = self.get_credentials() + creds = self.get_credentials + if not creds: + return False url = creds['base_url'] + endpoint headers = { 'accept': 'application/json', diff --git a/bemade_mailcow_blacklist/models/mailcow_alias.py b/bemade_mailcow_blacklist/models/mailcow_alias.py index 8164b0f..fa426f1 100644 --- a/bemade_mailcow_blacklist/models/mailcow_alias.py +++ b/bemade_mailcow_blacklist/models/mailcow_alias.py @@ -33,9 +33,10 @@ class MailcowAlias(models.Model): "private_comment": f"Created by {self.env.user.name} on {fields.Datetime.now()}", "public_comment": "Alias created in Odoo" } - result = self.env['mail.mailcow'].api_request('api/v1/add/alias', 'POST', data) + result = self.env['mail.mailcow'].api_request('/api/v1/add/alias', 'POST', data) if not result: - raise ValidationError(_("Failed to create alias on Mailcow server.")) + #pass + raise ValidationError("Failed to create alias on Mailcow server.") return alias @@ -59,7 +60,7 @@ class MailcowAlias(models.Model): }, "items": [record.mc_id] } - result = self.env['mail.mailcow'].api_request('api/v1/edit/alias', 'POST', data) + result = self.env['mail.mailcow'].api_request('/api/v1/edit/alias', 'POST', data) if not result: raise ValidationError(_("Failed to update alias on Mailcow server.")) @@ -72,7 +73,7 @@ class MailcowAlias(models.Model): For each alias fetched from Mailcow server, it tries to find a matching record in Odoo. If it doesn't exist, it creates a new record. """ - endpoint = 'api/v1/get/alias/all' + endpoint = '/api/v1/get/alias/all' mailcow_aliases = self.api_request(endpoint) if not mailcow_aliases: @@ -80,6 +81,7 @@ class MailcowAlias(models.Model): for mc_alias in mailcow_aliases: domain = mc_alias['domain'] + alias_domain = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.domain') if domain == alias_domain: alias = self.search([('address', '=', mc_alias['address'])], limit=1) diff --git a/bemade_mailcow_blacklist/models/mailcow_blacklist.py b/bemade_mailcow_blacklist/models/mailcow_blacklist.py index e3f9eeb..15c4154 100644 --- a/bemade_mailcow_blacklist/models/mailcow_blacklist.py +++ b/bemade_mailcow_blacklist/models/mailcow_blacklist.py @@ -10,8 +10,8 @@ class MailcowBlacklist(models.Model): _description = 'Mailcow Blacklist' _inherit = ['mail.mailcow', 'mail.thread', 'mail.activity.mixin'] - email = fields.Char(string='Email', required=True, track_visibility='onchange') - prefid = fields.Integer(string='Mailcow ID', required=True, track_visibility='onchange') + email = fields.Char(string='Email', required=True, tracking=True) + prefid = fields.Integer(string='Mailcow ID', required=True, tracking=True) @api.model def create(self, vals): @@ -19,10 +19,10 @@ class MailcowBlacklist(models.Model): Overridden create method to add the new blacklist entry to the Mailcow server. """ res = super().create(vals) - domain = self.env['res.config.settings'].get_values()['mail.catchall.domain'] + domain = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.domain') - endpoint_add = 'api/v1/add/domain-policy' - endpoint_get_bl = f"api/v1/get/policy_bl_domain/{domain}" + endpoint_add = '/api/v1/add/domain-policy' + endpoint_get_bl = f"/api/v1/get/policy_bl_domain/{domain}" data = { 'domain': domain, 'object_from': res.email, @@ -42,8 +42,8 @@ class MailcowBlacklist(models.Model): old_email = self.email res = super().write(vals) if 'email' in vals: - delete_endpoint = 'api/v1/delete/domain-policy' - add_endpoint = 'api/v1/add/domain-policy' + delete_endpoint = '/api/v1/delete/domain-policy' + add_endpoint = '/api/v1/add/domain-policy' delete_data = { 'items': [old_email] } @@ -60,7 +60,7 @@ class MailcowBlacklist(models.Model): Overridden unlink method to remove the blacklist entry from the Mailcow server. """ for record in self: - endpoint = 'api/v1/delete/blacklist' + endpoint = '/api/v1/delete/blacklist' data = { 'items': [record.email] } diff --git a/bemade_mailcow_blacklist/models/mailcow_mailbox.py b/bemade_mailcow_blacklist/models/mailcow_mailbox.py index 3107ecd..778d3fd 100644 --- a/bemade_mailcow_blacklist/models/mailcow_mailbox.py +++ b/bemade_mailcow_blacklist/models/mailcow_mailbox.py @@ -11,13 +11,16 @@ class MailcowMailbox(models.Model): _inherit = ['mail.mailcow', 'mail.thread', 'mail.activity.mixin'] _description = 'Mailcow Mailbox' - name = fields.Char(track_visibility='onchange') - address = fields.Char(compute='_compute_address', store=True, readonly=True, track_visibility='onchange') - local_part = fields.Char(required=True, track_visibility='onchange') - domain = fields.Char(required=True, track_visibility='onchange') - active = fields.Boolean(default=True, track_visibility='onchange') - user_id = fields.Many2one('res.users', ondelete='cascade', track_visibility='onchange') - password = fields.Char(readonly=True, track_visibility='onchange') + def _default_domain(self): + return self.env["ir.config_parameter"].sudo().get_param("mail.catchall.domain") + + name = fields.Char(tracking=True) + address = fields.Char(compute='_compute_address', store=True, readonly=True, tracking=True) + local_part = fields.Char(required=True, tracking=True) + domain = fields.Char(required=True, tracking=True, default=_default_domain) + active = fields.Boolean(default=True, tracking=True) + user_id = fields.Many2one('res.users', ondelete='cascade', tracking=True) + password = fields.Char(readonly=True, tracking=True) @api.depends('local_part', 'domain') def _compute_address(self): @@ -28,7 +31,7 @@ class MailcowMailbox(models.Model): """ Synchronize Mailcow mailboxes with Odoo """ - endpoint = 'api/v1/get/mailbox/all' + endpoint = '/api/v1/get/mailbox/all' data = self.api_request(endpoint) if data: for item in data: @@ -51,12 +54,12 @@ class MailcowMailbox(models.Model): vals['password'] = password # Check if email exists on Mailcow - endpoint = f"api/v1/get/mailbox/{vals['address']}" + endpoint = f"/api/v1/get/mailbox/{vals['local_part']}@{vals['domain']}" response = self.api_request(endpoint) if not response: # If email does not exist on Mailcow, create it - endpoint = 'api/v1/add/mailbox' + endpoint = '/api/v1/add/mailbox' data = { 'local_part': vals['local_part'], 'domain': vals['domain'], @@ -70,7 +73,7 @@ class MailcowMailbox(models.Model): 'tls_enforce_out': "0", } self.api_request(endpoint, method='POST', data=data) - _logger.info(f'Mailbox {vals["address"]} has been created on Mailcow server') + _logger.info(f"Mailbox {vals['local_part']}@{vals['domain']} has been created on Mailcow server") return super().create(vals) @@ -79,7 +82,7 @@ class MailcowMailbox(models.Model): Override the write function to update a Mailcow mailbox whenever a user is updated in Odoo. """ if 'active' in vals or 'local_part' in vals or 'domain' in vals: - endpoint = f'api/v1/edit/mailbox/{self.address}' + endpoint = f'/api/v1/edit/mailbox/{self.address}' data = { 'items': [self.address], 'attr': { @@ -97,7 +100,7 @@ class MailcowMailbox(models.Model): """ Override the unlink function to delete a Mailcow mailbox whenever a user is deleted in Odoo. """ - endpoint = f'api/v1/delete/mailbox/{self.address}' + endpoint = f'/api/v1/delete/mailbox/{self.address}' self.api_request(endpoint, method='POST') _logger.info(f'Mailbox {self.address} has been deleted on Mailcow server') @@ -119,7 +122,7 @@ class MailcowMailbox(models.Model): 'name': user.name, 'password': user.password, } - endpoint = 'api/v1/add/mailbox' + endpoint = '/api/v1/add/mailbox' self.api_request(endpoint, method='POST', data=data) _logger.info(f'Mailbox for user {user.login} has been created on Mailcow server') diff --git a/bemade_mailcow_blacklist/models/res_users.py b/bemade_mailcow_blacklist/models/res_users.py index a3130ea..884fdbc 100644 --- a/bemade_mailcow_blacklist/models/res_users.py +++ b/bemade_mailcow_blacklist/models/res_users.py @@ -5,10 +5,11 @@ class ResUsers(models.Model): mailcow_mailbox = fields.Boolean(string='Mailcow Mailbox', default=False) + @api.model def create(self, vals): res = super(ResUsers, self).create(vals) - if vals.get('mailcow_mailbox'): + if vals.get('mailcow_mailbox', false): self.env['mail.mailcow.mailbox'].create_mailbox_for_user(res) return res diff --git a/bemade_mailcow_blacklist/static/src/js/mailcow_mailbox.js b/bemade_mailcow_blacklist/static/src/js/mailcow_mailbox.js new file mode 100644 index 0000000..94192dc --- /dev/null +++ b/bemade_mailcow_blacklist/static/src/js/mailcow_mailbox.js @@ -0,0 +1,30 @@ +odoo.define('bemade_mailcow_blacklist.mailcow_mailbox', function (require) { +"use strict"; + + var core = require('web.core'); + var ListController = require('web.ListController'); + var ListView = require('web.ListView'); + var viewRegistry = require('web.view_registry'); + + var MailboxesListController = ListController.extend({ + buttons_template: 'mail.mailcow_mailbox_list_view_buttons', + events: _.extend({}, ListController.prototype.events, { + 'click .o_button_sync_mailboxes': '_onSyncMailboxes', + }), + _onSyncMailboxes: function () { + this._rpc({ + model: 'mail.mailcow.mailbox', + method: 'sync_mailboxes', + args: [], + }); + }, + }); + + var MailboxesListView = ListView.extend({ + config: _.extend({}, ListView.prototype.config, { + Controller: MailboxesListController, + }), + }); + + viewRegistry.add('mail.mailcow.mailbox', MailboxesListView); +}); diff --git a/bemade_mailcow_blacklist/tests/__init__.py b/bemade_mailcow_blacklist/tests/__init__.py new file mode 100644 index 0000000..cc6beea --- /dev/null +++ b/bemade_mailcow_blacklist/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mailcow diff --git a/bemade_mailcow_blacklist/tests/test_mailcow.py b/bemade_mailcow_blacklist/tests/test_mailcow.py new file mode 100644 index 0000000..53ed6ca --- /dev/null +++ b/bemade_mailcow_blacklist/tests/test_mailcow.py @@ -0,0 +1,12 @@ +from odoo.tests.common import TransactionCase, tagged + + +@tagged('-at_install', 'post_install') +class TestMailcow(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + def test_new_mail_alias(self): + model_id = self.env['ir.model']._get('res.partner').id + self.alias = self.env['mail.alias'].create({'alias_name': 'test', 'alias_model_id': model_id}) diff --git a/bemade_mailcow_blacklist/views/mailcow_mailbox_views.xml b/bemade_mailcow_blacklist/views/mailcow_mailbox_views.xml index baa7a91..e64afc6 100644 --- a/bemade_mailcow_blacklist/views/mailcow_mailbox_views.xml +++ b/bemade_mailcow_blacklist/views/mailcow_mailbox_views.xml @@ -1,10 +1,13 @@ - + mailcow.mailbox.view.tree mail.mailcow.mailbox +
+
@@ -39,7 +42,7 @@
- + Mailboxes ir.actions.act_window mail.mailcow.mailbox @@ -62,6 +65,21 @@ + + + Sync Mailboxes + + code + model.sync_mailboxes() + + + + + tree + + + +