3
0
Fork 0

[MIG] web_field_tooltip: Migration to 16.0

16.0
Benjamin Willig 2024-03-26 08:52:09 +01:00
parent f65f71be8e
commit e729006a19
24 changed files with 402 additions and 372 deletions

View File

@ -0,0 +1 @@
../../../../web_field_tooltip

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

View File

@ -7,7 +7,7 @@ Web Field Tooltip
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:546620e49e8a51bd4af1d867397642b2810a3e7a0d30f39c06cd7d8454a96c43 !! source digest: sha256:6a4fafacb03368529d861e303732dce4c182ee511090cb4fd9ca04d67347bae0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@ -17,20 +17,19 @@ Web Field Tooltip
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/13.0/web_field_tooltip :target: https://github.com/OCA/web/tree/16.0/web_field_tooltip
:alt: OCA/web :alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_field_tooltip :target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_field_tooltip
:alt: Translate me on Weblate :alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=13.0 :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0
:alt: Try me on Runboat :alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5| |badge1| |badge2| |badge3| |badge4| |badge5|
This module gives the possibility to add tooltips next to fields labels on any This module gives the possibility to add tooltips next to fields labels on any
field of a model. The tooltip displays an html field that can contain links and field of a model. The tooltip displays an html field.
the name of the user that last updated it.
**Table of contents** **Table of contents**
@ -65,7 +64,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported. 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 If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_field_tooltip%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/OCA/web/issues/new?body=module:%20web_field_tooltip%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.
@ -95,6 +94,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/13.0/web_field_tooltip>`_ project on GitHub. This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_field_tooltip>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -6,7 +6,7 @@
"name": "Web Field Tooltip", "name": "Web Field Tooltip",
"summary": """ "summary": """
Displays customizable tooltips for fields""", Displays customizable tooltips for fields""",
"version": "13.0.1.0.0", "version": "16.0.1.0.0",
"license": "AGPL-3", "license": "AGPL-3",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)", "author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/web", "website": "https://github.com/OCA/web",
@ -16,6 +16,17 @@
"security/ir_model_access.xml", "security/ir_model_access.xml",
"views/ir_model_fields_tooltip.xml", "views/ir_model_fields_tooltip.xml",
"views/res_users.xml", "views/res_users.xml",
"views/template.xml",
], ],
"assets": {
"web.assets_backend": [
"/web_field_tooltip/static/src/components/field_tooltip/field_tooltip.esm.js",
"/web_field_tooltip/static/src/components/field_tooltip/field_tooltip.scss",
"/web_field_tooltip/static/src/components/field_tooltip/field_tooltip.xml",
"/web_field_tooltip/static/src/views/form/form_controller.esm.js",
"/web_field_tooltip/static/src/views/form/form_label.esm.js",
"/web_field_tooltip/static/src/views/form/form_label.xml",
"/web_field_tooltip/static/src/views/list/list_renderer.esm.js",
"/web_field_tooltip/static/src/views/list/list_renderer.xml",
],
},
} }

View File

@ -1,3 +1,4 @@
from . import base
from . import ir_http from . import ir_http
from . import ir_model_fields_tooltip from . import ir_model_fields_tooltip
from . import res_users from . import res_users

View File

@ -0,0 +1,29 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, models
class Base(models.AbstractModel):
_inherit = "base"
@api.model
def fields_get(self, allfields=None, attributes=None):
res = super().fields_get(allfields=allfields, attributes=attributes)
fnames = res.keys()
tooltips_data = (
self.env["ir.model.fields.tooltip"]
.sudo()
.search_read(
[
("model", "=", self._name),
("field_name", "in", list(fnames)),
],
[],
)
)
for tooltip_data in tooltips_data:
tooltip_fname = tooltip_data["field_name"]
res[tooltip_fname]["field_tooltip"] = tooltip_data
return res

View File

@ -18,20 +18,24 @@ class IrModelFieldsTooltip(models.Model):
help="Model for the Field Tooltip.", help="Model for the Field Tooltip.",
default=lambda self: self._get_default_model_id(), default=lambda self: self._get_default_model_id(),
) )
model = fields.Char(related="model_id.model", string="Model Name") model = fields.Char(related="model_id.model", string="Model Name", store=True)
field_id = fields.Many2one( field_id = fields.Many2one(
string="Field", string="Field",
required=True, required=True,
comodel_name="ir.model.fields", comodel_name="ir.model.fields",
ondelete="cascade", ondelete="cascade",
) )
name = fields.Char(compute="_compute_name", readonly=True,) field_name = fields.Char(related="field_id.name", store=True)
name = fields.Char(
compute="_compute_name",
readonly=True,
)
active = fields.Boolean( active = fields.Boolean(
default=True, default=True,
help="Set active to false to hide the Tooltip without removing it.", help="Set active to false to hide the Tooltip without removing it.",
) )
field_name = fields.Char(related="field_id.name") field_name = fields.Char(related="field_id.name")
tooltip_text = fields.Html(string="Tooltip Text", required=True) tooltip_text = fields.Html(required=True)
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
@ -61,7 +65,7 @@ class IrModelFieldsTooltip(models.Model):
raise UserError(_("A tooltip already exists for this field")) raise UserError(_("A tooltip already exists for this field"))
def _get_default_model_id(self): def _get_default_model_id(self):
tooltip_model = self.env.context.get("tooltip_model") tooltip_model = self.env.context.get("default_model")
model = self.env["ir.model"].search([("model", "=", tooltip_model)], limit=1) model = self.env["ir.model"].search([("model", "=", tooltip_model)], limit=1)
return model.id or False return model.id or False

View File

@ -16,11 +16,17 @@ class ResUsers(models.Model):
compute="_compute_tooltip_show_add_helper_allowed" compute="_compute_tooltip_show_add_helper_allowed"
) )
def __init__(self, pool, cr): @property
super().__init__(pool, cr) def TOOLTIP_READABLE_FIELDS(self):
field_names = ["tooltip_show_add_helper"] return ["tooltip_show_add_helper"]
self.SELF_READABLE_FIELDS.extend(field_names)
self.SELF_WRITEABLE_FIELDS.extend(field_names) @property
def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + self.TOOLTIP_READABLE_FIELDS
@property
def SELF_WRITEABLE_FIELDS(self):
return super().SELF_WRITEABLE_FIELDS + self.TOOLTIP_READABLE_FIELDS
def _compute_tooltip_show_add_helper_allowed(self): def _compute_tooltip_show_add_helper_allowed(self):
for rec in self: for rec in self:

View File

@ -1,3 +1,2 @@
This module gives the possibility to add tooltips next to fields labels on any This module gives the possibility to add tooltips next to fields labels on any
field of a model. The tooltip displays an html field that can contain links and field of a model. The tooltip displays an html field.
the name of the user that last updated it.

View File

@ -367,12 +367,11 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:546620e49e8a51bd4af1d867397642b2810a3e7a0d30f39c06cd7d8454a96c43 !! source digest: sha256:6a4fafacb03368529d861e303732dce4c182ee511090cb4fd9ca04d67347bae0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/web/tree/13.0/web_field_tooltip"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/web-13-0/web-13-0-web_field_tooltip"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/web&amp;target_branch=13.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/web/tree/16.0/web_field_tooltip"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_field_tooltip"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/web&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module gives the possibility to add tooltips next to fields labels on any <p>This module gives the possibility to add tooltips next to fields labels on any
field of a model. The tooltip displays an html field that can contain links and field of a model. The tooltip displays an html field.</p>
the name of the user that last updated it.</p>
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents"> <div class="contents local topic" id="contents">
<ul class="simple"> <ul class="simple">
@ -413,7 +412,7 @@ on a field as some fields are not displayed with a label.</li>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. 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 If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_field_tooltip%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_field_tooltip%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
@ -437,7 +436,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose <p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use.</p> promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/13.0/web_field_tooltip">OCA/web</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_field_tooltip">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p> <p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div> </div>
</div> </div>

View File

@ -0,0 +1,86 @@
/** @odoo-module */
import {Component, markup} from "@odoo/owl";
import {FormViewDialog} from "@web/views/view_dialogs/form_view_dialog";
import {session} from "@web/session";
import {usePopover} from "@web/core/popover/popover_hook";
import {useService} from "@web/core/utils/hooks";
export class FieldTooltipPopover extends Component {}
FieldTooltipPopover.template = "web_field_tooltip.FieldTooltipPopover";
export class FieldTooltip extends Component {
setup() {
this.popover = usePopover();
this.tooltipPopover = null;
this.hasFieldTooltip = this.props.hasFieldTooltip;
this.canManageTooltip = session.can_manage_tooltips;
this.showAddHelper =
session.can_manage_tooltips && session.tooltip_show_add_helper;
this.fieldTooltip = this.props.field.field_tooltip;
if (session.can_manage_tooltips) {
this.dialogService = useService("dialog");
}
}
get tooltipInfo() {
const props = this.props;
return {
title: props.field.string,
help: markup(this.tooltipText),
};
}
get tooltipText() {
return this.fieldTooltip.tooltip_text;
}
onClickTooltip(e) {
e.preventDefault();
e.stopPropagation();
if (!this.canManageTooltip) {
return;
}
const tooltipId = (this.fieldTooltip && this.fieldTooltip.id) || false;
this.dialogService.add(FormViewDialog, {
resModel: "ir.model.fields.tooltip",
resId: tooltipId,
context: {
default_model: this.props.resModel,
default_field_name: this.props.fieldName,
},
});
}
onMouseEnter(ev) {
if (!this.hasFieldTooltip) {
return;
}
this.closeTooltip();
this.tooltipPopover = this.popover.add(
ev.currentTarget,
FieldTooltipPopover,
this.tooltipInfo,
{
closeOnClickAway: true,
position: "top",
title: "title",
}
);
}
onMouseLeave() {
this.closeTooltip();
}
closeTooltip() {
if (this.tooltipPopover) {
this.tooltipPopover();
this.tooltipPopover = null;
}
}
}
FieldTooltip.template = "web_field_tooltip.FieldTooltip";

View File

@ -0,0 +1,35 @@
sup.field-tooltip {
.tooltip-icon {
background: none;
border: none;
display: inline-block;
width: fit-content;
margin-left: 0px;
&[has-tooltip] {
color: #666666 !important;
}
}
&:hover {
cursor: pointer;
}
}
.popup-div {
min-width: 100px;
min-height: 30px;
> * {
padding: 5px;
}
.popover-title {
font-weight: bold;
background-color: #f7f7f7;
}
.popover-content {
background-color: white;
}
}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates>
<t t-name="web_field_tooltip.FieldTooltip" owl="1">
<sup
class="field-tooltip"
t-on-click="(ev) => this.onClickTooltip(ev)"
t-on-mouseenter="(ev) => this.onMouseEnter(ev)"
t-on-mouseleave="(ev) => this.onMouseLeave(ev)"
>
<a
class="fa fa fa-question-circle tooltip-icon text-info"
t-att-has-tooltip="props.hasFieldTooltip"
/>
</sup>
</t>
<t t-name="web_field_tooltip.FieldTooltipPopover" owl="1">
<div class="popup-div">
<div class="popover-title">
<span t-esc="props.title" />
</div>
<p class="popover-content">
<t t-out="props.help or ''" />
</p>
</div>
</t>
</templates>

View File

@ -1,23 +0,0 @@
sup.field-tooltip {
margin-right: 5px;
}
sup.field-tooltip:hover {
cursor: pointer;
}
.tooltip-icon {
color: #666666;
background: none;
border: none;
display: inline-block;
width: 0;
font-size: 12px;
margin-left: 0px;
}
.popover-footer {
margin: 0;
padding: 8px 14px;
background-color: #f7f7f7;
}

View File

@ -1,59 +0,0 @@
/* Copyright 2023 ACSONE SA/NV
Copyright 2019 TODAY Serpent Consulting Services Pvt. Ltd.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("web_field_tooltip.controller", function(require) {
"use strict";
const FormController = require("web.FormController");
const ListController = require("web.ListController");
const tooltips = require("web_field_tooltip.FieldTooltip");
const core = require("web.core");
const session = require("web.session");
const _t = core._t;
ListController.include(
Object.assign({}, tooltips.TooltipController, {
custom_events: _.extend({}, ListController.prototype.custom_events, {
add_tooltip: "_onAddTooltip",
edit_tooltip: "_onEditTooltip",
}),
})
);
FormController.include(
Object.assign({}, tooltips.TooltipController, {
custom_events: _.extend({}, FormController.prototype.custom_events, {
add_tooltip: "_onAddTooltip",
edit_tooltip: "_onEditTooltip",
}),
renderSidebar: function($node) {
this._super($node);
if (this.sidebar && session.can_manage_tooltips) {
this.sidebar.items.other.push({
label: _t("Manage Tooltips"),
callback: this.on_manage_tooltips,
});
}
},
on_manage_tooltips: function() {
const self = this;
return self.do_action({
type: "ir.actions.act_window",
name: _t("Manage Tooltips"),
res_model: "ir.model.fields.tooltip",
target: "current",
views: [
[false, "list"],
[false, "form"],
],
view_mode: "list",
domain: [["model", "=", self.modelName]],
context: {tooltip_model: self.modelName},
});
},
})
);
});

View File

@ -1,61 +0,0 @@
/* Copyright 2023 ACSONE SA/NV
Copyright 2019 TODAY Serpent Consulting Services Pvt. Ltd.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("web_field_tooltip.renderer", function(require) {
"use strict";
const FormRenderer = require("web.FormRenderer");
const ListRenderer = require("web.ListRenderer");
const tooltips = require("web_field_tooltip.FieldTooltip");
FormRenderer.include(
Object.assign({}, tooltips.TooltipRenderer, {
init: function() {
this._super.apply(this, arguments);
this.tooltips = undefined;
},
/**
* @override
*/
_renderTagLabel: function(node) {
const self = this;
const $result = this._super.apply(this, arguments);
const fieldName =
node.tag === "label" ? node.attrs.for : node.attrs.name;
if (!fieldName) {
return $result;
}
self.add_tooltip($result, node);
return $result;
},
})
);
ListRenderer.include(
Object.assign({}, tooltips.TooltipRenderer, {
init: function() {
this._super.apply(this, arguments);
this.tooltips = undefined;
},
/**
* @override
*/
_renderHeaderCell: function(node) {
const self = this;
const $result = this._super.apply(this, arguments);
const fieldName =
node.tag === "label" ? node.attrs.for : node.attrs.name;
if (!fieldName) {
return $result;
}
self.add_tooltip($result, node);
return $result;
},
})
);
});

View File

@ -1,189 +0,0 @@
/* Copyright 2023 ACSONE SA/NV
Copyright 2019 TODAY Serpent Consulting Services Pvt. Ltd.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("web_field_tooltip.FieldTooltip", function(require) {
"use strict";
const core = require("web.core");
const dialogs = require("web.view_dialogs");
const field_utils = require("web.field_utils");
const rpc = require("web.rpc");
const session = require("web.session");
const _t = core._t;
const TooltipRenderer = {
add_tooltip: function($result, node) {
const self = this;
const tooltip_title = $result.text();
const fieldName = node.tag === "label" ? node.attrs.for : node.attrs.name;
if (!fieldName) {
return $result;
}
// Check if there's any tooltip that must be rendered for the given view
if (!self.tooltips) {
self.load_tooltips();
}
self.$tooltip_promise.then(function() {
let $tooltip = null;
let allow_add_tooltip_helper =
session.tooltip_show_add_helper && session.can_manage_tooltips;
_.each(self.tooltips, function(tooltip) {
if (tooltip.field_name === fieldName) {
$tooltip = self.get_tooltip_elem(
fieldName,
tooltip_title,
tooltip
);
$result.append($tooltip);
allow_add_tooltip_helper = false;
}
});
if (allow_add_tooltip_helper) {
$result.append(self.get_add_tooltip_elem(fieldName));
}
});
},
load_tooltips: function() {
const self = this;
self.$tooltip_promise = rpc
.query({
model: "ir.model.fields.tooltip",
method: "search_read",
domain: [["model", "=", self.state.model]],
})
.then(function(result) {
self.tooltips = result;
});
},
get_add_tooltip_elem: function(fieldName) {
const self = this;
const $after_elem = $("<a>", {
class: "fa fa fa-question-circle tooltip-icon text-info",
});
$after_elem.on("click", function(e) {
e.preventDefault();
e.stopPropagation();
self.trigger_up("add_tooltip", {
context: {
default_model: self.state.model,
default_field_name: fieldName,
},
});
});
const $sup = $("<sup/>", {
class: "field-tooltip",
});
$sup.append($after_elem);
return $sup;
},
get_tooltip_elem: function(fieldName, tooltip_title, tooltip) {
const self = this;
const $after_elem = $("<a>", {
class: "fa fa fa-question-circle tooltip-icon",
tabIndex: 0,
});
const $popup_div = $("<div/>", {
class: "popup-div",
});
const $popup_text = $("<p>", {
html: tooltip.tooltip_text,
});
$popup_div.append($popup_text);
if (session.can_manage_tooltips) {
const $popup_last_edit = $("<p>", {
class: "popover-footer",
html:
_t("Last Updated by: ") +
field_utils.format.many2one(tooltip.write_uid),
});
const $edit_button = $("<button>", {
title: _t("Edit the tooltip"),
class: "fa fa-edit tooltip-icon",
});
$edit_button.on("click", function(e) {
e.preventDefault();
e.stopPropagation();
self.trigger_up("edit_tooltip", {
res_id: tooltip.id,
});
});
$popup_last_edit.append($edit_button);
$popup_div.append($popup_last_edit);
}
const options = {
content: $popup_div,
html: true,
placement: "top",
title: tooltip_title,
trigger: "focus",
delay: {
show: 0,
hide: 0,
},
};
$after_elem.popover(options);
$after_elem.on("click", function(e) {
e.preventDefault();
e.stopPropagation();
$after_elem.popover("show");
});
const $sup = $("<sup/>", {
class: "field-tooltip",
});
$sup.append($after_elem);
return $sup;
},
};
const TooltipController = {
_onAddTooltip: function(params) {
const self = this;
new dialogs.FormViewDialog(self, {
res_model: "ir.model.fields.tooltip",
context: _.extend(session.user_context, params.data.context),
title: _t("Add a tooltip"),
disable_multiple_selection: true,
}).open();
},
_onEditTooltip: function(params) {
const self = this;
const tooltipId = params.data.res_id;
new dialogs.FormViewDialog(self, {
res_model: "ir.model.fields.tooltip",
res_id: tooltipId,
context: session.user_context,
title: _t("Edit a tooltip"),
disable_multiple_selection: false,
deletable: true,
on_remove: function() {
rpc.query({
model: "ir.model.fields.tooltip",
method: "unlink",
args: [tooltipId],
});
},
}).open();
},
};
return {
TooltipRenderer: TooltipRenderer,
TooltipController: TooltipController,
};
});

View File

@ -0,0 +1,34 @@
/** @odoo-module **/
import {FormController} from "@web/views/form/form_controller";
import {patch} from "@web/core/utils/patch";
import {session} from "@web/session";
patch(FormController.prototype, "web_field_tooltip", {
getActionMenuItems() {
const menuItems = this._super(...arguments);
const otherMenuItems = menuItems.other;
if (session.can_manage_tooltips) {
otherMenuItems.push({
key: "manage_tooltips",
description: this.env._t("Manage tooltips"),
callback: () => this.manageTooltips(),
});
}
return menuItems;
},
manageTooltips() {
const model = this.props.resModel;
this.env.services.action.doAction(
"web_field_tooltip.ir_model_fields_tooltip_act_window",
{
additionalContext: {
search_default_model: model,
default_model: model,
},
}
);
},
});

View File

@ -0,0 +1,34 @@
/** @odoo-module **/
import {FieldTooltip} from "../../components/field_tooltip/field_tooltip.esm";
import {FormLabel} from "@web/views/form/form_label";
import {patch} from "@web/core/utils/patch";
import {session} from "@web/session";
patch(FormLabel.prototype, "web_field_tooltip", {
get showTooltipAddHelper() {
return session.tooltip_show_add_helper;
},
get hasFieldTooltip() {
const props = this.props;
return Boolean(props.record.fields[props.fieldName].field_tooltip);
},
get getFieldTooltipProps() {
const props = this.props;
const record = props.record;
return {
hasFieldTooltip: this.hasFieldTooltip,
resModel: record.resModel,
field: record.fields[props.fieldName],
fieldName: props.fieldName,
};
},
});
FormLabel.components = Object.assign({}, FormLabel.components, {
FieldTooltip,
});

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t
t-name="web_field_tooltip.FormLabel"
t-inherit="web.FormLabel"
t-inherit-mode="extension"
owl="1"
>
<xpath expr="//sup" position="before">
<t t-if="hasFieldTooltip or showTooltipAddHelper">
<FieldTooltip t-props="getFieldTooltipProps" />
</t>
</xpath>
</t>
</templates>

View File

@ -0,0 +1,36 @@
/** @odoo-module **/
import {FieldTooltip} from "../../components/field_tooltip/field_tooltip.esm";
import {ListRenderer} from "@web/views/list/list_renderer";
import {patch} from "@web/core/utils/patch";
import {session} from "@web/session";
patch(ListRenderer.prototype, "web_field_tooltip", {
showTooltipAddHelper() {
return session.tooltip_show_add_helper;
},
hasFieldTooltip(column) {
const fieldName = column.name;
const fields = this.props.list.fields;
return Boolean(fields[fieldName].field_tooltip);
},
getFieldTooltipProps(column) {
const props = this.props;
const fieldName = column.name;
const fields = props.list.fields;
return {
hasFieldTooltip: this.hasFieldTooltip(column),
resModel: props.list.resModel,
field: fields[fieldName],
fieldName: fieldName,
};
},
});
ListRenderer.components = Object.assign({}, ListRenderer.components, {
FieldTooltip,
});

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t
t-name="web_field_tooltip.ListRenderer"
t-inherit="web.ListRenderer"
t-inherit-mode="extension"
owl="1"
>
<xpath
expr="//t[@t-foreach='state.columns']//span[@t-esc='column.label']"
position="after"
>
<div class="d-block min-w-0 text-truncate flex-grow-1">
<t t-if="hasFieldTooltip(column) or showTooltipAddHelper()">
<FieldTooltip t-props="getFieldTooltipProps(column)" />
</t>
</div>
</xpath>
<xpath
expr="//t[@t-foreach='state.columns']//span[@t-esc='column.label']"
position="attributes"
>
<attribute name="class">d-block min-w-0 text-truncate</attribute>
</xpath>
</t>
</templates>

View File

@ -2,10 +2,10 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests.common import Form, SavepointCase from odoo.tests.common import Form, TransactionCase
class TestWebFieldTooltip(SavepointCase): class TestWebFieldTooltip(TransactionCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
@ -34,7 +34,7 @@ class TestWebFieldTooltip(SavepointCase):
"tooltip_text": "this explains a lot", "tooltip_text": "this explains a lot",
} }
) )
self.assertIn(e.exception.name, "A tooltip already exists for this field") self.assertIn(e.exception.args[0], "A tooltip already exists for this field")
def test_tooltip_name(self): def test_tooltip_name(self):
self.assertEqual( self.assertEqual(
@ -43,6 +43,6 @@ class TestWebFieldTooltip(SavepointCase):
def test_tooltip_model_id(self): def test_tooltip_model_id(self):
res_partner_form = Form( res_partner_form = Form(
self.Tooltip.with_context(tooltip_model=self.partner_model_name) self.Tooltip.with_context(default_model=self.partner_model_name)
) )
self.assertEqual(res_partner_form.model_id, self.partner_model) self.assertEqual(res_partner_form.model_id, self.partner_model)

View File

@ -15,29 +15,37 @@
bg_color="bg-danger" bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}" attrs="{'invisible': [('active', '=', True)]}"
/> />
<group> <group name="first">
<field name="active" invisible="1" /> <group name="left">
<field name="model_id" /> <field name="active" invisible="1" />
<field name="field_id" domain="[('model_id', '=', model_id)]" /> <field name="model_id" />
<field
name="field_id"
domain="[('model_id', '=', model_id)]"
/>
</group>
<group name="right">
<field name="write_date" />
<field name="write_uid" />
</group>
</group> </group>
<group> <group name="second">
<field name="tooltip_text" /> <field name="tooltip_text" />
</group> </group>
<group>
<field name="write_date" />
<field name="write_uid" />
</group>
</sheet> </sheet>
</form> </form>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="ir_model_fields_tooltip_search_view"> <record model="ir.ui.view" id="ir_model_fields_tooltip_search_view">
<field name="name">Fields Tooltips</field>
<field name="model">ir.model.fields.tooltip</field> <field name="model">ir.model.fields.tooltip</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search> <search>
<field name="model_id" /> <field name="model_id" />
<field name="model" />
<field name="field_id" /> <field name="field_id" />
<field name="field_name" />
<separator /> <separator />
<filter <filter
string="Archived" string="Archived"