first push message

This commit is contained in:
2026-07-01 14:41:49 +07:00
parent 6667dec2bf
commit 58b5f46cc4
2951 changed files with 316619 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
from . import controllers
from . import models
+40
View File
@@ -0,0 +1,40 @@
{
'name': "web_replace_url",
'summary': "Short (1 phrase/line) summary of the module's purpose",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "https://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/15.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base','web'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'data/data.xml',
'views/ir_config_parameter_views.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
"assets": {
"web.assets_backend": [
"web_replace_url/static/src/**/*",
],
},
"installable": True,
'uninstall_hook': '_uninstall_cleanup',
}
+1
View File
@@ -0,0 +1 @@
from . import controllers
@@ -0,0 +1,21 @@
# from odoo import http
# class WebReplaceUrl(http.Controller):
# @http.route('/web_replace_url/web_replace_url', auth='public')
# def index(self, **kw):
# return "Hello, world"
# @http.route('/web_replace_url/web_replace_url/objects', auth='public')
# def list(self, **kw):
# return http.request.render('web_replace_url.listing', {
# 'root': '/web_replace_url/web_replace_url',
# 'objects': http.request.env['web_replace_url.web_replace_url'].search([]),
# })
# @http.route('/web_replace_url/web_replace_url/objects/<model("web_replace_url.web_replace_url"):obj>', auth='public')
# def object(self, obj, **kw):
# return http.request.render('web_replace_url.object', {
# 'object': obj
# })
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="rec_config_parameter_web_basesorturl" model="ir.config_parameter">
<field name="key">web.base.sorturl</field>
<field name="value">web</field>
</record>
</data>
</odoo>
+30
View File
@@ -0,0 +1,30 @@
<odoo>
<data>
<!--
<record id="object0" model="web_replace_url.web_replace_url">
<field name="name">Object 0</field>
<field name="value">0</field>
</record>
<record id="object1" model="web_replace_url.web_replace_url">
<field name="name">Object 1</field>
<field name="value">10</field>
</record>
<record id="object2" model="web_replace_url.web_replace_url">
<field name="name">Object 2</field>
<field name="value">20</field>
</record>
<record id="object3" model="web_replace_url.web_replace_url">
<field name="name">Object 3</field>
<field name="value">30</field>
</record>
<record id="object4" model="web_replace_url.web_replace_url">
<field name="name">Object 4</field>
<field name="value">40</field>
</record>
-->
</data>
</odoo>
+2
View File
@@ -0,0 +1,2 @@
from . import home
from . import middleware
+106
View File
@@ -0,0 +1,106 @@
import json
import logging
import psycopg2
import threading
import re
from odoo import tools, api, fields, models, _
from odoo.addons.base.models.ir_http import _logger, FasterRule, IrHttp
from odoo.addons.base.models.assetsbundle import JavascriptAsset
from odoo.http import request, ROUTING_KEYS
from odoo.tools.misc import submap
from odoo.modules.registry import Registry
from odoo.tools.js_transpiler import transpile_javascript
from odoo.tools import config as odoo_config
import odoo
import odoo.exceptions
import odoo.modules.registry
import werkzeug.utils
import werkzeug.routing
import werkzeug.exceptions
import werkzeug
# Global variable for base URL replacement
base_sorturl = ['']
class IrConfigParameter(models.Model):
_inherit = "ir.config_parameter"
def write(self, vals):
result = super(IrConfigParameter, self).write(vals)
if result and any(rec.key == 'web.base.sorturl' for rec in self):
# Clear routing cache properly for Odoo 19
self.env['ir.http'].env.registry.clear_cache("routing")
# Regenerate assets bundles
self.env['ir.attachment'].regenerate_assets_bundles()
# Return proper action for client reload
return {'type': 'ir.actions.client', 'tag': 'reload'}
return result
# ✅ Override JavascriptAsset.content property - Odoo 19 compatible
@property
def content(self):
content = super(JavascriptAsset, self).content
replacement = base_sorturl[0] or ''
if self.name == "/web/static/src/core/browser/router.js":
content = re.sub(r'(?<![@\w])odoo(?!\w)', replacement, content)
if self.name == "/web/static/src/webclient/navbar/navbar.js":
content = re.sub(r'(?<![@\w])odoo(?!\w)', replacement, content)
if self.is_transpiled:
if not getattr(self, '_converted_content', None):
self._converted_content = transpile_javascript(self.url, content)
return self._converted_content
return content
JavascriptAsset.content = content
# ✅ Override routing_map - Keep BOTH original and custom routes
@tools.ormcache('key', cache='routing')
def routing_map(self, key=None):
config_parameter = self.env['ir.config_parameter'].sudo()
base_sorturl[0] = config_parameter.get_param("web.base.sorturl", "")
_logger.info("Generating routing map for key %s, base_sorturl=%s",
str(key), base_sorturl[0])
registry = Registry(threading.current_thread().dbname)
installed = registry._init_modules.union(
set(odoo_config.get('server_wide_modules', []))
)
mods = sorted(installed)
routing_map = werkzeug.routing.Map(
strict_slashes=False, converters=self._get_converters())
for url, endpoint in self._generate_routing_rules(mods, converters=self._get_converters()):
# ✅ Add the custom route (e.g., /china/...)
if 'odoo' in url and base_sorturl[0]:
custom_url = url.replace('odoo', base_sorturl[0])
routing = submap(endpoint.routing, ROUTING_KEYS)
if routing['methods'] is not None and 'OPTIONS' not in routing['methods']:
routing['methods'] = routing['methods'] + ['OPTIONS']
rule = FasterRule(custom_url, endpoint=endpoint, **routing)
rule.merge_slashes = False
routing_map.add(rule)
_logger.debug("Added custom route: %s", custom_url)
# ✅ ALWAYS add the original route (e.g., /odoo/...)
routing = submap(endpoint.routing, ROUTING_KEYS)
if routing['methods'] is not None and 'OPTIONS' not in routing['methods']:
routing['methods'] = routing['methods'] + ['OPTIONS']
rule = FasterRule(url, endpoint=endpoint, **routing)
rule.merge_slashes = False
routing_map.add(rule)
_logger.debug("Added original route: %s", url)
return routing_map
IrHttp.routing_map = routing_map
+35
View File
@@ -0,0 +1,35 @@
# middleware.py
import re
import odoo
from odoo.tools import config as odoo_config
from odoo import tools, api, fields, models, _
class URLRewriteMiddleware:
"""WSGI middleware to rewrite 'odoo' in URLs to custom base"""
def __init__(self, app):
self.app = app
def _get_replacement(self):
"""Get replacement value from ir.config_parameter"""
try:
from odoo.modules.registry import Registry
registry = Registry(odoo_config.get('database', 'postgres'))
with registry.cursor() as cr:
env = api.Environment(cr, odoo.SUPERUSER_ID, {})
return env['ir.config_parameter'].sudo().get_param('web.base.sorturl', '')
except Exception:
return ''
def __call__(self, environ, start_response):
path = environ.get('PATH_INFO', '')
replacement = self._get_replacement()
# Only rewrite non-static paths containing 'odoo'
if replacement and 'odoo' in path and '/web/static' not in path:
new_path = path.replace('odoo', replacement, 1) # Replace first occurrence only
environ['PATH_INFO'] = new_path
environ['REQUEST_URI'] = environ.get('REQUEST_URI', '').replace('odoo', replacement, 1)
return self.app(environ, start_response)
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_web_replace_url_web_replace_url,web_replace_url.web_replace_url,model_web_replace_url_web_replace_url,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_web_replace_url_web_replace_url web_replace_url.web_replace_url model_web_replace_url_web_replace_url base.group_user 1 1 1 1
@@ -0,0 +1,31 @@
// web_url_view_ir_config_inherit_form.js - Odoo 19 Compatible
import { registry } from "@web/core/registry";
import { FormController } from "@web/views/form/form_controller";
import { formView } from "@web/views/form/form_view";
import { browser } from '@web/core/browser/browser';
const defaultRegistry = registry.category("views");
export class ReloadFormController extends FormController {
async save(params = {}) {
// ✅ Call super with proper params handling for Odoo 19
const saved = await super.save(params);
// Check if we saved the web.base.sorturl config parameter
if (saved && this.model.root?.data?.key === 'web.base.sorturl') {
// ✅ Use browser.location for reliable redirect in Odoo 19
// Add debug=assets to force asset regeneration
browser.location.href = "/?debug=assets";
return true;
}
return saved;
}
}
export const ReloadFormView = {
...formView,
Controller: ReloadFormController,
};
// ✅ Register the view with proper category for Odoo 19
defaultRegistry.add("web_url_view_ir_config_inherit_form", ReloadFormView);
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="view_ir_config_inherit_form">
<field name="name">base.view_ir_config_inherit_form</field>
<field name="model">ir.config_parameter</field>
<field name="inherit_id" ref="base.view_ir_config_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="attributes">
<attribute name="js_class">web_url_view_ir_config_inherit_form</attribute>
</xpath>
</field>
</record>
</odoo>