diff --git a/mailcatcher_menu/__init__.py b/mailcatcher_menu/__init__.py new file mode 100644 index 0000000..15668ac --- /dev/null +++ b/mailcatcher_menu/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +from . import controllers +from . import models +from . import wizard +from .hooks import post_init_hook \ No newline at end of file diff --git a/mailcatcher_menu/__manifest__.py b/mailcatcher_menu/__manifest__.py new file mode 100644 index 0000000..78ef345 --- /dev/null +++ b/mailcatcher_menu/__manifest__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +{ + 'name': "mailcatcher_menu", + + 'summary': """ + Add menu-item to local mailcatcher""", + + 'description': """ + Add menu-item to local mailcatcher + """, + + 'author': "Open2bizz", + 'website': "http://www.open2bizz.tech", + 'category': 'Tools', + 'license': "AGPL-3", + 'version': '15.0.1.0.0', + 'module_type': 'official', + 'depends': ['mail','base',], + 'data': [ + 'wizard/install_warning_wizard_view.xml', + 'views/views.xml', + 'data/data.xml', + 'security/ir.model.access.csv', + ], + 'post_init_hook': 'post_init_hook', +} diff --git a/mailcatcher_menu/controllers/__init__.py b/mailcatcher_menu/controllers/__init__.py new file mode 100644 index 0000000..457bae2 --- /dev/null +++ b/mailcatcher_menu/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import controllers \ No newline at end of file diff --git a/mailcatcher_menu/controllers/controllers.py b/mailcatcher_menu/controllers/controllers.py new file mode 100644 index 0000000..4d06ad4 --- /dev/null +++ b/mailcatcher_menu/controllers/controllers.py @@ -0,0 +1,55 @@ +# Copyright 2024 Open2Bizz +# License LGPL-3 + +import logging +import requests + +from odoo import http +from odoo.http import request, Response + +_logger = logging.getLogger(__name__) + +MAILHOG_BASE = "http://localhost:8025" + + +class MailcatcherProxy(http.Controller): + + @http.route(['/mailcatcher', '/mailcatcher/'], + type='http', auth='user', website=False, csrf=False) + def mailcatcher_proxy(self, subpath='', **kw): + """Proxy requests to the local MailHog instance.""" + target_url = f"{MAILHOG_BASE}/{subpath}" + if request.httprequest.query_string: + target_url += '?' + request.httprequest.query_string.decode('utf-8') + + try: + headers = { + k: v for k, v in request.httprequest.headers + if k.lower() not in ('host', 'cookie', 'authorization') + } + resp = requests.request( + method=request.httprequest.method, + url=target_url, + headers=headers, + data=request.httprequest.get_data(), + timeout=30, + allow_redirects=False, + ) + except requests.exceptions.ConnectionError: + _logger.warning("Could not connect to MailHog at %s", MAILHOG_BASE) + return Response("MailHog is not available", status=502) + + excluded_headers = { + 'content-encoding', 'content-length', 'transfer-encoding', + 'connection', + } + response_headers = [ + (k, v) for k, v in resp.headers.items() + if k.lower() not in excluded_headers + ] + + return Response( + resp.content, + status=resp.status_code, + headers=response_headers, + ) diff --git a/mailcatcher_menu/data/data.xml b/mailcatcher_menu/data/data.xml new file mode 100644 index 0000000..cb672a8 --- /dev/null +++ b/mailcatcher_menu/data/data.xml @@ -0,0 +1,4 @@ + + + + diff --git a/mailcatcher_menu/demo/demo.xml b/mailcatcher_menu/demo/demo.xml new file mode 100644 index 0000000..fbbff02 --- /dev/null +++ b/mailcatcher_menu/demo/demo.xml @@ -0,0 +1,30 @@ + + + + + \ No newline at end of file diff --git a/mailcatcher_menu/hooks.py b/mailcatcher_menu/hooks.py new file mode 100644 index 0000000..a60960d --- /dev/null +++ b/mailcatcher_menu/hooks.py @@ -0,0 +1,40 @@ +# Copyright 2024 Open2Bizz +# License LGPL-3 + +import logging +import os +import subprocess + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, registry): + """Install MailHog as a systemd service when the module is installed.""" + script_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'install_mailhog_as_service.sh', + ) + + if not os.path.isfile(script_path): + _logger.error("MailHog install script not found at %s", script_path) + return + + _logger.info("Running MailHog install script: %s", script_path) + try: + result = subprocess.run( + ['bash', script_path], + capture_output=True, + text=True, + timeout=300, + ) + if result.returncode == 0: + _logger.info("MailHog installed successfully.\n%s", result.stdout) + else: + _logger.error( + "MailHog install script failed (exit %s):\n%s\n%s", + result.returncode, result.stdout, result.stderr, + ) + except subprocess.TimeoutExpired: + _logger.error("MailHog install script timed out after 300 seconds") + except Exception as e: + _logger.error("Failed to run MailHog install script: %s", e) \ No newline at end of file diff --git a/mailcatcher_menu/install_mailhog_as_service.sh b/mailcatcher_menu/install_mailhog_as_service.sh new file mode 100644 index 0000000..c950793 --- /dev/null +++ b/mailcatcher_menu/install_mailhog_as_service.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +set -e # stop bij fouten + +echo "" +echo "===================================" +echo "Update apt..." +echo "===================================" +apt update + +echo "" +echo "===================================" +echo "Install dependencies..." +echo "===================================" +apt-get -y install wget tar + +echo "" +echo "===================================" +echo "Install Go 1.24.3..." +echo "===================================" +GO_VERSION=1.24.3 +wget https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz + +sudo rm -rf /usr/local/go +sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz + +# Go environment correct instellen +export GOROOT=/usr/local/go +export GOPATH=$HOME/go +export PATH=$GOROOT/bin:$GOPATH/bin:$PATH + +echo "Go version:" +go version + +echo "" +echo "===================================" +echo "Installing MailHog..." +echo "===================================" +/usr/local/go/bin/go install github.com/mailhog/MailHog@latest + +if [ -f "$HOME/go/bin/MailHog" ]; then + echo "MailHog installed successfully" +else + echo "ERROR: MailHog binary not found!" + exit 1 +fi + +echo "" +echo "===================================" +echo "Copy MailHog to /usr/local/bin..." +echo "===================================" +sudo cp "$HOME/go/bin/MailHog" /usr/local/bin/MailHog + +echo "" +echo "===================================" +echo "Create MailHog service..." +echo "===================================" +sudo tee /etc/systemd/system/mailhog.service > /dev/null < +# License LGPL-3 + +# MailHog is now accessed via the internal proxy controller at /mailcatcher/ +# so no dynamic URL computation is needed anymore. + + diff --git a/mailcatcher_menu/security/ir.model.access.csv b/mailcatcher_menu/security/ir.model.access.csv new file mode 100644 index 0000000..1849ee3 --- /dev/null +++ b/mailcatcher_menu/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_mailcatcher_install_warning_wizard","mailcatcher.install.warning.wizard","model_mailcatcher_install_warning_wizard","base.group_user",1,1,1,1 diff --git a/mailcatcher_menu/static/description/email-icon.png b/mailcatcher_menu/static/description/email-icon.png new file mode 100644 index 0000000..a582cfb Binary files /dev/null and b/mailcatcher_menu/static/description/email-icon.png differ diff --git a/mailcatcher_menu/static/description/icon.png b/mailcatcher_menu/static/description/icon.png new file mode 100644 index 0000000..34cfef5 Binary files /dev/null and b/mailcatcher_menu/static/description/icon.png differ diff --git a/mailcatcher_menu/static/description/index.html b/mailcatcher_menu/static/description/index.html new file mode 100644 index 0000000..a898b80 --- /dev/null +++ b/mailcatcher_menu/static/description/index.html @@ -0,0 +1,18 @@ + + +
+

mailcatcher_menu

+

+ This module is ONLY for test environments In DC Open2Bizz +

+ +

+ Module is developed by Open2Bizz. Bug reports: support@open2bizz.eu +

+

+ +

+ +
+ + diff --git a/mailcatcher_menu/views/views.xml b/mailcatcher_menu/views/views.xml new file mode 100644 index 0000000..108f59d --- /dev/null +++ b/mailcatcher_menu/views/views.xml @@ -0,0 +1,14 @@ + + + + + Open Mailcatcher + /mailcatcher/ + new + + + + + + + diff --git a/mailcatcher_menu/wizard/__init__.py b/mailcatcher_menu/wizard/__init__.py new file mode 100644 index 0000000..54c2cdf --- /dev/null +++ b/mailcatcher_menu/wizard/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import install_warning_wizard diff --git a/mailcatcher_menu/wizard/install_warning_wizard.py b/mailcatcher_menu/wizard/install_warning_wizard.py new file mode 100644 index 0000000..505c6cd --- /dev/null +++ b/mailcatcher_menu/wizard/install_warning_wizard.py @@ -0,0 +1,43 @@ +# Copyright 2024 Open2Bizz +# License LGPL-3 + +from odoo import models, fields, api, _ + + +class MailcatcherInstallWarningWizard(models.TransientModel): + _name = 'mailcatcher.install.warning.wizard' + _description = 'MailCatcher Install Warning' + + module_id = fields.Many2one('ir.module.module', string='Module', readonly=True) + + def action_confirm_install(self): + """User confirmed — proceed with the actual module installation.""" + self.ensure_one() + return self.module_id.with_context( + skip_mailcatcher_warning=True + ).button_immediate_install() + + +class IrModuleModule(models.Model): + _inherit = 'ir.module.module' + + def button_immediate_install(self): + """Override to show a warning before installing mailcatcher_menu.""" + if self.env.context.get('skip_mailcatcher_warning'): + return super().button_immediate_install() + + mailcatcher_modules = self.filtered(lambda m: m.name == 'mailcatcher_menu') + if mailcatcher_modules: + wizard = self.env['mailcatcher.install.warning.wizard'].create({ + 'module_id': mailcatcher_modules[0].id, + }) + return { + 'name': _('Install MailCatcher Module'), + 'type': 'ir.actions.act_window', + 'res_model': 'mailcatcher.install.warning.wizard', + 'res_id': wizard.id, + 'view_mode': 'form', + 'target': 'new', + } + + return super().button_immediate_install() diff --git a/mailcatcher_menu/wizard/install_warning_wizard_view.xml b/mailcatcher_menu/wizard/install_warning_wizard_view.xml new file mode 100644 index 0000000..a5f5922 --- /dev/null +++ b/mailcatcher_menu/wizard/install_warning_wizard_view.xml @@ -0,0 +1,28 @@ + + + mailcatcher.install.warning.wizard.form + mailcatcher.install.warning.wizard + +
+ +
+
+
+
+
+
diff --git a/testserver_o2b/data/ir_cron.xml b/testserver_o2b/data/ir_cron.xml new file mode 100644 index 0000000..b766b76 --- /dev/null +++ b/testserver_o2b/data/ir_cron.xml @@ -0,0 +1,29 @@ + + + + + Testserver: Set Date (ONLY FOR TEST ENV.) + + code + model.set_db_param() + 1 + days + + True + + + + Testserver: Cleanup Unneeded Filestore + + code + model.cleanup_unneeded_filestore_attachments(5000) + 15 + minutes + + True + + + True + + + diff --git a/testserver_o2b/hooks.py b/testserver_o2b/hooks.py new file mode 100644 index 0000000..7d6ec9d --- /dev/null +++ b/testserver_o2b/hooks.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from odoo import api, SUPERUSER_ID + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + + env['ir.logging'].sudo().cleanup_unneeded_filestore_attachments( + limit=50 + ) \ No newline at end of file diff --git a/testserver_website_o2b/__init__.py b/testserver_website_o2b/__init__.py new file mode 100644 index 0000000..4e2b3cc --- /dev/null +++ b/testserver_website_o2b/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from .hooks import post_init_hook \ No newline at end of file diff --git a/testserver_website_o2b/__manifest__.py b/testserver_website_o2b/__manifest__.py new file mode 100644 index 0000000..0bf9646 --- /dev/null +++ b/testserver_website_o2b/__manifest__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +{ + 'name': "Set as testserver Open2Bizz, when website is enabled", + + 'summary': """ + Only to be used when installed as TEST env. Open2Bizz""", + + + 'author': "Open2bizz", + 'website': "http://www.open2bizz.tech", + 'category': 'Tools', + 'license': "AGPL-3", + 'version': '15.0.1.0.0', + 'depends': ['testserver_o2b','website'], + 'data': [ + 'views/templates.xml', + ], + 'post_init_hook': 'post_init_hook', +} diff --git a/testserver_website_o2b/hooks.py b/testserver_website_o2b/hooks.py new file mode 100644 index 0000000..75fc8ec --- /dev/null +++ b/testserver_website_o2b/hooks.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- + +import logging + +from odoo import api, SUPERUSER_ID + +_logger = logging.getLogger(__name__) + + +def post_init_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + + Website = env['website'].sudo() + WebsitePage = env['website.page'].sudo() + + # --------------------------------------------------------- + # 1. Get the generic TEST page template + # --------------------------------------------------------- + test_view = env.ref( + 'testserver_website_o2b.testserver_page_website', + raise_if_not_found=False, + ) + + if not test_view: + _logger.error( + "Testserver website template " + "'testserver_website_o2b.testserver_page_website' not found." + ) + return + + # --------------------------------------------------------- + # 2. Unpublish all existing website pages + # --------------------------------------------------------- + pages = WebsitePage.search([]) + + if pages: + _logger.info( + "Unpublishing %s existing website pages.", + len(pages), + ) + + pages.write({ + 'website_published': False, + }) + + # --------------------------------------------------------- + # 3. Create a TEST homepage for every website + # --------------------------------------------------------- + websites = Website.search([]) + + for website in websites: + _logger.info( + "Creating test homepage for website %s (%s).", + website.name, + website.id, + ) + + # Each website gets its own ir.ui.view. + # + # In Odoo 15 website.page.website_id is related to + # view_id.website_id. Therefore we should not use the same + # ir.ui.view for multiple websites. + website_test_view = test_view.copy({ + 'name': 'Test environment - %s' % website.name, + 'key': '%s.website_%s' % (test_view.key, website.id), + 'website_id': website.id, + }) + + test_page = WebsitePage.create({ + 'url': '/testmodus', + 'view_id': website_test_view.id, + 'website_published': True, + 'website_indexed': False, + }) + + # Odoo 15 uses homepage_id, NOT homepage_url. + website.write({ + 'domain': False, + 'homepage_id': test_page.id, + }) + + _logger.info( + "Test homepage %s assigned to website %s.", + test_page.id, + website.id, + ) + + # --------------------------------------------------------- + # 4. Disable all payment acquirers + # --------------------------------------------------------- + # + # Odoo 15 uses payment.acquirer. + # payment.provider belongs to later Odoo versions. + # + if env.registry.get('payment.acquirer'): + payment_acquirers = env['payment.acquirer'].sudo().search([ + ('state', '!=', 'disabled'), + ]) + + if payment_acquirers: + _logger.info( + "Disabling %s payment acquirers.", + len(payment_acquirers), + ) + + payment_acquirers.write({ + 'state': 'disabled', + }) + + # --------------------------------------------------------- + # 5. Unpublish all webshop products + # --------------------------------------------------------- + # + # website_published only exists on product.template when the + # relevant website/eCommerce functionality is installed. + # + if env.registry.get('product.template'): + ProductTemplate = env['product.template'].sudo() + + if 'website_published' in ProductTemplate._fields: + published_products = ProductTemplate.search([ + ('website_published', '=', True), + ]) + + if published_products: + _logger.info( + "Unpublishing %s webshop products.", + len(published_products), + ) + + published_products.write({ + 'website_published': False, + }) + + _logger.info( + "Testserver website neutralization completed successfully." + ) \ No newline at end of file diff --git a/testserver_website_o2b/models/__init__.py b/testserver_website_o2b/models/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/testserver_website_o2b/models/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/testserver_website_o2b/static/description/icon.png b/testserver_website_o2b/static/description/icon.png new file mode 100644 index 0000000..34cfef5 Binary files /dev/null and b/testserver_website_o2b/static/description/icon.png differ diff --git a/testserver_website_o2b/static/description/index.html b/testserver_website_o2b/static/description/index.html new file mode 100644 index 0000000..c8cdcf3 --- /dev/null +++ b/testserver_website_o2b/static/description/index.html @@ -0,0 +1,18 @@ + + +
+

Testservers when website

+

+ This module is ONLY for test environments In DC Open2Bizz +

+ +

+ Module is developed by Open2Bizz. Bug reports: support@open2bizz.eu +

+

+ +

+ +
+ + diff --git a/testserver_website_o2b/views/templates.xml b/testserver_website_o2b/views/templates.xml new file mode 100644 index 0000000..8238c60 --- /dev/null +++ b/testserver_website_o2b/views/templates.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + \ No newline at end of file