+
+Maintainer
+----------
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+This module is maintained by the OCA.
+
+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.
+
+To contribute to this module, please visit https://odoo-community.org.
diff --git a/web_tree_dynamic_colored_field/__init__.py b/web_tree_dynamic_colored_field/__init__.py
new file mode 100644
index 000000000..8945c43ca
--- /dev/null
+++ b/web_tree_dynamic_colored_field/__init__.py
@@ -0,0 +1,2 @@
+# Copyright 2015-2018 Camptocamp SA, Damien Crier
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
diff --git a/web_tree_dynamic_colored_field/__manifest__.py b/web_tree_dynamic_colored_field/__manifest__.py
new file mode 100644
index 000000000..665310b81
--- /dev/null
+++ b/web_tree_dynamic_colored_field/__manifest__.py
@@ -0,0 +1,19 @@
+# Copyright 2015-2018 Camptocamp SA, Damien Crier
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+{
+ 'name': 'Colorize field in tree views',
+ 'summary': 'Allows you to dynamically color fields on tree views',
+ 'category': 'Hidden/Dependency',
+ 'version': '11.0.1.0.0',
+ 'depends': ['web'],
+ 'author': "Camptocamp, Therp BV, Odoo Community Association (OCA)",
+ 'license': 'AGPL-3',
+ 'website': 'https://github.com/OCA/web',
+ 'demo': [
+ "demo/res_users.xml",
+ ],
+ 'data': [
+ 'views/web_tree_dynamic_colored_field.xml',
+ ],
+ 'installable': True,
+}
diff --git a/web_tree_dynamic_colored_field/demo/res_users.xml b/web_tree_dynamic_colored_field/demo/res_users.xml
new file mode 100644
index 000000000..ef364722f
--- /dev/null
+++ b/web_tree_dynamic_colored_field/demo/res_users.xml
@@ -0,0 +1,24 @@
+
+
+
+ res.users
+
+
+
+ color_field: lang
+
+
+ {
+ "bg_color": "#9e1635: login_date == False",
+ "fg_color": "white: login_date == False"
+ }
+
+
+ {
+ "bg_color": "blue; #653b5b: login == 'admin'",
+ "fg_color": "white"
+ }
+
+
+
+
diff --git a/web_tree_dynamic_colored_field/static/description/icon.png b/web_tree_dynamic_colored_field/static/description/icon.png
new file mode 100644
index 000000000..3a0328b51
Binary files /dev/null and b/web_tree_dynamic_colored_field/static/description/icon.png differ
diff --git a/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js b/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js
new file mode 100644
index 000000000..2a96aba66
--- /dev/null
+++ b/web_tree_dynamic_colored_field/static/src/js/web_tree_dynamic_colored_field.js
@@ -0,0 +1,129 @@
+odoo.define('web_tree_dynamic_colored_field', function (require) {
+ 'use strict';
+
+ var ListRenderer = require('web.ListRenderer');
+ var pyeval = require('web.pyeval');
+
+ ListRenderer.include({
+ /**
+ * Look up for a `color_field` parameter in tree `colors` attribute
+ *
+ * @override
+ */
+ _renderBody: function () {
+ if (this.arch.attrs.colors) {
+ var colorField = this.arch.attrs.colors.split(';')
+ .filter(color => color.trim().startsWith('color_field'))[0]
+ .split(':')[1]
+ .trim();
+ // validate the presence of that field in tree view
+ var fieldNames = _(this.columns).map(
+ (value) => { return value.attrs.name; }
+ );
+ if (fieldNames.indexOf(colorField) === -1) {
+ console.warn(
+ "No field named '" + colorField + "' present in view."
+ );
+ } else {
+ this.colorField = colorField;
+ }
+ }
+ return this._super();
+ },
+ /**
+ * Colorize a cell during it's render
+ *
+ * @override
+ */
+ _renderBodyCell: function (record, node, colIndex, options) {
+ var $td = this._super.apply(this, arguments);
+ var ctx = this.getEvalContext(record);
+ this.applyColorize($td, record, node, ctx);
+ return $td;
+ },
+
+ /**
+ * Colorize the current cell depending on expressions provided.
+ *
+ * @param {Query Node} $td a tag inside a table representing a list view
+ * @param {Object} node an XML node (must be a )
+ */
+ applyColorize: function ($td, record, node, ctx) {
+ // safely resolve value of `color_field` given in
+ var treeColor = record.data[this.colorField];
+ if (treeColor) {
+ $td.css('color', treeColor);
+ }
+ // apply 's own `options`
+ if (!node.attrs.options) { return; }
+ var nodeOptions = JSON.parse(node.attrs.options);
+ this.applyColorizeHelper($td, nodeOptions, node, 'fg_color', 'color', ctx);
+ this.applyColorizeHelper($td, nodeOptions, node, 'bg_color', 'background-color', ctx);
+ },
+ /**
+ * @param {Object} nodeOptions a mapping of nodeOptions parameters to the color itself
+ * @param {Object} node an XML node (must be a )
+ * @param {string} nodeAttribute an attribute of a node to apply a style onto
+ * @param {string} cssAttribute a real CSS-compatible attribute
+ */
+ applyColorizeHelper: function ($td, nodeOptions, node, nodeAttribute, cssAttribute, ctx) {
+ if (nodeOptions[nodeAttribute]) {
+ var colors = _(nodeOptions[nodeAttribute].split(';'))
+ .chain()
+ .map(this.pairColors)
+ .value()
+ .filter(function CheckUndefined(value, index, ar) {
+ return value !== undefined;
+ });
+ for (var i=0, len=colors.length; i: ` forms to
+ * evaluatable expressions
+ *
+ * @param {string} pairColor `color: expression` pair
+ */
+ pairColors: function (pairColor) {
+ if (pairColor !== "") {
+ var pairList = pairColor.split(':'),
+ color = pairList[0],
+ // if one passes a bare color instead of an expression,
+ // then we consider that color is to be shown in any case
+ expression = pairList[1]? pairList[1] : 'True';
+ return [color, py.parse(py.tokenize(expression)), expression];
+ }
+ return undefined;
+ },
+ /**
+ * Construct domain evaluation context, mostly by passing
+ * record's fields's values to local scope.
+ *
+ * @param {Object} record a record to build a context from
+ */
+ getEvalContext: function (record) {
+ var ctx = _.extend(
+ {},
+ record.data,
+ pyeval.context()
+ );
+ for (var key in ctx) {
+ var value = ctx[key];
+ if (ctx[key] instanceof moment) {
+ // date/datetime fields are represented w/ Moment objects
+ // docs: https://momentjs.com/
+ ctx[key] = value.format('YYYY-MM-DD hh:mm:ss');
+ }
+ }
+ return ctx;
+ }
+ });
+});
diff --git a/web_tree_dynamic_colored_field/views/web_tree_dynamic_colored_field.xml b/web_tree_dynamic_colored_field/views/web_tree_dynamic_colored_field.xml
new file mode 100644
index 000000000..ea8e11291
--- /dev/null
+++ b/web_tree_dynamic_colored_field/views/web_tree_dynamic_colored_field.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
|