[IMP] web_timeline: Follow create/edit/delete attrs

In addition to security rights (was already implemented), now follow
`create="0"` / `edit="0"` / `delete="0"` attributes one can set onto the
`timeline` tag, same as in other Odoo views.
pull/3136/head
Houzéfa Abbasbhay 2024-04-04 12:48:32 +02:00 committed by JasminSForgeFlow
parent 6f514424be
commit 8de0b1776b
7 changed files with 87 additions and 44 deletions

View File

@ -7,7 +7,7 @@ Web timeline
!! 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:bd504d36989179a1e11223c03dbf38ac45f0a792c6ad15a2786c9283bc64650b !! source digest: sha256:de40cea8c12ae858a82a28ad5fae3c70c75def8b5b12311ed124973ec7da15c9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
@ -181,7 +181,6 @@ Known issues / Roadmap
* Implement a more efficient way of refreshing timeline after a record update; * Implement a more efficient way of refreshing timeline after a record update;
* Make ``attrs`` attribute work; * Make ``attrs`` attribute work;
* Make action attributes work (create, edit, delete) like in form and tree views.
* When grouping by m2m and more than one record is set, the timeline item appears only * When grouping by m2m and more than one record is set, the timeline item appears only
on one group. Allow showing in both groups. on one group. Allow showing in both groups.
* When grouping by m2m and dragging for changing the time or the group, the changes on * When grouping by m2m and dragging for changing the time or the group, the changes on

View File

@ -4,7 +4,7 @@
{ {
"name": "Web timeline", "name": "Web timeline",
"summary": "Interactive visualization chart to show events in time", "summary": "Interactive visualization chart to show events in time",
"version": "16.0.1.1.4", "version": "16.0.1.1.5",
"development_status": "Production/Stable", "development_status": "Production/Stable",
"author": "ACSONE SA/NV, " "author": "ACSONE SA/NV, "
"Tecnativa, " "Tecnativa, "

View File

@ -1,6 +1,5 @@
* Implement a more efficient way of refreshing timeline after a record update; * Implement a more efficient way of refreshing timeline after a record update;
* Make ``attrs`` attribute work; * Make ``attrs`` attribute work;
* Make action attributes work (create, edit, delete) like in form and tree views.
* When grouping by m2m and more than one record is set, the timeline item appears only * When grouping by m2m and more than one record is set, the timeline item appears only
on one group. Allow showing in both groups. on one group. Allow showing in both groups.
* When grouping by m2m and dragging for changing the time or the group, the changes on * When grouping by m2m and dragging for changing the time or the group, the changes on

View File

@ -367,7 +367,7 @@ 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:bd504d36989179a1e11223c03dbf38ac45f0a792c6ad15a2786c9283bc64650b !! source digest: sha256:de40cea8c12ae858a82a28ad5fae3c70c75def8b5b12311ed124973ec7da15c9
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.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_timeline"><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_timeline"><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><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.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_timeline"><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_timeline"><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>Define a new view displaying events in an interactive visualization chart.</p> <p>Define a new view displaying events in an interactive visualization chart.</p>
@ -553,7 +553,6 @@ with the dragged start and end date.</p>
<ul class="simple"> <ul class="simple">
<li>Implement a more efficient way of refreshing timeline after a record update;</li> <li>Implement a more efficient way of refreshing timeline after a record update;</li>
<li>Make <tt class="docutils literal">attrs</tt> attribute work;</li> <li>Make <tt class="docutils literal">attrs</tt> attribute work;</li>
<li>Make action attributes work (create, edit, delete) like in form and tree views.</li>
<li>When grouping by m2m and more than one record is set, the timeline item appears only <li>When grouping by m2m and more than one record is set, the timeline item appears only
on one group. Allow showing in both groups.</li> on one group. Allow showing in both groups.</li>
<li>When grouping by m2m and dragging for changing the time or the group, the changes on <li>When grouping by m2m and dragging for changing the time or the group, the changes on

View File

@ -12,6 +12,7 @@ import {Component} from "@odoo/owl";
export default AbstractController.extend({ export default AbstractController.extend({
custom_events: _.extend({}, AbstractController.prototype.custom_events, { custom_events: _.extend({}, AbstractController.prototype.custom_events, {
onGroupClick: "_onGroupClick", onGroupClick: "_onGroupClick",
onItemDoubleClick: "_onItemDoubleClick",
onUpdate: "_onUpdate", onUpdate: "_onUpdate",
onRemove: "_onRemove", onRemove: "_onRemove",
onMove: "_onMove", onMove: "_onMove",
@ -101,6 +102,17 @@ export default AbstractController.extend({
}); });
}, },
/**
* Triggered on double-click on an item in read-only mode (otherwise, we use _onUpdate).
*
* @private
* @param {EventObject} event
* @returns {jQuery.Deferred}
*/
_onItemDoubleClick: function (event) {
return this.openItem(event.data.item, false);
},
/** /**
* Opens a form view of a clicked timeline * Opens a form view of a clicked timeline
* item (triggered by the TimelineRenderer). * item (triggered by the TimelineRenderer).
@ -109,33 +121,35 @@ export default AbstractController.extend({
* @param {EventObject} event * @param {EventObject} event
*/ */
_onUpdate: function (event) { _onUpdate: function (event) {
this.renderer = event.data.renderer;
const rights = event.data.rights;
const item = event.data.item; const item = event.data.item;
const id = Number(item.evt.id) || item.evt.id; const item_id = Number(item.evt.id) || item.evt.id;
const title = item.evt.__name; return this.openItem(item_id, true);
},
/** Open specified item, either through modal, or by navigating to form view. */
openItem: function (item_id, is_editable) {
if (this.open_popup_action) { if (this.open_popup_action) {
const options = {
resModel: this.model.modelName,
resId: item_id,
context: this.getSession().user_context,
};
if (is_editable) {
options.onRecordSaved = () => this.write_completed();
} else {
options.preventEdit = true;
}
this.Dialog = Component.env.services.dialog.add( this.Dialog = Component.env.services.dialog.add(
FormViewDialog, FormViewDialog,
{ options,
resId: id,
context: this.getSession().user_context,
title: title,
onRecordSaved: () => this.write_completed(),
resModel: this.model.modelName,
},
{} {}
); );
} else { } else {
let mode = "readonly";
if (rights.write) {
mode = "edit";
}
this.trigger_up("switch_view", { this.trigger_up("switch_view", {
view_type: "form", view_type: "form",
res_id: id,
mode: mode,
model: this.model.modelName, model: this.model.modelName,
res_id: item_id,
mode: is_editable ? "edit" : "readonly",
}); });
} }
}, },

View File

@ -29,6 +29,9 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
this.modelName = params.model; this.modelName = params.model;
this.mode = params.mode; this.mode = params.mode;
this.options = params.options; this.options = params.options;
this.can_create = params.can_create;
this.can_update = params.can_update;
this.can_delete = params.can_delete;
this.min_height = params.min_height; this.min_height = params.min_height;
this.date_start = params.date_start; this.date_start = params.date_start;
this.date_stop = params.date_stop; this.date_stop = params.date_stop;
@ -192,22 +195,25 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
*/ */
init_timeline: function () { init_timeline: function () {
this._computeMode(); this._computeMode();
this.options.editable = { this.options.editable = {};
// Add new items by double tapping if (this.can_update && this.modelClass.data.rights.write) {
add: this.modelClass.data.rights.create, this.options.onMove = this.on_move;
this.options.onUpdate = this.on_update;
// Drag items horizontally // Drag items horizontally
updateTime: this.modelClass.data.rights.write, this.options.editable.updateTime = true;
// Drag items from one group to another // Drag items from one group to another
updateGroup: this.modelClass.data.rights.write, this.options.editable.updateGroup = true;
if (this.can_create && this.modelClass.data.rights.create) {
this.options.onAdd = this.on_add;
// Add new items by double tapping
this.options.editable.add = true;
}
}
if (this.can_delete && this.modelClass.data.rights.unlink) {
this.options.onRemove = this.on_remove;
// Delete an item by tapping the delete button top right // Delete an item by tapping the delete button top right
remove: this.modelClass.data.rights.unlink, this.options.editable.remove = true;
}; }
$.extend(this.options, {
onAdd: this.on_add,
onMove: this.on_move,
onUpdate: this.on_update,
onRemove: this.on_remove,
});
this.options.xss = {disabled: true}; this.options.xss = {disabled: true};
this.qweb = new QWeb(session.debug, {_s: session.origin}, false); this.qweb = new QWeb(session.debug, {_s: session.origin}, false);
if (this.arch.children.length) { if (this.arch.children.length) {
@ -218,7 +224,11 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
} }
this.timeline = new vis.Timeline(this.$timeline.get(0), {}, this.options); this.timeline = new vis.Timeline(this.$timeline.get(0), {}, this.options);
this.timeline.on("click", this.on_group_click); this.timeline.on("click", this.on_timeline_click);
if (!this.options.onUpdate) {
// In read-only mode, catch double-clicks this way.
this.timeline.on("doubleClick", this.on_timeline_double_click);
}
const group_bys = this.arch.attrs.default_group_by.split(","); const group_bys = this.arch.attrs.default_group_by.split(",");
this.last_group_bys = group_bys; this.last_group_bys = group_bys;
this.last_domains = this.modelClass.data.domain; this.last_domains = this.modelClass.data.domain;
@ -553,12 +563,12 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
}, },
/** /**
* Handle a click on a group header. * Handle a click within the timeline.
* *
* @param {ClickEvent} e * @param {ClickEvent} e
* @private * @private
*/ */
on_group_click: function (e) { on_timeline_click: function (e) {
if (e.what === "group-label" && e.group !== -1) { if (e.what === "group-label" && e.group !== -1) {
this._trigger( this._trigger(
e, e,
@ -570,6 +580,24 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
} }
}, },
/**
* Handle a double-click within the timeline.
*
* @param {ClickEvent} e
* @private
*/
on_timeline_double_click: function (e) {
if (e.what === "item" && e.item !== -1) {
this._trigger(
e.item,
() => {
// No callback
},
"onItemDoubleClick"
);
}
},
/** /**
* Trigger onUpdate. * Trigger onUpdate.
* *
@ -615,7 +643,7 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
}, },
/** /**
* Trigger_up encapsulation adds by default the rights, and the renderer. * Trigger_up encapsulation adds by default the renderer.
* *
* @param {HTMLElement} item * @param {HTMLElement} item
* @param {Function} callback * @param {Function} callback
@ -626,7 +654,6 @@ odoo.define("web_timeline.TimelineRenderer", function (require) {
this.trigger_up(trigger, { this.trigger_up(trigger, {
item: item, item: item,
callback: callback, callback: callback,
rights: this.modelClass.data.rights,
renderer: this, renderer: this,
}); });
}, },

View File

@ -22,6 +22,10 @@ odoo.define("web_timeline.TimelineView", function (require) {
return _.isUndefined(value) || _.isNull(value); return _.isUndefined(value) || _.isNull(value);
} }
function toBoolDefaultTrue(value) {
return isNullOrUndef(value) ? true : utils.toBoolElse(value, true);
}
var TimelineView = AbstractView.extend({ var TimelineView = AbstractView.extend({
display_name: _lt("Timeline"), display_name: _lt("Timeline"),
icon: "fa fa-tasks", icon: "fa fa-tasks",
@ -100,6 +104,9 @@ odoo.define("web_timeline.TimelineView", function (require) {
this.rendererParams.model = this.modelName; this.rendererParams.model = this.modelName;
this.rendererParams.view = this; this.rendererParams.view = this;
this.rendererParams.options = this._preapre_vis_timeline_options(attrs); this.rendererParams.options = this._preapre_vis_timeline_options(attrs);
this.rendererParams.can_create = toBoolDefaultTrue(attrs.create);
this.rendererParams.can_update = toBoolDefaultTrue(attrs.edit);
this.rendererParams.can_delete = toBoolDefaultTrue(attrs.delete);
this.rendererParams.date_start = date_start; this.rendererParams.date_start = date_start;
this.rendererParams.date_stop = date_stop; this.rendererParams.date_stop = date_stop;
this.rendererParams.date_delay = date_delay; this.rendererParams.date_delay = date_delay;
@ -127,9 +134,7 @@ odoo.define("web_timeline.TimelineView", function (require) {
selectable: true, selectable: true,
multiselect: true, multiselect: true,
showCurrentTime: true, showCurrentTime: true,
stack: isNullOrUndef(attrs.stack) stack: toBoolDefaultTrue(attrs.stack),
? true
: utils.toBoolElse(attrs.stack, true),
margin: attrs.margin ? JSON.parse(attrs.margin) : {item: 2}, margin: attrs.margin ? JSON.parse(attrs.margin) : {item: 2},
zoomKey: attrs.zoomKey || "ctrlKey", zoomKey: attrs.zoomKey || "ctrlKey",
}; };