From 8fc85fb49f48db50c08d74dcc0463549722f2d4e Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Wed, 16 Jul 2025 05:46:39 +0000 Subject: [PATCH] [ADD] web_hide_custom_search --- .../odoo/addons/web_hide_custom_search | 1 + setup/web_hide_custom_search/setup.py | 6 + web_hide_custom_search/README.rst | 95 ++++ web_hide_custom_search/__init__.py | 1 + web_hide_custom_search/__manifest__.py | 27 ++ web_hide_custom_search/models/__init__.py | 2 + web_hide_custom_search/models/ir_model.py | 24 + web_hide_custom_search/models/res_groups.py | 16 + web_hide_custom_search/readme/CONFIGURE.md | 11 + web_hide_custom_search/readme/CONTRIBUTORS.md | 2 + web_hide_custom_search/readme/DESCRIPTION.md | 2 + .../static/description/index.html | 440 ++++++++++++++++++ .../static/src/js/custom_filter_patch.esm.js | 24 + .../src/js/custom_group_by_patch.esm.js | 28 ++ .../js/search_menu_custom_filter_patch.esm.js | 28 ++ .../search_menu_custom_group_by_patch.esm.js | 28 ++ .../static/src/xml/custom_filter_template.xml | 19 + .../src/xml/custom_group_by_template.xml | 16 + .../tests/web_hide_custom_search.esm.js | 67 +++ .../views/ir_model_views.xml | 17 + .../views/res_groups_views.xml | 17 + 21 files changed, 871 insertions(+) create mode 120000 setup/web_hide_custom_search/odoo/addons/web_hide_custom_search create mode 100644 setup/web_hide_custom_search/setup.py create mode 100644 web_hide_custom_search/README.rst create mode 100644 web_hide_custom_search/__init__.py create mode 100644 web_hide_custom_search/__manifest__.py create mode 100644 web_hide_custom_search/models/__init__.py create mode 100644 web_hide_custom_search/models/ir_model.py create mode 100644 web_hide_custom_search/models/res_groups.py create mode 100644 web_hide_custom_search/readme/CONFIGURE.md create mode 100644 web_hide_custom_search/readme/CONTRIBUTORS.md create mode 100644 web_hide_custom_search/readme/DESCRIPTION.md create mode 100644 web_hide_custom_search/static/description/index.html create mode 100644 web_hide_custom_search/static/src/js/custom_filter_patch.esm.js create mode 100644 web_hide_custom_search/static/src/js/custom_group_by_patch.esm.js create mode 100644 web_hide_custom_search/static/src/js/search_menu_custom_filter_patch.esm.js create mode 100644 web_hide_custom_search/static/src/js/search_menu_custom_group_by_patch.esm.js create mode 100644 web_hide_custom_search/static/src/xml/custom_filter_template.xml create mode 100644 web_hide_custom_search/static/src/xml/custom_group_by_template.xml create mode 100644 web_hide_custom_search/static/tests/web_hide_custom_search.esm.js create mode 100644 web_hide_custom_search/views/ir_model_views.xml create mode 100644 web_hide_custom_search/views/res_groups_views.xml diff --git a/setup/web_hide_custom_search/odoo/addons/web_hide_custom_search b/setup/web_hide_custom_search/odoo/addons/web_hide_custom_search new file mode 120000 index 0000000000..fa45dd3fdc --- /dev/null +++ b/setup/web_hide_custom_search/odoo/addons/web_hide_custom_search @@ -0,0 +1 @@ +../../../../web_hide_custom_search \ No newline at end of file diff --git a/setup/web_hide_custom_search/setup.py b/setup/web_hide_custom_search/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/web_hide_custom_search/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_hide_custom_search/README.rst b/web_hide_custom_search/README.rst new file mode 100644 index 0000000000..fdcda2271e --- /dev/null +++ b/web_hide_custom_search/README.rst @@ -0,0 +1,95 @@ +====================== +Web Hide Custom Search +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e6b42245e8f445172fd95488fce48ce16240881b5e53d4730208a3b9db9e8197 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/15.0/web_hide_custom_search + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-15-0/server-ux-15-0-web_hide_custom_search + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-ux&target_branch=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows controlling the visibility of the “Add Custom Filter” +and “Add Custom Group” options in the search view for specific models +and specific user groups. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To show the “Add Custom Filter” and “Add Custom Group” options for a +specific model to specific groups only: + +1. Go to **Settings → Technical → Database Structure → Models**. +2. Open the model where you want to control the visibility of the **Add + Custom Filter** and **Add Custom Group** options. +3. Set the **Custom Search Groups** field as needed. If no groups are + assigned, Odoo will follow its standard behavior. + +Alternatively, you can assign models from the Groups form view by adding +them under the "Custom Search Models" tab. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Quartile + +Contributors +------------ + +- `Quartile `__: + + - Aung Ko Ko Lin + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_hide_custom_search/__init__.py b/web_hide_custom_search/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/web_hide_custom_search/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/web_hide_custom_search/__manifest__.py b/web_hide_custom_search/__manifest__.py new file mode 100644 index 0000000000..c78814e152 --- /dev/null +++ b/web_hide_custom_search/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Web Hide Custom Search", + "category": "Usability", + "version": "15.0.1.0.0", + "author": "Quartile, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-ux", + "license": "AGPL-3", + "depends": ["web"], + "data": [ + "views/ir_model_views.xml", + "views/res_groups_views.xml", + ], + "assets": { + "web.assets_qweb": [ + "web_hide_custom_search/static/src/xml/*.xml", + ], + "web.assets_backend": [ + "web_hide_custom_search/static/src/js/*.esm.js", + ], + "web.qunit_suite_tests": [ + "web_hide_custom_search/static/tests/*.esm.js", + ], + }, + "installable": True, +} diff --git a/web_hide_custom_search/models/__init__.py b/web_hide_custom_search/models/__init__.py new file mode 100644 index 0000000000..febff92aea --- /dev/null +++ b/web_hide_custom_search/models/__init__.py @@ -0,0 +1,2 @@ +from . import ir_model +from . import res_groups diff --git a/web_hide_custom_search/models/ir_model.py b/web_hide_custom_search/models/ir_model.py new file mode 100644 index 0000000000..b5e3f67486 --- /dev/null +++ b/web_hide_custom_search/models/ir_model.py @@ -0,0 +1,24 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class IrModel(models.Model): + _inherit = "ir.model" + + custom_search_group_ids = fields.Many2many( + "res.groups", + string="Custom Search Groups", + relation="custom_search_group_rel", + help="If set, only users in these groups will see the 'Add Custom Filter' " + "and 'Add Custom Group' options in the search view for this model.", + ) + + @api.model + def is_custom_search_visible(self, model_name): + model = self.sudo().search([("model", "=", model_name)], limit=1) + groups = model.custom_search_group_ids + if not model or not groups or self.env.user in groups.users: + return True + return False diff --git a/web_hide_custom_search/models/res_groups.py b/web_hide_custom_search/models/res_groups.py new file mode 100644 index 0000000000..8144e24f4a --- /dev/null +++ b/web_hide_custom_search/models/res_groups.py @@ -0,0 +1,16 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResGroups(models.Model): + _inherit = "res.groups" + + custom_search_model_ids = fields.Many2many( + "ir.model", + relation="custom_search_group_rel", + string="Custom Search Models", + help="Only users in this group will see the 'Add Custom Filter' and " + "'Add Custom Group' options in the search view of the selected models.", + ) diff --git a/web_hide_custom_search/readme/CONFIGURE.md b/web_hide_custom_search/readme/CONFIGURE.md new file mode 100644 index 0000000000..719ee7d641 --- /dev/null +++ b/web_hide_custom_search/readme/CONFIGURE.md @@ -0,0 +1,11 @@ +To show the “Add Custom Filter” and “Add Custom Group” options for a specific model +to specific groups only: + +1. Go to **Settings → Technical → Database Structure → Models**. +2. Open the model where you want to control the visibility of the + **Add Custom Filter** and **Add Custom Group** options. +3. Set the **Custom Search Groups** field as needed. + If no groups are assigned, Odoo will follow its standard behavior. + +Alternatively, you can assign models from the Groups form view by adding them +under the "Custom Search Models" tab. diff --git a/web_hide_custom_search/readme/CONTRIBUTORS.md b/web_hide_custom_search/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..faae3280c4 --- /dev/null +++ b/web_hide_custom_search/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [Quartile](https://www.quartile.co): + - Aung Ko Ko Lin diff --git a/web_hide_custom_search/readme/DESCRIPTION.md b/web_hide_custom_search/readme/DESCRIPTION.md new file mode 100644 index 0000000000..45ba7562f7 --- /dev/null +++ b/web_hide_custom_search/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module allows controlling the visibility of the “Add Custom Filter” and +“Add Custom Group” options in the search view for specific models and specific user groups. diff --git a/web_hide_custom_search/static/description/index.html b/web_hide_custom_search/static/description/index.html new file mode 100644 index 0000000000..ac7c7e3aa3 --- /dev/null +++ b/web_hide_custom_search/static/description/index.html @@ -0,0 +1,440 @@ + + + + + +Web Hide Custom Search + + + + + + diff --git a/web_hide_custom_search/static/src/js/custom_filter_patch.esm.js b/web_hide_custom_search/static/src/js/custom_filter_patch.esm.js new file mode 100644 index 0000000000..6ea39cb516 --- /dev/null +++ b/web_hide_custom_search/static/src/js/custom_filter_patch.esm.js @@ -0,0 +1,24 @@ +odoo.define("web_hide_custom_search.web.CustomFilterItemPatch", function (require) { + const CustomFilterItem = require("web.CustomFilterItem"); + const {patch} = require("web.utils"); + const {hooks, useState} = owl; + const {onWillStart} = hooks; + const rpc = require("web.rpc"); + + patch(CustomFilterItem.prototype, "web_hide_custom_search.CustomFilterItemPatch", { + setup() { + this._super.apply(this, arguments); + this.visibleState = useState({isVisible: true}); + + onWillStart(async () => { + const modelName = this.model.config.modelName; + const isVisible = await rpc.query({ + model: "ir.model", + method: "is_custom_search_visible", + args: [modelName], + }); + this.visibleState.isVisible = isVisible; + }); + }, + }); +}); diff --git a/web_hide_custom_search/static/src/js/custom_group_by_patch.esm.js b/web_hide_custom_search/static/src/js/custom_group_by_patch.esm.js new file mode 100644 index 0000000000..52fd0d54cb --- /dev/null +++ b/web_hide_custom_search/static/src/js/custom_group_by_patch.esm.js @@ -0,0 +1,28 @@ +odoo.define("web_hide_custom_search.CustomGroupByItemPatch", function (require) { + const CustomGroupByItem = require("web.CustomGroupByItem"); + const {patch} = require("web.utils"); + const {hooks, useState} = owl; + const {onWillStart} = hooks; + const rpc = require("web.rpc"); + + patch( + CustomGroupByItem.prototype, + "web_hide_custom_search.CustomGroupByItemPatch", + { + setup() { + this._super.apply(this, arguments); + this.visibleState = useState({isVisible: true}); + + onWillStart(async () => { + const modelName = this.model.config.modelName; + const isVisible = await rpc.query({ + model: "ir.model", + method: "is_custom_search_visible", + args: [modelName], + }); + this.visibleState.isVisible = isVisible; + }); + }, + } + ); +}); diff --git a/web_hide_custom_search/static/src/js/search_menu_custom_filter_patch.esm.js b/web_hide_custom_search/static/src/js/search_menu_custom_filter_patch.esm.js new file mode 100644 index 0000000000..d912d85623 --- /dev/null +++ b/web_hide_custom_search/static/src/js/search_menu_custom_filter_patch.esm.js @@ -0,0 +1,28 @@ +/** @odoo-module **/ + +import {patch} from "@web/core/utils/patch"; +import rpc from "web.rpc"; +import {CustomFilterItem} from "@web/search/filter_menu/custom_filter_item"; +const {hooks, useState} = owl; +const {onWillStart} = hooks; + +patch( + CustomFilterItem.prototype, + "web_hide_custom_search.search.CustomFilterItemPatch", + { + setup() { + this._super(...arguments); + this.visibleState = useState({isVisible: true}); + + onWillStart(async () => { + const modelName = this.env.searchModel.resModel; + const isVisible = await rpc.query({ + model: "ir.model", + method: "is_custom_search_visible", + args: [modelName], + }); + this.visibleState.isVisible = isVisible; + }); + }, + } +); diff --git a/web_hide_custom_search/static/src/js/search_menu_custom_group_by_patch.esm.js b/web_hide_custom_search/static/src/js/search_menu_custom_group_by_patch.esm.js new file mode 100644 index 0000000000..2695666b91 --- /dev/null +++ b/web_hide_custom_search/static/src/js/search_menu_custom_group_by_patch.esm.js @@ -0,0 +1,28 @@ +/** @odoo-module **/ + +import {patch} from "@web/core/utils/patch"; +import rpc from "web.rpc"; +import {CustomGroupByItem} from "@web/search/group_by_menu/custom_group_by_item"; +const {hooks, useState} = owl; +const {onWillStart} = hooks; + +patch( + CustomGroupByItem.prototype, + "web_hide_custom_search.search.CustomGroupByItemPatch", + { + setup() { + this._super(...arguments); + this.visibleState = useState({isVisible: true}); + + onWillStart(async () => { + const modelName = this.env.searchModel.resModel; + const isVisible = await rpc.query({ + model: "ir.model", + method: "is_custom_search_visible", + args: [modelName], + }); + this.visibleState.isVisible = isVisible; + }); + }, + } +); diff --git a/web_hide_custom_search/static/src/xml/custom_filter_template.xml b/web_hide_custom_search/static/src/xml/custom_filter_template.xml new file mode 100644 index 0000000000..6c26176f91 --- /dev/null +++ b/web_hide_custom_search/static/src/xml/custom_filter_template.xml @@ -0,0 +1,19 @@ + + + + + visibleState.isVisible + + + + + + + + diff --git a/web_hide_custom_search/static/src/xml/custom_group_by_template.xml b/web_hide_custom_search/static/src/xml/custom_group_by_template.xml new file mode 100644 index 0000000000..7328d2508c --- /dev/null +++ b/web_hide_custom_search/static/src/xml/custom_group_by_template.xml @@ -0,0 +1,16 @@ + + + + + visibleState.isVisible + + + + + + + + diff --git a/web_hide_custom_search/static/tests/web_hide_custom_search.esm.js b/web_hide_custom_search/static/tests/web_hide_custom_search.esm.js new file mode 100644 index 0000000000..b32bce53fb --- /dev/null +++ b/web_hide_custom_search/static/tests/web_hide_custom_search.esm.js @@ -0,0 +1,67 @@ +/* global QUnit */ +odoo.define("web_hide_custom_search.tests", function (require) { + const CustomFilterItem = require("web.CustomFilterItem"); + const CustomGroupByItem = require("web.CustomGroupByItem"); + const ActionModel = require("web.ActionModel"); + const testUtils = require("web.test_utils"); + const rpc = require("web.rpc"); + const {createComponent} = testUtils; + + function createTestComponent(Component, rpcVisible = true) { + const originalQuery = rpc.query; + rpc.query = (params) => { + if ( + params.model === "ir.model" && + params.method === "is_custom_search_visible" + ) { + return Promise.resolve(rpcVisible); + } + return originalQuery.apply(this, arguments); + }; + return createComponent(Component, { + props: { + fields: [ + {sortable: true, name: "date", string: "Super Date", type: "date"}, + ], + }, + env: { + searchModel: new ActionModel(), + }, + }).then((cmp) => { + rpc.query = originalQuery; // Restore after setup + return cmp; + }); + } + + QUnit.module("Components", {}, function () { + QUnit.module("web_hide_custom_search"); + + QUnit.test("Custom Filter Visible", async function (assert) { + assert.expect(1); + const cgi = await createTestComponent(CustomFilterItem); + assert.strictEqual(cgi.el.innerText.trim(), "Add Custom Filter"); + cgi.destroy(); + }); + + QUnit.test("Custom Filter Invisible", async function (assert) { + assert.expect(1); + const cgi = await createTestComponent(CustomFilterItem, false); + assert.notStrictEqual(cgi.el.innerText.trim(), "Add Custom Filter"); + cgi.destroy(); + }); + + QUnit.test("Custom Group By Visible", async function (assert) { + assert.expect(1); + const cgi = await createTestComponent(CustomGroupByItem); + assert.strictEqual(cgi.el.innerText.trim(), "Add Custom Group"); + cgi.destroy(); + }); + + QUnit.test("Custom Group By Invisible", async function (assert) { + assert.expect(1); + const cgi = await createTestComponent(CustomGroupByItem, false); + assert.notStrictEqual(cgi.el.innerText.trim(), "Add Custom Group"); + cgi.destroy(); + }); + }); +}); diff --git a/web_hide_custom_search/views/ir_model_views.xml b/web_hide_custom_search/views/ir_model_views.xml new file mode 100644 index 0000000000..f12b103c65 --- /dev/null +++ b/web_hide_custom_search/views/ir_model_views.xml @@ -0,0 +1,17 @@ + + + + ir.model.form + ir.model + + + + + + + + + + + + diff --git a/web_hide_custom_search/views/res_groups_views.xml b/web_hide_custom_search/views/res_groups_views.xml new file mode 100644 index 0000000000..35fe0e3ecc --- /dev/null +++ b/web_hide_custom_search/views/res_groups_views.xml @@ -0,0 +1,17 @@ + + + + res.groups.form + res.groups + + + + + + + + + + + +