summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/fields/basic_fields.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/fields/basic_fields.js')
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js224
1 files changed, 224 insertions, 0 deletions
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index 34c2b08e362..2dedec8978d 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -14,10 +14,14 @@ var core = require('web.core');
var crash_manager = require('web.crash_manager');
var datepicker = require('web.datepicker');
var dom = require('web.dom');
+var Domain = require('web.Domain');
+var DomainSelector = require('web.DomainSelector');
+var DomainSelectorDialog = require('web.DomainSelectorDialog');
var field_utils = require('web.field_utils');
var framework = require('web.framework');
var session = require('web.session');
var utils = require('web.utils');
+var view_dialogs = require('web.view_dialogs');
var qweb = core.qweb;
var _t = core._t;
@@ -1189,6 +1193,225 @@ var JournalDashboardGraph = AbstractField.extend({
});
/**
+ * The "Domain" field allows the user to construct a technical-prefix domain
+ * thanks to a tree-like interface and see the selected records in real time.
+ * In debug mode, an input is also there to be able to enter the prefix char
+ * domain directly (or to build advanced domains the tree-like interface does
+ * not allow to).
+ */
+var FieldDomain = AbstractField.extend({
+ /**
+ * The model this field widget works with could be dependant on another
+ * field value. This field widget must then be reset when a field is changed
+ * in the view.
+ */
+ resetOnAnyFieldChange: true,
+
+ events: _.extend({}, AbstractField.prototype.events, {
+ "click .o_domain_show_selection_button": "_onShowSelectionButtonClick",
+ "click .o_form_field_domain_dialog_button": "_onDialogEditButtonClick",
+ }),
+ custom_events: {
+ "domain_changed": "_onDomainSelectorValueChange",
+ "domain_selected": "_onDomainSelectorDialogValueChange",
+ },
+ /**
+ * @constructor
+ * @override init from AbstractField
+ */
+ init: function () {
+ this._super.apply(this, arguments);
+
+ this.inDialog = !!this.nodeOptions.in_dialog;
+ this.modelOption = this.nodeOptions.model;
+ this.fsFilters = this.nodeOptions.fs_filters || {};
+
+ this.className = "o_form_field_domain";
+ if (this.mode === "edit") {
+ this.className += " o_edit_mode";
+ }
+ if (!this.inDialog) {
+ this.className += " o_inline_mode";
+ }
+
+ this._initModel();
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override isValid from AbstractField.isValid
+ * Parsing the char value is not enough for this field. It is considered
+ * valid if the internal domain selector was built correctly and that the
+ * query to the model to test the domain did not fail.
+ *
+ * @returns {boolean}
+ */
+ isValid: function () {
+ return (
+ this._super.apply(this, arguments)
+ && this._isValidForModel
+ );
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @override _render from AbstractField
+ * @returns {Deferred}
+ */
+ _render: function () {
+ // If there is no model, only change the non-domain-selector content
+ if (!this.domainModel) {
+ this._replaceContent();
+ return $.when();
+ }
+
+ // Convert char value to array value
+ var value = this.value || "[]";
+ var domain = Domain.prototype.stringToArray(value);
+
+ // Create the domain selector or change the value of the current one...
+ var def;
+ if (!this.domainSelector) {
+ this.domainSelector = new DomainSelector(this, this.domainModel, domain, {
+ readonly: this.mode === "readonly" || this.inDialog,
+ filters: this.fsFilters,
+ debugMode: session.debug,
+ });
+ def = this.domainSelector.prependTo(this.$el);
+ } else {
+ def = this.domainSelector.setDomain(domain);
+ }
+ // ... then query the model to get the number of matched record if valid
+ var self = this;
+ return def.then(function () {
+ var context = {}; // FIXME ?
+ return self.performModelRPC(self.domainModel, "search_count", [
+ domain,
+ context
+ ]).then(
+ function (data) {
+ self._isValidForModel = true;
+ return data;
+ },
+ function (error, e) {
+ e.preventDefault();
+ self._isValidForModel = false;
+ }
+ ).always(self._replaceContent.bind(self));
+ });
+ },
+ /**
+ * Render the field DOM except for the domain selector part. The full field
+ * DOM is composed of a DIV which contains the domain selector widget,
+ * followed by other content. This other content is handled by this method.
+ *
+ * @private
+ * @param {integer} [nbRecords=0] - The number of records the domain matches
+ */
+ _replaceContent(nbRecords) {
+ if (this._$content) {
+ this._$content.remove();
+ }
+ this._$content = $(qweb.render("FieldDomain.content", {
+ hasModel: !!this.domainModel,
+ isValid: !!this._isValidForModel,
+ nbRecords: nbRecords || 0,
+ inDialogEdit: this.inDialog && this.mode === "edit",
+ }));
+ this._$content.appendTo(this.$el);
+ },
+ /**
+ * @override _reset from AbstractField
+ * Check if the model the field works with has (to be) changed.
+ */
+ _reset: function () {
+ this._super.apply(this, arguments);
+ var oldDomainModel = this.domainModel;
+ this._initModel();
+ if (this.domainSelector && this.domainModel !== oldDomainModel) {
+ // If the model has changed, destroy the current domain selector
+ this.domainSelector.destroy();
+ this.domainSelector = null;
+ }
+ },
+ /**
+ * Checks the model the field must work with. It is either directly given
+ * in the field option or it is the value of another field in the view.
+ *
+ * @private
+ */
+ _initModel: function () {
+ this.domainModel = this.modelOption;
+ if (this.recordData.hasOwnProperty(this.domainModel)) {
+ this.domainModel = this.recordData[this.domainModel];
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called when the "Show selection" button is clicked
+ * -> Open a modal to see the matched records
+ *
+ * @param {Event} e
+ */
+ _onShowSelectionButtonClick: function (e) {
+ e.preventDefault();
+ new view_dialogs.SelectCreateDialog(this, {
+ title: _t("Selected records"),
+ res_model: this.domainModel,
+ domain: this.value || "[]",
+ no_create: true,
+ readonly: true,
+ disable_multiple_selection: true,
+ }).open();
+ },
+ /**
+ * Called when the "Edit domain" button is clicked (when using the in_dialog
+ * option) -> Open a DomainSelectorDialog to edit the value
+ *
+ * @param {Event} e
+ */
+ _onDialogEditButtonClick: function (e) {
+ e.preventDefault();
+ new DomainSelectorDialog(this, this.domainModel, this.value || "[]", {
+ readonly: this.mode === "readonly",
+ filters: this.fsFilters,
+ debugMode: session.debug,
+ }).open();
+ },
+ /**
+ * Called when the domain selector value is changed (do nothing if it is the
+ * one which is in a dialog (@see _onDomainSelectorDialogValueChange))
+ * -> Adapt the internal value state
+ *
+ * @param {OdooEvent} e
+ */
+ _onDomainSelectorValueChange: function (e) {
+ if (this.inDialog) return;
+ this._setValue(Domain.prototype.arrayToString(this.domainSelector.getDomain()));
+ },
+ /**
+ * Called when the in-dialog domain selector value is confirmed
+ * -> Adapt the internal value state
+ *
+ * @param {OdooEvent} e
+ */
+ _onDomainSelectorDialogValueChange: function (e) {
+ this._setValue(Domain.prototype.arrayToString(e.data.domain));
+ },
+});
+
+/**
* This widget is intended to be used on Text fields. It will provide Ace Editor
* for editing XML and Python.
*/
@@ -1275,6 +1498,7 @@ return {
FieldChar: FieldChar,
FieldDate: FieldDate,
FieldDateTime: FieldDateTime,
+ FieldDomain: FieldDomain,
FieldFloat: FieldFloat,
FieldFloatTime: FieldFloatTime,
FieldHtml: FieldHtml,