', {
- 'class': 'col-sm-10 col-sm-offset-2'
- }).appendTo(jQuery('
', {
- 'class': 'form-group'
- }).appendTo(body)));
- this.wid_text = jQuery('
', {
- 'type': 'text',
- 'class': 'form-control input-sm',
- 'placeholder': Sao.i18n.gettext('Search'),
- 'name': attributes.name,
- }).appendTo(group);
-
- if (!attributes.completion || attributes.completion == '1') {
- this.wid_completion = Sao.common.get_completion(
- group,
- this._update_completion.bind(this),
- this._completion_match_selected.bind(this));
- this.wid_text.completion = this.wid_completion;
- }
-
- this.but_add = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Add"),
- 'title': Sao.i18n.gettext("Add"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-add')
- ).appendTo(jQuery('
', {
- 'class': 'input-group-btn'
- }).appendTo(group));
- this.but_add.click(this.add.bind(this));
-
- this._readonly = false;
- this._record_id = null;
- this._popup = false;
- },
- _required_el: function() {
- return this.wid_text;
- },
- _invalid_el: function() {
- return this.wid_text;
- },
- add: function() {
- var context = this.field.get_context(this.record);
- var value = this.wid_text.val();
- var domain = this.field.get_domain(this.record);
-
- if (this._popup) {
- return;
- } else {
- this._popup = true;
- }
-
- const callback = result => {
- if (!jQuery.isEmptyObject(result)) {
- var ids = result.map(function(e) {
- return e[0];
- });
- this.add_new_keys(ids);
- }
- this.wid_text.val('');
- this._popup = false;
- };
-
- var parser = new Sao.common.DomainParser();
- new Sao.Window.Search(this.schema_model,
- callback, {
- sel_multi: true,
- context: context,
- domain: domain,
- new_: false,
- search_filter: parser.quote(value),
- title: this.attributes.string
- });
- },
- add_new_keys: function(ids) {
- var field = this.field;
- field.add_new_keys(ids, this.record)
- .then(new_names => {
- this.send_modified();
- var value = this.field.get_client(this.record);
- for (const key of new_names) {
- value[key] = null;
- }
- this.field.set_client(this.record, value);
- this._display().then(() => {
- this.fields[new_names[0]].input.focus();
- });
- });
- },
- remove: function(key, modified=true) {
- delete this.fields[key];
- this.rows[key].remove();
- delete this.rows[key];
- if (modified) {
- this.send_modified();
- this.set_value(this.record, this.field);
- }
- },
- set_value: function() {
- this.field.set_client(this.record, this.get_value());
- },
- get_value: function() {
- var value = {};
- for (var key in this.fields) {
- var widget = this.fields[key];
- value[key] = widget.get_value();
- }
- return value;
- },
- get modified() {
- if (this.record && this.field) {
- var value = this.field.get_client(this.record);
- for (var key in this.fields) {
- var widget = this.fields[key];
- if (widget.modified(value)) {
- return true;
- }
- }
- }
- return false;
- },
- set_readonly: function(readonly) {
- Sao.View.Form.Dict._super.set_readonly.call(this, readonly);
- this._set_button_sensitive();
- for (var key in this.fields) {
- var widget = this.fields[key];
- widget.set_readonly(readonly);
- }
- this.wid_text.prop('disabled', readonly);
- },
- _set_button_sensitive: function() {
- var create = this.attributes.create;
- if (create === undefined) {
- create = 1;
- } else if (typeof create == 'string') {
- create = Boolean(parseInt(create, 10));
- }
- var delete_ = this.attributes['delete'];
- if (delete_ === undefined) {
- delete_ = 1;
- } else if (typeof delete_ == 'string') {
- delete_ = Boolean(parseInt(delete_, 10));
- }
- this.but_add.prop('disabled', this._readonly || !create);
- for (var key in this.fields) {
- var button = this.fields[key].button;
- button.prop('disabled', this._readonly || !delete_);
- }
- },
- add_line: function(key, position) {
- var field, row;
- var key_schema = this.field.keys[key];
- this.fields[key] = field = new (
- this.get_entries(key_schema.type))(key, this);
- this.rows[key] = row = jQuery('
', {
- 'class': 'form-group'
- });
- var text = key_schema.string + Sao.i18n.gettext(':');
- var label = jQuery('
', {
- 'class': 'dict-label col-sm-2 control-label'
- }).appendTo(row));
-
- field.el.addClass('col-sm-10').appendTo(row);
-
- label.uniqueId();
- field.labelled.uniqueId();
- field.labelled.attr('aria-labelledby', label.attr('id'));
- label.attr('for', field.labelled.attr('id'));
-
- field.button.click(() => {
- this.remove(key, true);
- });
-
- var previous = null;
- if (position > 0) {
- previous = this.container.children().eq(position - 1);
- }
- if (previous) {
- previous.after(row);
- } else {
- this.container.prepend(row);
- }
- },
- display: function() {
- this._display();
- },
- _display: function() {
- Sao.View.Form.Dict._super.display.call(this);
-
- var record = this.record;
- var field = this.field;
- if (!field) {
- return;
- }
-
- var record_id = record ? record.id : null;
- var key;
-
- if (record_id != this._record_id) {
- for (key in this.fields) {
- this.remove(key, false);
- }
- this._record_id = record_id;
- }
-
- var value = field.get_client(record);
- var new_key_names = Object.keys(value).filter(
- e => !this.fields[e]);
-
- var prm;
- if (!jQuery.isEmptyObject(new_key_names)) {
- prm = field.add_keys(new_key_names, record);
- } else {
- prm = jQuery.when();
- }
- prm.then(() => {
- var i, len, key;
- var keys = Object.keys(value)
- .filter(function(key) {
- return field.keys[key];
- })
- .sort(function(key1, key2) {
- var seq1 = field.keys[key1].sequence;
- var seq2 = field.keys[key2].sequence;
- if (seq1 < seq2) {
- return -1;
- } else if (seq1 > seq2) {
- return 1;
- } else {
- return 0;
- }
- });
- // We remove first the old keys in order to keep the order
- // inserting the new ones
- var removed_key_names = Object.keys(this.fields).filter(
- function(e) {
- return !(e in value);
- });
- for (i = 0, len = removed_key_names.length; i < len; i++) {
- key = removed_key_names[i];
- this.remove(key, false);
- }
- var decoder = new Sao.PYSON.Decoder();
- var inversion = new Sao.common.DomainInversion();
- for (i = 0, len = keys.length; i < len; i++) {
- key = keys[i];
- var val = value[key];
- if (!this.fields[key]) {
- this.add_line(key, i);
- }
- var widget = this.fields[key];
- widget.set_value(val);
- widget.set_readonly(this._readonly);
- var key_domain = (decoder.decode(field.keys[key].domain ||
- 'null'));
- if (key_domain !== null) {
- if (!inversion.eval_domain(key_domain, value)) {
- widget.el.addClass('has-error');
- } else {
- widget.el.removeClass('has-error');
- }
- }
- }
- });
- this._set_button_sensitive();
- return prm;
- },
- _update_completion: function(text) {
- if (this.wid_text.prop('disabled')) {
- return jQuery.when();
- }
- if (!this.record) {
- return jQuery.when();
- }
- return Sao.common.update_completion(
- this.wid_text, this.record, this.field, this.schema_model);
- },
- _completion_match_selected: function(value) {
- this.add_new_keys([value.id]);
- this.wid_text.val('');
- },
- get_entries: function(type) {
- switch (type) {
- case 'char':
- return Sao.View.Form.Dict.Entry;
- case 'boolean':
- return Sao.View.Form.Dict.Boolean;
- case 'selection':
- return Sao.View.Form.Dict.Selection;
- case 'multiselection':
- return Sao.View.Form.Dict.MultiSelection;
- case 'integer':
- return Sao.View.Form.Dict.Integer;
- case 'float':
- return Sao.View.Form.Dict.Float;
- case 'numeric':
- return Sao.View.Form.Dict.Numeric;
- case 'date':
- return Sao.View.Form.Dict.Date;
- case 'datetime':
- return Sao.View.Form.Dict.DateTime;
- }
- }
- });
-
- Sao.View.Form.Dict.Entry = Sao.class_(Object, {
- init: function(name, parent_widget) {
- this.name = name;
- this.definition = parent_widget.field.keys[name];
- this.parent_widget = parent_widget;
- this.create_widget();
- if (this.definition.help) {
- this.el.attr('title', this.definition.help);
- }
- },
- create_widget: function() {
- this.el = jQuery('
', {
- 'class': this.class_
- });
- var group = jQuery('
', {
- 'class': 'input-group input-group-sm'
- }).appendTo(this.el);
- this.input = this.labelled = jQuery('
', {
- 'type': 'text',
- 'class': 'form-control input-sm mousetrap',
- 'name': this.name,
- }).appendTo(group);
- this.button = jQuery('
', {
- 'class': 'btn btn-default',
- 'type': 'button',
- 'arial-label': Sao.i18n.gettext("Remove"),
- 'title': Sao.i18n.gettext("Remove"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-remove')
- ).appendTo(jQuery('
', {
- 'class': 'input-group-btn'
- }).appendTo(group));
-
- this.el.on('keydown',
- this.parent_widget.send_modified.bind(this.parent_widget));
- this.el.change(
- this.parent_widget.focus_out.bind(this.parent_widget));
- },
- modified: function(value) {
- return (JSON.stringify(this.get_value()) !=
- JSON.stringify(value[this.name]));
- },
- get_value: function() {
- return this.input.val();
- },
- set_value: function(value) {
- this.input.val(value || '');
- },
- set_readonly: function(readonly) {
- this._readonly = readonly;
- this.input.prop('readonly', readonly);
- }
- });
-
- Sao.View.Form.Dict.Char = Sao.class_(Sao.View.Form.Dict.Entry, {
- class_: 'dict-char',
- modified: function(value) {
- return (JSON.stringify(this.get_value()) !=
- JSON.stringify(value[this.name] || ""));
- }
- });
-
- Sao.View.Form.Dict.Boolean = Sao.class_(Sao.View.Form.Dict.Entry, {
- class_: 'dict-boolean',
- create_widget: function() {
- Sao.View.Form.Dict.Boolean._super.create_widget.call(this);
- this.input.attr('type', 'checkbox');
- this.input.change(
- this.parent_widget.focus_out.bind(this.parent_widget));
- },
- get_value: function() {
- return this.input.prop('checked');
- },
- set_readonly: function(readonly) {
- this._readonly = readonly;
- this.input.prop('disabled', readonly);
- },
- set_value: function(value) {
- this.input.prop('checked', value);
- }
- });
-
- Sao.View.Form.Dict.SelectionEntry = Sao.class_(Sao.View.Form.Dict.Entry, {
- create_widget: function() {
- Sao.View.Form.Dict.SelectionEntry._super.create_widget.call(this);
- var select = jQuery('
', {
- 'class': 'form-control input-sm mousetrap',
- 'name': this.name,
- });
- select.change(
- this.parent_widget.focus_out.bind(this.parent_widget));
- this.input.replaceWith(select);
- this.input = this.labelled = select;
- var selection = jQuery.extend([], this.definition.selection);
- if (this.definition.sort === undefined || this.definition.sort) {
- selection.sort(function(a, b) {
- return a[1].localeCompare(b[1]);
- });
- }
- for (const e of selection) {
- select.append(jQuery('
', {
- 'value': JSON.stringify(e[0]),
- 'text': e[1],
- 'title': this.definition.help_selection[e[0]],
- }));
- }
- },
- set_readonly: function(readonly) {
- this._readonly = readonly;
- this.input.prop('disabled', readonly);
- }
- });
-
- Sao.View.Form.Dict.Selection = Sao.class_(
- Sao.View.Form.Dict.SelectionEntry, {
- class_: 'dict-selection',
- create_widget: function() {
- Sao.View.Form.Dict.Selection._super.create_widget.call(this);
- this.input.prepend(jQuery('
', {
- 'value': JSON.stringify(null),
- 'text': '',
- }));
- },
- get_value: function() {
- return JSON.parse(this.input.val());
- },
- set_value: function(value) {
- this.input.val(JSON.stringify(value));
- var title = this.definition.help_selection[value] || null;
- if (this.definition.help && title) {
- title = this.definition.help + '\n' + title;
- }
- this.input.attr('title', title);
- },
- });
-
- Sao.View.Form.Dict.MultiSelection = Sao.class_(
- Sao.View.Form.Dict.SelectionEntry, {
- class_: 'dict-multiselection',
- create_widget: function() {
- Sao.View.Form.Dict.MultiSelection._super
- .create_widget.call(this);
- this.input.prop('multiple', true);
- var widget_help = this.definition.help;
- if (widget_help) {
- this.input.children().each(function() {
- var option = jQuery(this);
- var help = option.attr('title');
- if (help) {
- help = widget_help + '\n' + help;
- option.attr('title', help);
- }
- });
- }
- },
- get_value: function() {
- var value = this.input.val();
- return value.map(function(e) { return JSON.parse(e); });
- },
- set_value: function(value) {
- if (value) {
- value = value.map(function(e) { return JSON.stringify(e); });
- }
- this.input.val(value);
- }
- });
-
- Sao.View.Form.Dict.Integer = Sao.class_(Sao.View.Form.Dict.Entry, {
- class_: 'dict-integer',
- create_widget: function() {
- Sao.View.Form.Dict.Integer._super.create_widget.call(this);
- this.input_text = this.labelled = integer_input(this.input);
- },
- get_value: function() {
- var value = parseInt(this.input.val(), 10);
- if (isNaN(value)) {
- return null;
- }
- return value;
- },
- set_value: function(value, options) {
- if (value !== null) {
- this.input.val(value);
- this.input_text.val(value.toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()), options));
- } else {
- this.input.val('');
- this.input_text.val('');
- }
- },
- set_readonly: function(readonly) {
- Sao.View.Form.Dict.Integer._super.set_readonly.call(this, readonly);
- this.input_text.prop('readonly', readonly);
- },
- });
-
- Sao.View.Form.Dict.Float = Sao.class_(Sao.View.Form.Dict.Integer, {
- class_: 'dict-float',
- get digits() {
- var record = this.parent_widget.record;
- if (record) {
- var digits = record.expr_eval(this.definition.digits);
- if (!digits || !digits.every(function(e) {
- return e !== null;
- })) {
- return null;
- }
- return digits;
- } else {
- return null;
- }
- },
- get_value: function() {
- var value = this.input.val();
- if (!value && (value !== 0)) {
- return null;
- }
- value = Number(value);
- if (isNaN(value)) {
- return null;
- }
- return value;
- },
- set_value: function(value) {
- var step = 'any',
- options = {};
- var digits = this.digits;
- if (digits) {
- step = Math.pow(10, -digits[1]).toFixed(digits[1]);
- options.minimumFractionDigits = digits[1];
- options.maximumFractionDigits = digits[1];
- }
- this.input.attr('step', step);
- Sao.View.Form.Dict.Float._super.set_value.call(this, value, options);
- },
- });
-
- Sao.View.Form.Dict.Numeric = Sao.class_(Sao.View.Form.Dict.Float, {
- class_: 'dict-numeric',
- get_value: function() {
- var value = this.input.val();
- if (!value && (value !== 0)) {
- return null;
- }
- value = new Sao.Decimal(value);
- if (isNaN(value.valueOf())) {
- return null;
- }
- return value;
- }
- });
-
- Sao.View.Form.Dict.Date = Sao.class_(Sao.View.Form.Dict.Entry, {
- class_: 'dict-date',
- format: '%x',
- _input: 'date',
- _input_format: '%Y-%m-%d',
- _format: Sao.common.format_date,
- _parse: Sao.common.parse_date,
- create_widget: function() {
- Sao.View.Form.Dict.Date._super.create_widget.call(this);
- var group = this.input.parent().find('.input-group-btn');
- this.input_date = jQuery('
', {
- 'type': this._input,
- 'role': 'button',
- 'tabindex': -1,
- });
- this.input_date.click(() => {
- var value = this.get_value();
- value = this._format(this._input_format, value);
- this.input_date.val(value);
- });
- this.input_date.change(() => {
- var value = this.input_date.val();
- if (value) {
- value = this._parse(this._input_format, value);
- value = this._format(this.format, value);
- this.input.val(value).change();
- this.input.focus();
- }
- });
- if (this.input_date[0].type == this._input) {
- var icon = jQuery('
', {
- 'class': 'btn btn-default',
- 'aria-label': Sao.i18n.gettext("Open the calendar"),
- 'title': Sao.i18n.gettext("Open the calendar"),
- }).prependTo(group);
- this.input_date.appendTo(icon);
- Sao.common.ICONFACTORY.get_icon_img('tryton-date')
- .appendTo(icon);
- }
- var mousetrap = new Mousetrap(this.el[0]);
-
- mousetrap.bind('enter', (e, combo) => {
- var value = this._parse(this.format, this.input.val());
- value = this._format(this.format, value);
- this.input.val(value).change();
- });
- mousetrap.bind('=', (e, combo) => {
- e.preventDefault();
- this.input.val(this._format(this.format, moment())).change();
- });
-
- Sao.common.DATE_OPERATORS.forEach(operator => {
- mousetrap.bind(operator[0], (e, combo) => {
- e.preventDefault();
- var date = this.get_value() || Sao.DateTime();
- date.add(operator[1]);
- this.input.val(this._format(this.format, date)).change();
- });
- });
- },
- get_value: function() {
- return this._parse(this.format, this.input.val());
- },
- set_value: function(value) {
- this.input.val(this._format(this.format, value));
- },
- });
-
- Sao.View.Form.Dict.DateTime = Sao.class_(Sao.View.Form.Dict.Date, {
- class_: 'dict-datetime',
- format: '%x %X',
- _input: 'datetime-local',
- _input_format: '%Y-%m-%dT%H:%M:%S',
- _format: Sao.common.format_datetime,
- _parse: Sao.common.parse_datetime,
- });
-
- Sao.View.Form.PYSON = Sao.class_(Sao.View.Form.Char, {
- class_: 'form-pyson',
- init: function(view, attributes) {
- Sao.View.Form.PYSON._super.init.call(this, view, attributes);
- this.encoder = new Sao.PYSON.Encoder({});
- this.decoder = new Sao.PYSON.Decoder({}, true);
- this.el.keyup(this.validate_pyson.bind(this));
- this.icon = jQuery('
', {
- 'class': 'icon form-control-feedback',
- }).appendTo(this.group);
- this.group.addClass('has-feedback');
- },
- display: function() {
- Sao.View.Form.PYSON._super.display.call(this);
- this.validate_pyson();
- },
- get_encoded_value: function() {
- var value = this.input.val();
- if (!value) {
- return value;
- }
- try {
- return this.encoder.encode(eval_pyson(value));
- }
- catch (err) {
- return null;
- }
- },
- set_value: function() {
- // avoid modification because different encoding
- var value = this.get_encoded_value();
- var record = this.record;
- var field = this.field;
- var previous = field.get_client(record);
- if (value && previous && Sao.common.compare(
- value, this.encoder.encode(this.decoder.decode(previous)))) {
- value = previous;
- }
- field.set_client(record, value);
- },
- get_client_value: function() {
- var value = Sao.View.Form.PYSON._super.get_client_value.call(this);
- if (value) {
- value = Sao.PYSON.toString(this.decoder.decode(value));
- }
- return value;
- },
- validate_pyson: function() {
- var icon = 'ok';
- if (this.get_encoded_value() === null) {
- icon = 'error';
- }
- Sao.common.ICONFACTORY.get_icon_url('tryton-' + icon)
- .then(url => {
- this.icon.attr('src', url);
- });
- },
- focus_out: function() {
- this.validate_pyson();
- Sao.View.Form.PYSON._super.focus_out.call(this);
- }
- });
-
- Sao.View.FormXMLViewParser.WIDGETS = {
- 'binary': Sao.View.Form.Binary,
- 'boolean': Sao.View.Form.Boolean,
- 'callto': Sao.View.Form.CallTo,
- 'char': Sao.View.Form.Char,
- 'date': Sao.View.Form.Date,
- 'datetime': Sao.View.Form.DateTime,
- 'dict': Sao.View.Form.Dict,
- 'document': Sao.View.Form.Document,
- 'email': Sao.View.Form.Email,
- 'float': Sao.View.Form.Float,
- 'html': Sao.View.Form.HTML,
- 'image': Sao.View.Form.Image,
- 'integer': Sao.View.Form.Integer,
- 'many2many': Sao.View.Form.Many2Many,
- 'many2one': Sao.View.Form.Many2One,
- 'multiselection': Sao.View.Form.MultiSelection,
- 'numeric': Sao.View.Form.Float,
- 'one2many': Sao.View.Form.One2Many,
- 'one2one': Sao.View.Form.One2One,
- 'password': Sao.View.Form.Password,
- 'progressbar': Sao.View.Form.ProgressBar,
- 'pyson': Sao.View.Form.PYSON,
- 'reference': Sao.View.Form.Reference,
- 'richtext': Sao.View.Form.RichText,
- 'selection': Sao.View.Form.Selection,
- 'sip': Sao.View.Form.SIP,
- 'text': Sao.View.Form.Text,
- 'time': Sao.View.Form.Time,
- 'timedelta': Sao.View.Form.TimeDelta,
- 'timestamp': Sao.View.Form.DateTime,
- 'url': Sao.View.Form.URL,
- };
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- if ('IntersectionObserver' in window) {
- var moreObserver = new IntersectionObserver(function(entries, observer) {
- for (const entry of entries) {
- if (entry.isIntersecting) {
- jQuery(entry.target).trigger('click');
- }
- }
- }, {
- rootMargin: '0px 0px 50px 0px',
- });
- }
-
- Sao.View.TreeXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
- _parse_tree: function(node, attributes) {
- for (const child of node.childNodes) {
- this.parse(child);
- }
- },
- _parse_field: function(node, attributes) {
- var name = attributes.name;
- var ColumnFactory = Sao.View.TreeXMLViewParser.WIDGETS[
- attributes.widget];
- var column = new ColumnFactory(this.view.screen.model, attributes);
- if (!this.view.widgets[name]) {
- this.view.widgets[name] = [];
- }
- column.tree = this.view;
- this.view.widgets[name].push(column);
-
- if ('symbol' in attributes) {
- column.suffixes.push(
- new Sao.View.Tree.Symbol(attributes, 1));
- }
- if (~['url', 'email', 'callto', 'sip'
- ].indexOf(attributes.widget)) {
- column.prefixes.push(
- new Sao.View.Tree.Affix(attributes, attributes.widget));
- }
- if ('icon' in attributes) {
- column.prefixes.push(new Sao.View.Tree.Affix(attributes));
- }
- for (const affix of node.childNodes) {
- let affix_attributes = {};
- for (const attribute of affix.attributes) {
- affix_attributes[attribute.name] = attribute.value;
- }
- if (!affix_attributes.name) {
- affix_attributes.name = name;
- }
- let list;
- if (affix.tagName == 'prefix') {
- list = column.prefixes;
- } else {
- list = column.suffixes;
- }
- list.push(new Sao.View.Tree.Affix(affix_attributes));
- }
- if ('symbol' in attributes) {
- column.prefixes.push(
- new Sao.View.Tree.Symbol(attributes, 0));
- }
- if (!this.view.attributes.sequence &&
- !this.view.children_field &&
- this.field_attrs[name].sortable !== false){
- column.sortable = true;
- }
- this.view.columns.push(column);
-
- if (attributes.optional) {
- this.view.optionals.push(column);
- }
-
- if (parseInt(attributes.sum || '0', 10)) {
- var sum = jQuery('
', {
- 'text': attributes.string,
- });
- var aggregate = jQuery('
', {
- 'class': 'value',
- });
- this.view.sum_widgets.set(column, [sum, aggregate]);
- }
- },
- _parse_button: function(node, attributes) {
- var column = new Sao.View.Tree.ButtonColumn(
- this.view, attributes);
- this.view.columns.push(column);
- }
- });
-
- Sao.View.Tree = Sao.class_(Sao.View, {
- view_type: 'tree',
- xml_parser: Sao.View.TreeXMLViewParser,
- draggable: false,
- display_size: Sao.config.display_size,
- init: function(view_id, screen, xml, children_field) {
- this.children_field = children_field;
- this.optionals = [];
- this.sum_widgets = new Map();
- this.columns = [];
- this.selection_mode = (screen.attributes.selection_mode ||
- Sao.common.SELECTION_MULTIPLE);
- this.el = jQuery('
', {
- 'class': 'tree-container',
- })
- // Prevent Chrome based browser to compute a min-content
- // such that only this table has scrollbar if needed
- .css('display', 'grid');
- this.scrollbar = jQuery('
', {
- 'class': 'scrollbar responsive',
- }).appendTo(this.el));
- this.treeview = jQuery('
', {
- 'class': 'treeview responsive'
- }).appendTo(this.el);
-
- // Synchronize both scrollbars
- this.treeview.scroll(() => {
- this.scrollbar.parent().scrollLeft(this.treeview.scrollLeft());
- });
- this.scrollbar.parent().scroll(() => {
- this.treeview.scrollLeft(this.scrollbar.parent().scrollLeft());
- });
-
- this.expanded = new Set();
-
- Sao.View.Tree._super.init.call(this, view_id, screen, xml);
-
- // Table of records
- this.rows = [];
- this.edited_row = null;
- this.table = jQuery('
', {
- 'class': 'tree table table-hover table-condensed'
- });
- if (this.editable) {
- this.table.addClass('table-bordered');
- }
- this.treeview.append(this.table);
- this.colgroup = jQuery('
', {
- 'class': 'selection-state',
- }).appendTo(this.colgroup);
- if (this.selection_mode == Sao.common.SELECTION_NONE) {
- col.css('width', 0);
- }
- this.thead = jQuery('
', {
- 'class': 'selection-state'
- });
- this.selection = jQuery('
', {
- 'type': 'checkbox',
- });
- this.selection.change(this.selection_changed.bind(this));
- th.append(this.selection);
- tr.append(th);
- this.thead.append(tr);
-
- this.tfoot = null;
- var sum_row;
- if (this.sum_widgets.size) {
- sum_row = jQuery('
');
- this.tfoot.append(sum_row);
- // insert before thead to not hide drop-down from thead
- this.table.prepend(this.tfoot);
- }
-
- if (this.children_field) {
- this.expander = jQuery('
', {
- 'class': 'expander',
- }).append(jQuery('
', {
- 'tabindex': 0,
- 'class': 'icon',
- }));
- this.update_expander('more');
- this.expander.on('click keypress',
- Sao.common.click_press(this.unfold.bind(this)));
- Sao.common.ICONFACTORY.get_icon_url(
- 'tryton-unfold-' + this.expander.action)
- .then(url => {
- this.expander.children().attr('src', url);
- });
- }
-
- for (const column of this.columns) {
- col = jQuery('
', {
- 'class': column.attributes.widget,
- }).appendTo(this.colgroup);
- th = jQuery('
', {
- 'class': column.attributes.widget,
- });
- th.uniqueId();
- var label = jQuery('
')
- .text(column.attributes.string)
- .attr('title', column.attributes.string);
- if (this.editable) {
- if (column.attributes.required) {
- label.addClass('required');
- }
- if (!column.attributes.readonly) {
- label.addClass('editable');
- }
- }
- if (column.attributes.help) {
- label.attr('title', column.attributes.help);
- }
- if (column.sortable) {
- var arrow = jQuery('
', {
- 'class': 'icon',
- });
- label.append(arrow);
- column.arrow = arrow;
- th.click(column, this.sort_model.bind(this));
- label.addClass('sortable');
- }
- tr.append(th.append(label));
- column.header = th;
- column.col = col;
-
- column.footers = [];
- if (this.sum_widgets.size) {
- var total_cell = jQuery('
', {
- 'class': column.class_,
- });
- if (this.sum_widgets.has(column)) {
- var sum_label = this.sum_widgets.get(column)[0];
- var sum_value = this.sum_widgets.get(column)[1];
- total_cell.append(sum_label);
- total_cell.append(sum_value);
- }
- sum_row.append(total_cell);
- column.footers.push(total_cell);
- }
- }
- this.tbody = jQuery('
');
- this.table.append(this.tbody);
-
- this.colgroup.prepend(jQuery('
', {
- 'class': 'tree-menu',
- }));
- this.thead.children().prepend(jQuery('
', {
- 'class': 'tree-menu',
- }));
- if (this.tfoot) {
- this.tfoot.children().prepend(jQuery('
'));
- }
-
- this.set_drag_and_drop();
-
- th = this.thead.find('th').first();
- if (this.optionals.length) {
- th.addClass('optional');
- }
- var menu = jQuery('
', {
- 'class': 'dropdown-menu',
- }).click(function(evt) {
- evt.stopImmediatePropagation();
- });
- var dropdown = jQuery('
', {
- 'data-toggle': 'dropdown',
- 'aria-haspopup': true,
- 'aria-expanded': false,
- 'title': Sao.i18n.gettext("Menu"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-menu'))
- .click(menu, this.tree_menu.bind(this))
- ).append(menu);
- dropdown.on('hide.bs.dropdown', () => {
- this.save_optional(true);
- Sao.common.set_overflow(th, 'hide');
- });
- dropdown.on('show.bs.dropdown', () => {
- Sao.common.set_overflow(th, 'show');
- });
- th.append(dropdown);
- },
- tree_menu: function(evt) {
- const toggle = evt => {
- var column = evt.data;
- column.set_visible(jQuery(evt.delegateTarget).prop('checked'));
- this.save_optional();
- this.display();
- for (const row of this.rows) {
- row.update_visible();
- }
- };
- var menu = evt.data;
- menu.empty();
- for (const optional of this.optionals) {
- menu.append(jQuery('
', {
- 'role': 'presentation',
- }).append(jQuery('
', {
- 'role': 'menuitem',
- }).append(jQuery('
', {
- 'class': 'checkbox',
- }).append(jQuery('
', {
- 'type': 'checkbox',
- 'checked': optional.get_visible(),
- }).change(optional, toggle))
- .append('Â ' + optional.attributes.string))));
- }
- if (this.optionals.length) {
- menu.append(jQuery('
', {
- 'role': 'separator',
- 'class': 'divider hidden-xs',
- }));
- }
- menu.append(jQuery('
', {
- 'role': 'presentation',
- 'class': (
- this.selected_records.length || !navigator.clipboard?
- '' : 'disabled'),
- }).append(jQuery('
', {
- 'role': 'menuitem',
- 'href': '#',
- 'tabindex': -1,
- }).text(' ' + Sao.i18n.gettext("Copy Selected Rows"))
- .prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-copy', {
- 'aria-hidden': 'true',
- }))
- .click(evt => {
- evt.preventDefault();
- this.on_copy();
- menu.dropdown('toggle');
- })));
- },
- save_optional: function(store=true) {
- if (!this.optionals.length) {
- return;
- }
- var fields = {};
- for (const column of this.optionals) {
- fields[column.attributes.name] = !column.get_visible();
- }
- if (store) {
- var tree_optional_model = new Sao.Model(
- 'ir.ui.view_tree_optional');
- tree_optional_model.execute('set_optional', [
- this.view_id, fields], {});
- }
- Sao.Screen.tree_column_optional[this.view_id] = fields;
- },
- reset: function() {
- this.display_size = Sao.config.display_size;
- },
- get editable() {
- return (parseInt(this.attributes.editable || 0, 10) &&
- !this.screen.attributes.readonly);
- },
- get creatable() {
- if (this.editable) {
- if (this.attributes.creatable) {
- return Boolean(parseInt(this.attributes.creatable, 10));
- } else {
- return true;
- }
- } else {
- return false;
- }
- },
- unfold: function() {
- if (!this.expander) {
- return;
- }
- var action, unfold;
- if (this.expander.action == 'more') {
- unfold = function(row) {
- if (row.is_selected() && !row.is_expanded()) {
- row.expand_row();
- }
- row.rows.forEach(unfold);
- };
- action = 'less';
- } else {
- unfold = function(row) {
- if (row.is_selected() && row.is_expanded()) {
- row.collapse_row();
- }
- row.rows.forEach(unfold);
- };
- action = 'more';
- }
- this.rows.forEach(unfold);
- this.update_expander(action);
- },
- update_expander: function(action) {
- if (!this.expander) {
- return;
- }
- if (action) {
- this.expander.action = action;
- }
- Sao.common.ICONFACTORY.get_icon_url(
- 'tryton-unfold-' + this.expander.action)
- .then(url => {
- this.expander.children().attr('src', url);
- });
- if (jQuery.isEmptyObject(this.selected_records)) {
- this.expander.css('visibility', 'hidden');
- } else {
- this.expander.css('visibility', 'visible');
- }
- },
- sort_model: function(e){
- var column = e.data;
- var arrow = column.arrow;
- for (const col of this.columns) {
- if (col.arrow){
- if (col != column && col.arrow.attr('src')) {
- col.arrow.attr('src', '');
- }
- }
- }
- this.screen.order = this.screen.default_order;
- if (arrow.data('order') == 'ASC') {
- arrow.data('order', 'DESC');
- Sao.common.ICONFACTORY.get_icon_url('tryton-arrow-up')
- .then(function(url) {
- arrow.attr('src', url);
- });
- this.screen.order = [[column.attributes.name, 'DESC']];
- } else if (arrow.data('order') == 'DESC') {
- arrow.data('order', '');
- arrow.attr('src', '');
- } else {
- arrow.data('order', 'ASC');
- Sao.common.ICONFACTORY.get_icon_url('tryton-arrow-down')
- .then(function(url) {
- arrow.attr('src', url);
- });
- this.screen.order = [[column.attributes.name, 'ASC']];
- }
- var unsaved_records = [];
- for (const unsaved_record of this.group) {
- if (unsaved_record.id < 0) {
- unsaved_records = unsaved_record.group;
- }
- }
- var search_string = this.screen.screen_container.get_text();
- if ((!jQuery.isEmptyObject(unsaved_records)) ||
- (this.screen.search_count == this.group.length) ||
- (this.group.parent)) {
- this.screen.search_filter(search_string, true).then(
- ids => {
- this.group.sort(function(a, b) {
- a = ids.indexOf(a.id);
- a = a < 0 ? ids.length : a;
- b = ids.indexOf(b.id);
- b = b < 0 ? ids.length : b;
- if (a < b) {
- return -1;
- } else if (a > b) {
- return 1;
- } else {
- return 0;
- }
- });
- this.screen.display();
- });
- } else {
- this.screen.search_filter(search_string);
- }
- },
- update_arrow: function() {
- var order = this.screen.order,
- name = null,
- direction = null,
- icon = '';
- if (order && (order.length == 1)) {
- name = order[0][0];
- direction = order[0][1];
- if (direction) {
- direction = direction.trim().split(' ', 1)[0];
- icon = {
- 'ASC': 'tryton-arrow-down',
- 'DESC': 'tryton-arrow-up',
- }[direction];
- }
- }
- this.columns.forEach(function(col) {
- var arrow = col.arrow;
- if (arrow) {
- if (col.attributes.name != name) {
- arrow.data('order', '');
- arrow.attr('src', '');
- } else {
- arrow.data('order', direction);
- Sao.common.ICONFACTORY.get_icon_url(icon)
- .then(function(url) {
- arrow.attr('src', url);
- });
- }
- }
- });
- },
- on_copy: function() {
- var data = [];
- this.selected_records.forEach((record) => {
- var values = [];
- this.columns.forEach((col) => {
- if (!col.get_visible() || !col.attributes.name) {
- return;
- }
- var text;
- if (!record.is_loaded(col.attributes.name)) {
- try {
- record.load(this.attributes.name, false, false);
- text = col.get_textual_value(record);
- } catch (e) {
- Sao.Logger.error(
- "Error loading " +
- `${this.attributes.name} for ${record}`);
- text = Sao.i18n.gettext('#ERROR');
- }
- } else {
- text = col.get_textual_value(record);
- }
- values.push('"' + String(text).replace('"', '""') + '"');
- });
- data.push(values.join('\t'));
- });
- navigator.clipboard.writeText(data.join('\n'));
- },
- _add_drag_n_drop: function() {
- Sortable.create(this.tbody[0], {
- handle: '.draggable-handle',
- ghostClass: 'dragged-row'
- });
- this.tbody.on('dragstart', this.drag_data_get.bind(this));
- this.tbody.on('drop', this.drag_data_received.bind(this));
- },
- set_drag_and_drop: function() {
- var dnd = false;
- var children, parent_name;
- if (this.children_field) {
- children = this.screen.model.fields[this.children_field];
- if (children) {
- parent_name = children.description.relation_field;
- dnd = Boolean(this.widgets[parent_name]);
- }
- } else if (this.attributes.sequence) {
- dnd = true;
- }
- if (this.screen.readonly) {
- dnd = false;
- }
-
- this.draggable = dnd;
- if (dnd) {
- this.thead.find('th').first().addClass('draggable-handle');
- this._add_drag_n_drop();
- }
- },
- drag_data_get: function(evt) {
- var row_position = 0;
- var row_leaves = [];
- var set_dragged_row = function(row) {
- if (row.el[0] === evt.target) {
- evt.originalEvent.dataTransfer.setData('path', row.path);
- evt.originalEvent.dataTransfer.setData(
- 'position', row_position);
- }
- if (row.rows.length === 0) {
- row_leaves.push(row);
- }
- row_position += 1;
- row.rows.forEach(set_dragged_row.bind(this));
- };
- this.rows.forEach(set_dragged_row.bind(this));
- },
- drag_data_received: function(evt) {
- var dataTransfer = evt.originalEvent.dataTransfer;
- var origin_path = dataTransfer.getData('path').split('.');
- if (origin_path.length === 0) {
- return ;
- }
-
- var row = this;
- while (origin_path.length > 0) {
- row = row.rows[origin_path[0]];
- origin_path = origin_path.slice(1);
- }
- var record = row.record;
-
- var parent_row = null;
- var dest_position;
- if ((evt.ctrlKey || evt.metaKey) && this.children_field) {
- parent_row = this._find_row(row.el.prev());
- dest_position = (parent_row || this).rows.length;
- } else {
- var sibling_row;
- if (evt.shiftKey) {
- sibling_row = this._find_row(row.el.prev());
- if (sibling_row) {
- parent_row = sibling_row.parent_;
- dest_position = (
- (parent_row || this).rows.indexOf(sibling_row) + 1);
- } else {
- parent_row = null;
- dest_position = 0;
- }
- } else {
- sibling_row = this._find_row(row.el.next());
- if (sibling_row) {
- parent_row = sibling_row.parent_;
- dest_position = (
- (parent_row || this).rows.indexOf(sibling_row));
- } else {
- parent_row = null;
- dest_position = this.rows.length;
- }
- }
- }
-
- var current_row = parent_row;
- while (current_row && (current_row != row)) {
- current_row = current_row.parent_;
- }
- if (current_row) {
- // There is a recursion cancel the drop
- // by moving the row at its previous place
- var original_position = dataTransfer.getData('position');
- var successor = jQuery(
- this.tbody.children()[original_position]);
- successor.before(row.el);
- return;
- }
-
- var previous_row = row;
- var move_child = function(child_row) {
- previous_row.el.after(child_row.el);
- previous_row = child_row;
- child_row.rows.forEach(move_child);
- };
- row.rows.forEach(move_child);
-
- var dest_group_prm;
- var origin_group, origin_position;
- origin_group = record.group;
- origin_position = row.group_position;
- if (parent_row) {
- dest_group_prm = parent_row.record.children_group(
- this.children_field);
- } else {
- dest_group_prm = jQuery.Deferred().resolve(this.group);
- }
-
- dest_group_prm.then(dest_group => {
- var origin_rows, dest_rows;
- if (row.parent_) {
- origin_rows = row.parent_.rows;
- } else {
- origin_rows = this.rows;
- }
- if (parent_row) {
- dest_rows = parent_row.rows;
- } else {
- dest_rows = this.rows;
- }
-
- if (origin_group === dest_group) {
- if (origin_position < dest_position) {
- dest_position -= 1;
- }
- origin_group.splice(origin_position, 1);
- origin_group.splice(dest_position, 0, record);
- origin_group.record_modified();
- } else {
- origin_group.remove(record, true, true);
- // Don't remove record from previous group
- // as the new parent will change the parent
- // This prevents concurrency conflict
- origin_group.record_removed.splice(
- origin_group.record_removed.indexOf(record));
- dest_group.add(record, dest_position);
- if (!record.parent_name) {
- record.modified_fields[origin_group.parent_name] = true;
- record._values[origin_group.parent_name] = null;
- } else {
- record.modified_fields[origin_group.parent_name] = true;
- }
- }
- dest_rows.splice(dest_position, 0, row);
- origin_rows.splice(origin_position, 1);
-
- row.parent_ = parent_row;
- row.record.group = dest_group;
- for (const r of dest_rows.slice(dest_position)) {
- r.reset_path();
- }
- for (const r of origin_rows.slice(origin_position)) {
- r.reset_path();
- }
-
- var selected = this.get_selected_paths();
- row.redraw(selected);
- var child_redraw = function(child_row) {
- child_row.redraw(selected);
- child_row.rows.forEach(child_redraw);
- };
- row.rows.forEach(child_redraw);
-
- if (this.attributes.sequence) {
- row.record.group.set_sequence(
- this.attributes.sequence, this.screen.new_position);
- }
- });
- },
- get_fields: function() {
- return Object.keys(this.widgets);
- },
- get_buttons: function() {
- var buttons = [];
- for (const column of this.columns) {
- if (column instanceof Sao.View.Tree.ButtonColumn) {
- buttons.push(column);
- }
- }
- return buttons;
- },
- display: function(selected, expanded) {
- var current_record = this.record;
- if (jQuery.isEmptyObject(selected) && current_record) {
- selected = this.get_selected_paths();
- if (this.selection.prop('checked') &&
- !this.selection.prop('indeterminate')) {
- for (const record of this.screen.group.slice(
- this.rows.length, this.display_size)) {
- selected.push([record.id]);
- }
- } else {
- var current_path = current_record.get_path(this.group);
- current_path = current_path.map(function(e) {
- return e[1];
- });
- if (!Sao.common.contains(selected, current_path)) {
- selected = [current_path];
- }
- }
- }
- expanded = expanded || this.get_expanded_paths();
-
- if (this.selection_mode == Sao.common.SELECTION_MULTIPLE) {
- this.selection.show();
- } else {
- this.selection.hide();
- }
-
- const group_records = (group, root) => {
- var records = [];
- for (const record of group) {
- records.push(record);
- let path = root.concat([record.id]);
- if (Sao.common.contains(expanded, path)) {
- const children = record.field_get_client(
- this.children_field);
- Array.prototype.push.apply(
- records, group_records(children, path));
- }
- }
- return records;
- };
-
- const row_records = rows => {
- var records = [];
- for (const row of rows) {
- records.push(row.record);
- if (row.is_expanded()) {
- Array.prototype.push.apply(
- records, row_records(row.rows));
- }
- }
- return records;
- };
- var min_display_size = Math.min(
- this.group.length, this.display_size);
- if (this.children_field) {
- if (!Sao.common.compare(
- group_records(this.group.slice(0, min_display_size), []),
- row_records(this.rows))) {
- this.construct();
- }
- } else if ((min_display_size > this.rows.length) &&
- Sao.common.compare(
- this.group.slice(0, this.rows.length),
- row_records(this.rows))) {
- this.construct(true);
- } else if ((min_display_size != this.rows.length) ||
- !Sao.common.compare(
- this.group.slice(0, this.rows.length),
- row_records(this.rows))){
- this.construct();
- }
-
- // Set column visibility depending on attributes and domain
- // start at 2 because of checkbox and option headers
- var visible_columns = 2;
- var domain = [];
- if (!jQuery.isEmptyObject(this.screen.domain)) {
- domain.push(this.screen.domain);
- }
- var tab_domain = this.screen.screen_container.get_tab_domain();
- if (!jQuery.isEmptyObject(tab_domain)) {
- domain.push(tab_domain);
- }
- var inversion = new Sao.common.DomainInversion();
- domain = inversion.simplify(domain);
- var decoder = new Sao.PYSON.Decoder(this.screen.context);
- var min_width = [];
- var tree_column_optional = (
- Sao.Screen.tree_column_optional[this.view_id] || {});
- for (const column of this.columns) {
- visible_columns += 1;
- var name = column.attributes.name;
- if (!name) {
- return;
- }
- var optional;
- if (Object.prototype.hasOwnProperty.call(
- tree_column_optional, name)) {
- optional = tree_column_optional[name];
- } else {
- optional = Boolean(parseInt(
- column.attributes.optional || '0', 10));
- }
- var invisible = decoder.decode(
- column.attributes.tree_invisible || '0');
- if (invisible || optional) {
- visible_columns -= 1;
- column.set_visible(false);
- } else if (name === this.screen.exclude_field) {
- visible_columns -= 1;
- column.set_visible(false);
- } else if (name in this.screen.model.fields) {
- const field = this.screen.model.fields[name];
- var inv_domain = inversion.domain_inversion(domain, name);
- if (typeof inv_domain != 'boolean') {
- inv_domain = inversion.simplify(inv_domain);
- }
- var unique = inversion.unique_value(
- inv_domain, field._single_value)[0];
- if (unique && jQuery.isEmptyObject(this.children_field)) {
- visible_columns -= 1;
- column.set_visible(false);
- } else {
- column.set_visible(true);
- }
- }
-
- if (!column.get_visible()) {
- column.col.css('width', 0);
- column.col.hide();
- } else if (!column.col.hasClass('draggable-handle') &&
- !column.col.hasClass('optional') &&
- !column.col.hasClass('selection-state') &&
- !column.col.hasClass('favorite')) {
- var width, c_width;
- if (column.attributes.width) {
- width = c_width = column.attributes.width;
- min_width.push(width + 'px');
- } else {
- width = {
- 'integer': 8,
- 'selection': 9,
- 'reference': 20,
- 'one2many': 5,
- 'many2many': 5,
- 'boolean': 3,
- 'binary': 20,
- }[column.attributes.widget] || 10;
- if (column.attributes.symbol) {
- width += 2;
- }
- var factor = 1;
- if (column.attributes.expand) {
- factor += parseInt(column.attributes.expand, 10);
- }
- c_width = width * 100 * factor + '%';
- min_width.push(width + 'em');
- }
- column.col.css('width', c_width);
- column.col.show();
- }
- }
- if (this.children_field) {
- this.columns.every(column => {
- if (column.col.hasClass('draggable-handle') ||
- column.header.hasClass('invisible')) {
- return true;
- } else {
- if (this.expander.parent()[0] !== column.header[0]) {
- column.header.prepend(this.expander);
- }
- return false;
- }
- });
- }
- this.table.css('min-width', 'calc(' + min_width.join(' + ') + ')');
- this.scrollbar.css('min-width', this.table.css('min-width'));
-
- if (!this.table.hasClass('no-responsive') &
- (this.columns.filter(function(c) {
- return c.get_visible();
- }).length > 1)) {
- this.table.addClass('responsive');
- this.table.addClass('responsive-header');
- } else {
- this.table.removeClass('responsive');
- this.table.removeClass('responsive-header');
- }
-
- this.update_arrow();
- return this.redraw(selected, expanded).then(() => {
- var tbody = this.table.children('tbody');
- if (!tbody.length) {
- this.table.append(this.tbody);
- } else if (tbody !== this.tbody) {
- tbody.replaceWith(this.tbody);
- }
- this.tbody.append(this.rows.filter(function(row) {
- return !row.el.parent().length;
- }).map(function(row) {
- return row.el;
- }));
- this.update_selection(); // update after new rows has been added
- if ((this.display_size < this.group.length) &&
- (!this.tbody.children().last().hasClass('more-row'))) {
- var more_row = jQuery('
', {
- 'class': 'more-row',
- });
- var more_cell = jQuery('
', {
- 'colspan': visible_columns,
- });
- var more_button = jQuery('
', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("More"),
- }).text(Sao.i18n.gettext('More')
- ).click(() => {
- var height = this.table.height();
- this.display_size += Sao.config.display_size;
- this.display();
- height -= this.treeview.height();
- height -= 50;
- if (this.tfoot) {
- height -= this.tfoot.height();
- }
- this.treeview[0].scroll({
- 'top': height,
- });
- });
- more_cell.append(more_button);
- more_row.append(more_cell);
- this.tbody.append(more_row);
- if (moreObserver) {
- moreObserver.observe(more_button[0]);
- }
- }
- }).done(
- Sao.common.debounce(this.update_sum.bind(this), 250));
- },
- construct: function(extend) {
- if (!extend) {
- this.rows = [];
- // The new tbody is added to the DOM
- // after the rows have been rendered
- // to minimize browser reflow
- this.tbody = jQuery('
');
- if (this.draggable) {
- this._add_drag_n_drop();
- }
- this.edited_row = null;
- } else {
- this.tbody.find('tr.more-row').remove();
- }
- // The rows are added to tbody after being rendered
- // to minimize browser reflow
- for (const record of this.group.slice(
- this.rows.length, this.display_size)) {
- var RowBuilder;
- if (this.editable) {
- RowBuilder = Sao.View.Tree.RowEditable;
- } else {
- RowBuilder = Sao.View.Tree.Row;
- }
- this.rows.push(new RowBuilder(this, record, this.rows.length));
- }
- },
- redraw: function(selected, expanded) {
- return redraw_async(this.rows, selected, expanded);
- },
- switch_: function(path) {
- this.screen.row_activate();
- },
- select_changed: function(record) {
- if (this.edited_row) {
- record = this.edited_row.record;
- this.edited_row.set_selection(true);
- }
- this.record = record;
- // TODO update_children
- },
- update_sum: function() {
- for (const [column, sum_widget] of this.sum_widgets) {
- var name = column.attributes.name;
- var selected_records = this.selected_records;
- var aggregate = '-';
- var sum_label = sum_widget[0];
- var sum_value = sum_widget[1];
- var sum_ = null;
- var selected_sum = null;
- var loaded = true;
- var digit = 0;
- var field = this.screen.model.fields[name];
- var i, record;
- var records_ids = selected_records.map(function(record){
- return record.id;
- });
- for (i=0; i < this.group.length; i++) {
- record = this.group[i];
- if (!record.get_loaded([name]) && record.id >=0){
- loaded = false;
- break;
- }
- var value = field.get(record);
- if (value && value.isTimeDelta) {
- value = value.asSeconds();
- }
- if (value !== null){
- if (sum_ === null){
- sum_ = value;
- }else {
- sum_ += value;
- }
- if (~records_ids.indexOf(record.id) ||
- !selected_records){
- if (selected_sum === null){
- selected_sum = value;
- }else {
- selected_sum += value;
- }
- }
- if (field.digits) {
- var fdigits = field.digits(record);
- if (fdigits && digit !== null){
- digit = Math.max(fdigits[1], digit);
- } else {
- digit = null;
- }
- }
- }
- }
- if (loaded) {
- if (field.description.type == 'timedelta'){
- var converter = field.converter(this.group);
- selected_sum = Sao.common.timedelta.format(
- Sao.TimeDelta(null, selected_sum), converter);
- sum_ = Sao.common.timedelta.format(
- Sao.TimeDelta(null, sum_), converter);
- } else if (digit !== null){
- var options = {};
- options.minimumFractionDigits = digit;
- options.maximumFractionDigits = digit;
- selected_sum = (selected_sum || 0).toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()), options);
- sum_ = (sum_ || 0).toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()), options);
- } else {
- selected_sum = (selected_sum || 0).toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()));
- sum_ = (sum_ || 0).toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()));
- }
- aggregate = selected_sum + '\n' + sum_;
- }
- sum_value.text(aggregate);
- sum_value.parent().attr(
- 'title', sum_label.text() + '\n' + sum_value.text());
- }
- },
- get selected_records() {
- if (this.selection_mode == Sao.common.SELECTION_NONE) {
- return [];
- }
- var records = [];
- var add_record = function(row) {
- if (row.is_selected()) {
- records.push(row.record);
- }
- row.rows.forEach(add_record);
- };
- this.rows.forEach(add_record);
- if (this.selection.prop('checked') &&
- !this.selection.prop('indeterminate')) {
- for (const record of this.group.slice(this.rows.length)) {
- records.push(record);
- }
- }
- return records;
- },
- get listed_records() {
- if (!this.children_field) {
- return this.group.slice();
- }
-
- const get_listed_records = start => {
- var records = [];
- var row = this.find_row(start);
- var children_rows = row ? row.rows : this.rows;
- for (var idx = 0, len = this.n_children(row);
- idx < len; idx++) {
- var path = start.concat([idx]);
- row = children_rows[idx];
- if (row) {
- var record = row.record;
- records.push(record);
- if (row.is_expanded()) {
- records = records.concat(get_listed_records(path));
- }
- }
- }
- return records;
- };
- return get_listed_records([]).concat(this.group.slice(this.rows.length));
- },
- get_listed_paths: function() {
- if (!this.children_field) {
- return this.group.map(function(record) {
- return [record.id];
- });
- }
-
- const get_listed_paths = (start, start_path) => {
- var paths = [];
- var row = this.find_row(start);
- var children_rows = row ? row.rows : this.rows;
- for (var idx = 0, len = this.n_children(row);
- idx < len; idx++) {
- var path = start.concat([idx]);
- row = children_rows[idx];
- if (row) {
- var record = row.record;
- var id_path = start_path.concat([record.id]);
- paths.push(id_path);
- if (row.is_expanded()) {
- paths = paths.concat(get_listed_paths(path, id_path));
- }
- }
- }
- return paths;
- };
- return get_listed_paths([], []).concat(
- this.group.slice(this.rows.length).map(function(record) {
- return [record.id];
- }));
- },
- select_records: function(from, to) {
- if (!from && to) {
- from = this.rows[0].record;
- }
- if (from && to) {
- var from_idx = from.get_index_path(this.screen.group);
- var to_idx = to.get_index_path(this.screen.group);
- var max_len = Math.min(from_idx.length, to_idx.length);
- var tmp;
- for (var i=0; i < max_len; i++) {
- if (from_idx[i] > to_idx[i]) {
- tmp = from;
- from = to;
- to = tmp;
- break;
- }
- }
- if (!tmp && (from_idx.length > to_idx.length)) {
- tmp = from;
- from = to;
- to = tmp;
- }
- }
- var value = this.rows[0].record === from;
- var select_record = function(row) {
- var record = row.record;
- if (record === from) {
- value = true;
- }
- row.set_selection(value);
- if (record === to) {
- value = false;
- }
- row.rows.forEach(select_record);
- };
- this.rows.forEach(select_record);
- },
- selection_changed: function() {
- var value = this.selection.prop('checked');
- var set_checked = function(row) {
- row.set_selection(value);
- row.rows.forEach(set_checked);
- };
- this.rows.forEach(set_checked);
- if (value && this.rows[0]) {
- this.select_changed(this.rows[0].record);
- } else {
- this.select_changed(null);
- }
- this.update_expander(value? 'more' : null);
- this.update_sum();
- },
- update_selection: function() {
- this.update_sum();
- var selected_records = this.selected_records;
- this.selection.prop('indeterminate', false);
- if (jQuery.isEmptyObject(selected_records)) {
- this.selection.prop('checked', false);
- } else if (
- this.rows.every((row) => row.is_selected()) &&
- (selected_records.length >= this.tbody.children().length)) {
- this.selection.prop('checked', true);
- } else {
- this.selection.prop('indeterminate', true);
- // Set checked to go first unchecked after first click
- this.selection.prop('checked', true);
- }
- this.update_expander();
- },
- get_selected_paths: function() {
- var selected_paths = [];
- function get_selected(row, path) {
- var i, r, len, r_path;
- for (i = 0, len = row.rows.length; i < len; i++) {
- r = row.rows[i];
- r_path = path.concat([r.record.id]);
- if (r.is_selected()) {
- selected_paths.push(r_path);
- }
- get_selected(r, r_path);
- }
- }
- get_selected(this, []);
- return selected_paths;
- },
- get_expanded_paths: function(
- starting_path=[], starting_id_path=[]) {
- var id_path, id_paths, row, children_rows, path;
- id_paths = [];
- row = this.find_row(starting_path);
- children_rows = row ? row.rows : this.rows;
- for (var path_idx = 0, len = this.n_children(row) ;
- path_idx < len ; path_idx++) {
- path = starting_path.concat([path_idx]);
- row = children_rows[path_idx];
- if (row && row.is_expanded()) {
- id_path = starting_id_path.concat(row.record.id);
- id_paths.push(id_path);
- id_paths = id_paths.concat(this.get_expanded_paths(path,
- id_path));
- }
- }
- return id_paths;
- },
- find_row: function(path) {
- var index;
- var row = null;
- var group = this.rows;
- for (var i=0, len=path.length; i < len; i++) {
- index = path[i];
- if (!group || index >= group.length) {
- return null;
- }
- row = group[index];
- group = row.rows;
- if (!this.children_field) {
- break;
- }
- }
- return row;
- },
- n_children: function(row) {
- if (!row || !this.children_field) {
- return this.rows.length;
- }
- if (row.record.is_loaded(this.children_field)) {
- return row.record.field_get_client(this.children_field).length;
- } else {
- return 0;
- }
- },
- set_cursor: function(new_, reset_view) {
- var path, row, column;
- var td, prm;
-
- if (!this.record) {
- return;
- }
- path = this.record.get_index_path(this.group);
- if (this.rows.length <= path[0]) {
- this.display_size = this.group.length;
- this.display();
- }
- if (path.length > 1) {
- prm = this.rows[path[0]].expand_to_path(
- path.slice(1),
- [this.record.get_path(this.group).map(function(value) {
- return value[1];
- })]);
- }
-
- const focus = () => {
- row = this.find_row(path);
- if (row) {
- column = row.next_column(null, new_);
- if (column !== null) {
- td = row._get_column_td(column);
- if (this.editable && new_) {
- td.trigger('click');
- }
- var child = Sao.common.find_focusable_child(td);
- if (child) {
- child.focus();
- }
- }
- }
- };
-
- if (prm) {
- prm.then(focus);
- } else {
- focus();
- }
- },
- save_row: function() {
- var i, prm, edited_row = this.edited_row;
- if (!this.editable || !this.edited_row) {
- return jQuery.when();
- }
- if (!this.edited_row.record.validate(
- this.get_fields(), false, false, true)) {
- var focused = false;
- var invalid_fields = this.edited_row.record.invalid_fields();
- for (i = 0; i < this.columns.length; i++) {
- var col = this.columns[i];
- if (col.attributes.name in invalid_fields) {
- var td = this.edited_row._get_column_td(i);
- var editable_el = this.edited_row.get_editable_el(td);
- var widget = editable_el.data('widget');
- widget.display(this.edited_row.record, col.field);
- if (!focused) {
- widget.focus();
- focused = true;
- }
- }
- }
- return;
- }
- if (!this.group.parent) {
- prm = this.edited_row.record.save();
- } else if (this.screen.attributes.pre_validate) {
- prm = this.record.pre_validate();
- } else {
- prm = jQuery.when();
- }
- prm.fail(() => {
- if (this.edited_row != edited_row) {
- this.edit_row(null);
- edited_row.set_selection(true);
- edited_row.selection_changed();
- this.edit_row(edited_row);
- }
- });
- return prm;
- },
- edit_row: function(row) {
- if (!this.editable || this.edited_row == row) {
- return;
- }
- if (this.edited_row) {
- this.edited_row.unset_editable();
- }
- if (row) {
- row.set_editable();
- }
- this.edited_row = row;
- },
- _find_row: function(tr) {
- var row = null;
- var find_row = function(r) {
- if (r.el[0] == tr[0]) {
- row = r;
- return;
- }
- r.rows.forEach(find_row);
- };
- this.rows.forEach(find_row);
- return row;
- }
- });
-
- function redraw_async(rows, selected, expanded) {
- var dfd= jQuery.Deferred(),
- i = 0;
- var redraw = function() {
- for (; i < rows.length; i++) {
- var row = rows[i];
- var record = row.record;
- var field_name;
- for (var j=0; j < row.tree.columns.length; j++) {
- var column = row.tree.columns[j];
- if (column.type == 'field') {
- field_name = column.attributes.name;
- break;
- }
- }
- if (field_name && !record.is_loaded(field_name)) {
- // Prefetch the first field to prevent promises in
- // Cell.render
- record.load(field_name, true, false).done(redraw);
- return;
- } else {
- row.redraw(selected, expanded);
- }
- }
- dfd.resolve();
- };
- redraw();
- return dfd.promise();
- }
-
- Sao.View.Tree.Row = Sao.class_(Object, {
- init: function(tree, record, pos, parent) {
- this.tree = tree;
- this.current_column = null;
- this.rows = [];
- this.record = record;
- this.parent_ = parent;
- this.children_field = tree.children_field;
- this.expander = null;
- this._group_position = null;
- this._path = null;
- this._drawed_record = null;
- this.el = jQuery('
');
- this.el.on('click', this.select_row.bind(this));
- this._construct();
- },
- get group_position() {
- if (this._group_position === null) {
- this._group_position = this.record.group.indexOf(this.record);
- }
- return this._group_position;
- },
- get path() {
- if (!this._path) {
- var path;
- if (this.parent_) {
- path = jQuery.extend([], this.parent_.path.split('.'));
- } else {
- path = [];
- }
- path.push(this.group_position);
- this._path = path.join('.');
- }
- return this._path;
- },
- reset_path: function() {
- this._group_position = null;
- this._path = null;
- for (const row of this.rows) {
- row.reset_path();
- }
- },
- is_expanded: function() {
- return this.tree.expanded.has(this);
- },
- get_id_path: function() {
- if (!this.parent_) {
- return [this.record.id];
- }
- return this.parent_.get_id_path().concat([this.record.id]);
- },
- _construct: function() {
- var td;
- this.tree.el.uniqueId();
- td = jQuery('
');
- if (this.tree.draggable) {
- td.addClass('draggable-handle');
- td.append(Sao.common.ICONFACTORY.get_icon_img('tryton-drag'));
- }
- if (this.tree.optionals.length) {
- td.addClass('optional');
- }
- this.el.append(td);
- td = jQuery('
', {
- 'class': 'selection-state',
- }).click(event_ => {
- event_.stopPropagation();
- this.selection.click();
- });
- this.el.append(td);
- this.selection = jQuery('
', {
- 'type': 'checkbox',
- 'name': 'tree-selection-' + this.tree.el.attr('id'),
- });
- this.selection.click(function(event_) {
- event_.stopPropagation();
- });
- this.selection.change(this.selection_changed.bind(this));
- td.append(this.selection);
-
- const on_click = event_ => {
- if (this.expander && !this.is_expanded() &&
- (this.tree.n_children(this) <= Sao.config.limit)) {
- this.toggle_row();
- }
- this.select_column(event_.data.index);
- };
-
- if (this.children_field) {
- this.expander = jQuery('
', {
- 'tabindex': 0,
- 'class': 'icon',
- });
- this.expander.children().html(' ');
- this.expander.on('click keypress',
- Sao.common.click_press(this.toggle_row.bind(this)));
- this.expander.dblclick((evt) => {
- // prevent calling switch_row on td
- evt.preventDefault();
- evt.stopImmediatePropagation();
- });
- }
-
- for (var i = 0; i < this.tree.columns.length; i++) {
- var column = this.tree.columns[i];
- if (column instanceof Sao.View.Tree.ButtonColumn) {
- td = jQuery('
', {
- 'data-title': column.attributes.string +
- Sao.i18n.gettext(': ')
- }).append(jQuery('
', { // For responsive min-height
- 'aria-hidden': true
- }));
- }
- td.attr('headers', column.header.attr('id'));
- td.on('click keypress', {'index': i}, on_click);
- if (!this.tree.editable) {
- td.dblclick(this.switch_row.bind(this));
- } else {
- if (column.attributes.required) {
- td.addClass('required');
- }
- if (!column.attributes.readonly) {
- td.addClass('editable');
- }
- }
- var cell = jQuery('
', {
- 'class': 'cell',
- });
- td.append(cell);
- if (column.prefixes) {
- for (let j = 0; j < column.prefixes.length; j++) {
- cell.append(jQuery('
', {
- 'class': 'prefix'
- }));
- }
- }
- cell.append(jQuery('
', {
- 'class': 'widget'
- }));
- if (column.suffixes) {
- for (let j = 0; j < column.suffixes.length; j++) {
- cell.append(jQuery('
', {
- 'class': 'suffix'
- }));
- }
- }
-
- this.el.append(td);
- }
- },
- _get_column_td: function(column_index, row) {
- row = row || this.el;
- // take into account the selection and option column
- return jQuery(row.children()[column_index + 2]);
- },
- redraw: function(selected, expanded) {
- selected = selected || [];
- expanded = expanded || [];
-
- switch(this.tree.selection_mode) {
- case Sao.common.SELECTION_NONE:
- this.selection.hide();
- break;
- case Sao.common.SELECTION_SINGLE:
- this.selection.attr('type', 'radio');
- this.selection.show();
- break;
- case Sao.common.SELECTION_MULTIPLE:
- this.selection.attr('type', 'checkbox');
- this.selection.show();
- break;
- }
-
- function apply_visual(el, visual) {
- for (const name of ['muted', 'success', 'warning', 'danger']) {
- var klass = name == 'muted' ? 'text-muted' : name;
- if (name == visual) {
- el.addClass(klass);
- } else {
- el.removeClass(klass);
- }
- }
- }
-
- if (this._drawed_record !== this.record.identity) {
- for (var i = 0; i < this.tree.columns.length; i++) {
- var column = this.tree.columns[i];
- var td = this._get_column_td(i);
- var cell = td.find('.cell');
- var item;
- if (column.prefixes) {
- for (var j = 0; j < column.prefixes.length; j++) {
- var prefix = column.prefixes[j];
- var prefix_el = jQuery(cell.children('.prefix')[j]);
- item = prefix_el.children();
- if (item.length) {
- prefix.render(this.record, item);
- } else {
- prefix_el.empty().append(prefix.render(this.record));
- }
- }
- }
- var widget = cell.children('.widget');
- item = widget.children();
- if (item.length) {
- column.render(this.record, item);
- } else {
- widget.empty().append(column.render(this.record));
- }
- if (column.suffixes) {
- for (var k = 0; k < column.suffixes.length; k++) {
- var suffix = column.suffixes[k];
- var suffix_el = jQuery(cell.children('.suffix')[k]);
- item = suffix_el.children();
- if (item.length) {
- suffix.render(this.record, item);
- } else {
- suffix_el.empty().append(suffix.render(this.record));
- }
- }
- }
- let visual = this.record.expr_eval(column.attributes.visual);
- if (this.record.deleted || this.record.removed) {
- visual = 'muted';
- }
- apply_visual(td, visual);
- }
- this.update_visible();
- }
- if (this.children_field) {
- this.tree.columns.every((column, i) => {
- if (column.col.hasClass('draggable-handle') ||
- column.header.hasClass('invisible')) {
- return true;
- } else {
- var td = this._get_column_td(i);
- var cell = td.find('.cell');
- if (this.expander.parent()[0] !== cell[0]) {
- cell.prepend(this.expander);
- }
- return false;
- }
- });
- }
- this._drawed_record = this.record.identity;
-
- var row_id_path = this.get_id_path();
- this.set_selection(Sao.common.contains(selected, row_id_path));
- if (this.children_field) {
- var depth = this.path.split('.').length;
- var margin = 'margin-left';
- if (Sao.i18n.rtl) {
- margin = 'margin-right';
- }
- this.expander.children().css(margin, (depth - 1) + 'em');
-
- const update_expander = () => {
- var length = this.record.field_get_client(
- this.children_field).length;
- if (length && (
- this.is_expanded() ||
- Sao.common.contains(expanded, row_id_path))) {
- this.expander.css('visibility', 'visible');
- this.tree.expanded.add(this);
- this.expand_children(selected, expanded);
- this.update_expander(true);
- } else {
- this.expander.css('visibility',
- length ? 'visible' : 'hidden');
- this.update_expander(false);
- }
- };
- if (!this.record.is_loaded(this.children_field)) {
- this.record.load(this.children_field, true, false)
- .done(update_expander);
- } else {
- update_expander();
- }
- }
- let visual = this.record.expr_eval(this.tree.attributes.visual);
- if (this.record.deleted || this.record.removed) {
- if (this.record.deleted) {
- this.el.css('text-decoration', 'line-through');
- }
- visual = 'muted';
- } else {
- this.el.css('text-decoration', 'inherit');
- }
- apply_visual(this.el, visual);
- },
- update_visible: function() {
- var thead_visible = this.tree.thead.is(':visible');
- for (var i = 0; i < this.tree.columns.length; i++) {
- var column = this.tree.columns[i];
- var td = this._get_column_td(i);
- if ((column.header.is(':hidden') && thead_visible) ||
- column.header.css('display') == 'none') {
- td.hide();
- td.addClass('invisible');
- } else {
- td.show();
- td.removeClass('invisible');
- }
- }
- },
- toggle_row: function() {
- if (this.is_expanded()) {
- this.collapse_row();
- } else {
- this.expand_row();
- }
- return false;
- },
- expand_row: function() {
- if (this.tree.n_children(this) > Sao.config.limit) {
- this.tree.record = this.record;
- this.tree.screen.switch_view('form');
- } else {
- this.update_expander(true);
- this.tree.expanded.add(this);
- this.expand_children();
- }
- },
- collapse_row: function() {
- for (const row of this.rows) {
- if (row.exception) {
- row.record.cancel();
- }
- }
- this.update_expander(false);
- this.tree.expanded.delete(this);
- this.collapse_children();
- },
- update_expander: function(expanded) {
- var icon;
- if (expanded) {
- icon = 'tryton-arrow-down';
- } else {
- icon = 'tryton-arrow-right';
- }
- Sao.common.ICONFACTORY.get_icon_url(icon)
- .then(url => {
- this.expander.children().attr('src', url);
- });
- },
- collapse_children: function() {
- for (const row of this.rows) {
- row.collapse_children();
- row.el.remove();
- }
- this.rows = [];
- },
- expand_children: function(selected, expanded) {
- return this.record.load(
- this.children_field, true, false).done(() => {
- if (this.rows.length === 0) {
- var children = this.record.field_get_client(
- this.children_field);
- children.forEach((record, pos, group) => {
- // The rows are added to the tbody after being rendered
- // to minimize browser reflow
- this.rows.push(new this.Class(
- this.tree, record, pos, this));
- });
- }
- redraw_async(this.rows, selected, expanded).then(() => {
- this.el.after(this.rows.filter(function(row) {
- return !row.el.parent().length;
- }).map(function(row) {
- return row.el;
- }));
- this.tree.update_selection();
- });
- });
- },
- switch_row: function() {
- if (window.getSelection) {
- if (window.getSelection().empty) { // Chrome
- window.getSelection().empty();
- } else if (window.getSelection().removeAllRanges) { // Firefox
- window.getSelection().removeAllRanges();
- }
- } else if (document.selection) { // IE?
- document.selection.empty();
- }
- if (this.tree.selection_mode != Sao.common.SELECTION_NONE) {
- this.set_selection(true);
- this.selection_changed();
- if (!this.is_selected()) {
- return;
- }
- }
- this.tree.switch_(this.path);
- },
- select_column: function(index) {
- },
- select_row: function(event_) {
- if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
- this.tree.select_changed(this.record);
- this.switch_row();
- } else {
- var current_record;
- if (event_.shiftKey &&
- this.tree.selection_mode != Sao.common.SELECTION_SINGLE) {
- current_record = this.tree.screen.current_record;
- this.tree.select_records(current_record, this.record);
- } else {
- if (!(event_.ctrlKey || event_.metaKey) ||
- this.tree.selection_mode ==
- Sao.common.SELECTION_SINGLE) {
- this.tree.select_records(null, null);
- }
- this.set_selection(!this.is_selected());
- }
- this.selection_changed();
- if (current_record) {
- // Keep original current record with shift select
- this.tree.screen.current_record = current_record;
- }
- }
- },
- is_selected: function() {
- if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
- return false;
- }
- return this.selection.prop('checked');
- },
- set_selection: function(value) {
- if (this.tree.selection_mode == Sao.common.SELECTION_NONE) {
- return;
- }
- this.selection.prop('checked', value);
- if (value) {
- this.el.addClass('selected');
- } else {
- this.el.removeClass('selected');
- }
- if (!value) {
- this.tree.selection.prop('checked', false);
- }
- },
- selection_changed: function() {
- var is_selected = this.is_selected();
- this.set_selection(is_selected);
- if (is_selected) {
- this.tree.select_changed(this.record);
- if (this.expander) {
- if (this.is_expanded()) {
- this.tree.update_expander('less');
- } else if (this.expander.css('visibility') == 'visible') {
- this.tree.update_expander('more');
- }
- }
- } else {
- this.tree.select_changed(
- this.tree.selected_records[0] || null);
- }
- this.tree.update_selection();
- },
- expand_to_path: function(path, selected) {
- if (path.length &&
- this.record.field_get_client(this.children_field).length) {
- this.expander.css('visibility', 'visible');
- this.tree.expanded.add(this);
- this.update_expander(true);
- return this.expand_children(selected).done(() => {
- return this.rows[path[0]].expand_to_path(path.slice(1), selected);
- });
- }
- },
- next_column: function(path, editable, sign) {
- var i, readonly, invisible;
- var column, column_index, state_attrs;
-
- sign = sign || 1;
- if ((path === null) && (sign > 0)) {
- path = -1;
- } else if (path === null) {
- path = 0;
- }
- column_index = 0;
- for (i = 0; i < this.tree.columns.length; i++) {
- column_index = ((path + (sign * (i + 1))) %
- this.tree.columns.length);
- // javascript modulo returns negative number for negative
- // numbers
- if (column_index < 0) {
- column_index += this.tree.columns.length;
- }
- column = this.tree.columns[column_index];
- if (!column.field) {
- continue;
- }
- state_attrs = column.field.get_state_attrs(this.record);
- invisible = state_attrs.invisible;
- if (column.header.is(':hidden')) {
- invisible = true;
- }
- if (editable) {
- var EditableBuilder = Sao.View.EditableTree.WIDGETS[
- column.attributes.widget];
- readonly = (column.attributes.readonly ||
- state_attrs.readonly ||
- !EditableBuilder);
- } else {
- readonly = false;
- }
- if (!(invisible || readonly)) {
- return column_index;
- }
- }
- }
- });
-
- Sao.View.Tree.RowEditable = Sao.class_(Sao.View.Tree.Row, {
- init: function(tree, record, pos, parent) {
- Sao.View.Tree.RowEditable._super.init.call(this, tree, record, pos,
- parent);
- this.edited_column = null;
- this.el.on('keypress', event_ => {
- if ((event_.which == Sao.common.RETURN_KEYCODE) &&
- (this.tree.edited_row != this)) {
- event_.preventDefault();
- this.tree.edit_row(this);
- }
- });
- },
- redraw: function(selected, expanded) {
- var i, cell, widget;
-
- Sao.View.Tree.RowEditable._super.redraw.call(this, selected,
- expanded);
- const display_callback = widget => {
- var record = this.record;
- return function() {
- var field = record.model.fields[widget.field_name];
- field.set_state(record);
- widget.display(record, field);
- };
- };
- // The autocompletion widget do not call display thus we have to
- // call it when redrawing the row
- for (i = 0; i < this.tree.columns.length; i++) {
- var column = this.tree.columns[i];
- cell = this._get_column_td(i).children('.cell');
- widget = jQuery(cell.children('.widget-editable')).data('widget');
- if (widget) {
- var callback = display_callback(widget);
- if (!this.record.is_loaded(column.attributes.name)) {
- this.record.load(column.attributes.name, true, false)
- .done(callback);
- } else {
- callback();
- }
- }
- }
- },
- select_column: function(index) {
- this.edited_column = index;
- },
- select_row: function(event_) {
- var body, listener;
- event_.stopPropagation();
- if (this.tree.edited_row &&
- (event_.currentTarget == this.tree.edited_row.el[0])) {
- return;
- }
-
- var current_record = this.tree.screen.current_record;
- if ((this.record != current_record) &&
- current_record && !current_record.validate(
- this.tree.get_fields(), false, false, true)) {
- return;
- }
-
- body = listener = jQuery(document.body);
- if (body.hasClass('modal-open')) {
- listener = this.tree.el.parents('.modal').last();
- }
- const handler = event_ => {
- if ((event_.currentTarget == body[0]) &&
- body.hasClass('modal-open')) {
- return;
- }
-
- if (!this.tree.save_row()) {
- event_.preventDefault();
- event_.stopPropagation();
- return;
- }
- body.off('click.sao.editabletree');
- this.tree.edit_row(null);
- return true;
- };
- if (!handler(event_)) {
- return;
- }
- listener.on('click.sao.editabletree', handler);
-
- Sao.View.Tree.RowEditable._super.select_row.call(this, event_);
-
- if (!event_.shiftKey && !(event_.ctrlKey || event_.metaKey) &&
- (this.record === current_record)) {
- this.tree.edit_row(this);
- }
- },
- unset_editable: function() {
- this.tree.columns.forEach((col, idx) => {
- var td = this._get_column_td(idx);
- var static_el = this.get_static_el(td);
- static_el.empty().append(col.render(this.record)).show();
- this.get_editable_el(td)
- .empty()
- .data('widget', null)
- .hide()
- .parents('.treeview td').addBack().removeClass('edited');
- });
- },
- set_editable: function() {
- var focus_widget = null;
- for (var i = 0, len=this.tree.columns.length; i < len; i++) {
- var td = this._get_column_td(i);
- var col = this.tree.columns[i];
- if (!col.field) {
- continue;
- }
- var EditableBuilder = Sao.View.EditableTree.WIDGETS[
- col.attributes.widget];
- if (!col.attributes.readonly && EditableBuilder) {
- var widget = new EditableBuilder(
- this.tree, col.attributes);
- widget.el.on('keydown', this.key_press.bind(this));
-
- var editable_el = this.get_editable_el(td);
- editable_el.append(widget.el);
- editable_el.data('widget', widget);
- widget.display(this.record, col.field);
-
- var static_el = this.get_static_el(td);
- static_el.hide();
- editable_el.show();
- editable_el.parents('.treeview td').addBack()
- .addClass('edited');
-
- if (this.edited_column == i) {
- focus_widget = widget;
- }
- }
- }
- if (focus_widget) {
- focus_widget.focus();
- }
- },
- get_static_el: function(td) {
- td = td || this.get_active_td();
- return td.find('.widget');
- },
- get_editable_el: function(td) {
- td = td || this.get_active_td();
- var editable = td.find('.widget-editable');
- if (!editable.length) {
- editable = jQuery('
', {
- 'class': 'widget-editable'
- }).insertAfter(td.find('.widget'));
- }
- return editable;
- },
- get_active_td: function() {
- return this._get_column_td(this.edited_column);
- },
- key_press: function(event_) {
- var next_column, next_idx, i;
-
- if (((event_.which != Sao.common.TAB_KEYCODE) &&
- (event_.which != Sao.common.UP_KEYCODE) &&
- (event_.which != Sao.common.DOWN_KEYCODE) &&
- (event_.which != Sao.common.ESC_KEYCODE) &&
- (event_.which != Sao.common.RETURN_KEYCODE)) ||
- jQuery(event_.currentTarget)
- .find('.dropdown-menu:visible').length) {
- return;
- }
- event_.preventDefault();
- var td = this._get_column_td(this.edited_column);
- var editable_el = this.get_editable_el(td);
- var widget = editable_el.data('widget');
- widget.focus_out();
- var column = this.tree.columns[this.edited_column];
- if (column.field.validate(this.record)) {
- if (event_.which == Sao.common.TAB_KEYCODE) {
- var sign = 1;
- if (event_.shiftKey) {
- sign = -1;
- }
- next_idx = this.next_column(this.edited_column, true, sign);
- if (next_idx !== null) {
- this.edited_column = next_idx;
- td = this._get_column_td(next_idx);
- editable_el = this.get_editable_el(td);
- widget = editable_el.data('widget');
- widget.focus();
- }
- } else if (event_.which == Sao.common.UP_KEYCODE ||
- event_.which == Sao.common.DOWN_KEYCODE ||
- event_.which == Sao.common.RETURN_KEYCODE) {
- next_column = this.edited_column;
- this.record.validate(this.tree.get_fields())
- .then(validate => {
- if (!validate) {
- var invalid_fields =
- this.record.invalid_fields();
- for (i = 0; i < this.tree.columns.length; i++) {
- var col = this.tree.columns[i];
- if (col.attributes.name in invalid_fields) {
- next_column = i;
- break;
- }
- }
- this._get_column_td(next_column)
- .find(':input,[tabindex=0]').focus();
- } else {
- var prm = jQuery.when();
- if (!this.tree.screen.group.parent) {
- prm = this.record.save();
- } else if (this.tree.screen.attributes.pre_validate) {
- prm = this.record.pre_validate();
- }
- prm.fail(function() {
- widget.focus();
- });
- var next_row;
- if (event_.which == Sao.common.UP_KEYCODE) {
- next_row = this.el.prev('tr');
- } else if (event_.which == Sao.common.DOWN_KEYCODE) {
- next_row = this.el.next('tr');
- } else {
- if (this.tree.screen.new_position == -1) {
- next_row = this.el.next('tr');
- } else {
- next_row = this.el.prev('tr');
- }
- }
- if (!next_row.length &&
- ((event_.which == Sao.common.RETURN_KEYCODE) ||
- ((event_.which == Sao.common.UP_KEYCODE) &&
- (this.tree.screen.new_position == 0)) ||
- ((event_.which == Sao.common.DOWN_KEYCODE) &&
- (this.tree.screen.new_position == -1)))) {
- var model = this.tree.screen.group;
- var access = Sao.common.MODELACCESS.get(
- this.tree.screen.model_name);
- var limit = ((this.tree.screen.size_limit !== null) &&
- (model.length >= this.tree.screen.size_limit));
- if (this.tree.creatable && access.create && !limit) {
- prm.then(() => this.tree.screen.new_())
- .then(record => {
- var sequence = this.tree.attributes.sequence;
- if (sequence) {
- record.group.set_sequence(
- sequence, this.tree.screen.new_position);
- }
- });
- }
- } else {
- prm.then(() => {
- this._get_column_td(
- next_column, next_row)
- .trigger('click')
- .find(':input,[tabindex=0]')
- .focus();
- });
- }
- }
- });
- } else if (event_.which == Sao.common.ESC_KEYCODE) {
- this.tree.edit_row(null);
- this.get_static_el().show().find('[tabindex=0]').focus();
- }
- } else {
- widget.display(this.record, column.field);
- }
- }
- });
-
- Sao.View.Tree.Affix = Sao.class_(Object, {
- init: function(attributes, protocol) {
- this.attributes = attributes;
- this.protocol = protocol || null;
- this.icon = attributes.icon;
- if (this.protocol && !this.icon) {
- this.icon = 'tryton-public';
- }
- },
- get_cell: function() {
- var cell;
- if (this.protocol) {
- cell = jQuery('
', {
- 'target': '_blank',
- 'rel': 'noreferrer noopener',
- });
- cell.append(jQuery('
![]()
'));
- cell.click({'cell': cell}, this.clicked.bind(this));
- } else if (this.icon) {
- cell = jQuery('
![]()
');
- } else {
- cell = jQuery('
');
- cell.attr('tabindex', 0);
- }
- cell.addClass('column-affix');
- return cell;
- },
- render: function(record, cell) {
- if (!cell) {
- cell = this.get_cell();
- }
- const render = () => {
- var value;
- var field = record.model.fields[this.attributes.name];
- field.set_state(record, ['invisible']);
- var invisible = field.get_state_attrs(record).invisible;
- if (invisible) {
- cell.hide();
- } else {
- cell.show();
- }
- if (this.protocol) {
- value = field.get(record);
- if (!jQuery.isEmptyObject(value)) {
- switch (this.protocol) {
- case 'email':
- value = 'mailto:' + value;
- break;
- case 'callto':
- value = 'callto:' + value;
- break;
- case 'sip':
- value = 'sip:' + value;
- break;
- }
- }
- cell.attr('href', value);
- }
- if (this.icon) {
- if (this.icon in record.model.fields) {
- var icon_field = record.model.fields[this.icon];
- value = icon_field.get_client(record);
- }
- else {
- value = this.icon;
- }
- var img_tag;
- if (cell.children('img').length) {
- img_tag = cell.children('img');
- } else {
- img_tag = cell;
- }
- if (this.attributes.icon_type == 'url') {
- if (value) {
- if (this.attributes.url_size) {
- var url = new URL(value, window.location);
- url.searchParams.set(
- this.attributes.url_size, 20);
- value = url.href;
- }
- img_tag.attr('src', value);
- } else {
- img_tag.removeAttr('src');
- }
- } else {
- Sao.common.ICONFACTORY.get_icon_url(value)
- .done(url => {
- if (url) {
- img_tag.attr('src', url);
- } else {
- img_tag.removeAttr('src');
- }
- });
- }
- switch (this.attributes.border) {
- case 'rounded':
- img_tag.addClass('img-rounded');
- break;
- case 'circle':
- img_tag.addClass('img-circle');
- break;
- default:
- break;
- }
- } else {
- value = this.attributes.string || '';
- if (!value) {
- value = field.get_client(record) || '';
- }
- cell.text(value);
- }
- };
- if (!record.is_loaded(this.attributes.name)) {
- record.load(this.attributes.name, true, false).done(render);
- } else {
- render();
- }
- return cell;
- },
- clicked: function(event) {
- event.stopPropagation(); // prevent edition
- }
- });
-
- Sao.View.Tree.Symbol = Sao.class_(Object, {
- class_: 'column-symbol',
- init: function(attributes, position) {
- this.attributes = attributes;
- this.position = position;
- },
- get_cell: function() {
- var cell = jQuery('
', {
- 'class': this.class_,
- 'tabindex': 0
- });
- return cell;
- },
- render: function(record, cell) {
- if (!cell) {
- cell = this.get_cell();
- }
- const render = () => {
- var field = record.model.fields[this.attributes.name];
- field.set_state(record, ['invisible']);
- var invisible = field.get_state_attrs(record).invisible;
- if (invisible) {
- cell.text('');
- cell.hide();
- return;
- }
- var result = field.get_symbol(record, this.attributes.symbol);
- var symbol = result[0],
- position = result[1];
- if (Math.round(position) === this.position) {
- cell.text(symbol);
- cell.show();
- } else {
- cell.text('');
- cell.hide();
- }
- };
- if (!record.is_loaded(this.attributes.name)) {
- record.load(this.attributes.name, true, false).done(render);
- } else {
- render();
- }
- return cell;
- },
- });
-
- Sao.View.Tree.CharColumn = Sao.class_(Object, {
- class_: 'column-char',
- init: function(model, attributes) {
- this.type = 'field';
- this.model = model;
- this.field = model.fields[attributes.name];
- this.tree = null;
- this.attributes = attributes;
- this.prefixes = [];
- this.suffixes = [];
- this.header = null;
- this.footers = [];
- },
- get field_name() {
- return this.attributes.name;
- },
- get model_name() {
- return this.model.name;
- },
- get_cell: function() {
- var cell = jQuery('
', {
- 'class': this.class_,
- 'tabindex': 0
- });
- return cell;
- },
- get_textual_value: function(record) {
- return this.field.get_client(record);
- },
- update_text: function(cell, record) {
- var text = this.get_textual_value(record);
- cell.text(text).attr('title', text);
- },
- render: function(record, cell) {
- if (!cell) {
- cell = this.get_cell();
- }
- const render = () => {
- this.update_text(cell, record);
- this.field.set_state(record);
- var state_attrs = this.field.get_state_attrs(record);
- if (state_attrs.invisible) {
- cell.hide();
- } else {
- cell.show();
- }
- };
- const render_error = () => {
- var text = Sao.i18n.gettext('#ERROR');
- cell.text(text).attr('title', text);
- };
-
- if (!record.is_loaded(this.attributes.name)) {
- record.load(this.attributes.name, true, false)
- .done(render)
- .fail(render_error);
- } else {
- if (record.exception) {
- render_error();
- } else {
- render();
- }
- }
- return cell;
- },
- set_visible: function(visible) {
- var cells = this.footers.slice();
- cells.push(this.header);
- for (const cell of cells) {
- if (visible) {
- cell.show();
- cell.removeClass('invisible');
- } else {
- cell.hide();
- cell.addClass('invisible');
- }
- }
- },
- get_visible: function() {
- return !this.header.hasClass('invisible');
- },
- });
-
- Sao.View.Tree.TextColum = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-text'
- });
-
- Sao.View.Tree.IntegerColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-integer',
- init: function(model, attributes) {
- Sao.View.Tree.IntegerColumn._super.init.call(this, model, attributes);
- this.factor = Number(attributes.factor || 1);
- this.grouping = Boolean(Number(attributes.grouping || 1));
- },
- get_cell: function() {
- return Sao.View.Tree.IntegerColumn._super.get_cell.call(this);
- },
- get_textual_value: function(record) {
- return this.field.get_client(record, this.factor, this.grouping);
- },
- });
-
- Sao.View.Tree.FloatColumn = Sao.class_(Sao.View.Tree.IntegerColumn, {
- class_: 'column-float'
- });
-
- Sao.View.Tree.BooleanColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-boolean',
- get_cell: function() {
- return jQuery('
', {
- 'type': 'checkbox',
- 'class': this.class_,
- 'tabindex': 0
- });
- },
- update_text: function(cell, record) {
- cell.prop('checked', this.field.get(record));
- },
- render: function(record, cell) {
- var new_cell = !cell;
- cell = Sao.View.Tree.BooleanColumn._super.render.call(
- this, record, cell);
- var disabled = true;
- if (this.tree.editable) {
- if (new_cell) {
- cell.on('click', null,
- {record: record, cell:cell},
- this.clicked.bind(this));
- }
- var state_attrs = this.field.get_state_attrs(record);
- disabled = this.attributes.readonly || state_attrs.readonly;
- }
- cell.prop('disabled', disabled);
- return cell;
- },
- clicked: function(evt) {
- var record = evt.data.record;
- var cell = evt.data.cell;
- var current_record = this.tree.screen.current_record;
- var fields = this.tree.get_fields();
- if (!current_record || current_record.validate(
- fields, false, false, true)) {
- var value = cell.prop('checked');
- this.field.set_client(record, value);
- } else {
- evt.preventDefault();
- }
- }
- });
-
- Sao.View.Tree.Many2OneColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-many2one',
- get_cell: function() {
- var cell = Sao.View.Tree.Many2OneColumn._super.get_cell.call(this);
- cell.append(jQuery('
', {
- 'href': '#',
- }));
- return cell;
- },
- update_text: function(cell, record) {
- cell = cell.children('a');
- cell.unbind('click');
- Sao.View.Tree.Many2OneColumn._super.update_text.call(this, cell, record);
- cell.click(event => {
- event.stopPropagation();
- var params = {};
- params.model = this.attributes.relation;
- params.res_id = this.field.get(record);
- params.mode = ['form'];
- params.name = this.attributes.string;
- params.context = this.field.get_context(record);
- Sao.Tab.create(params);
- });
- }
- });
-
- Sao.View.Tree.One2OneColumn = Sao.class_(Sao.View.Tree.Many2OneColumn, {
- class_: 'column-one2one'
- });
-
- Sao.View.Tree.SelectionColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-selection',
- init: function(model, attributes) {
- Sao.View.Tree.SelectionColumn._super.init.call(this, model,
- attributes);
- Sao.common.selection_mixin.init.call(this);
- if (!this.tree || this.tree.editable) {
- this.init_selection();
- }
- },
- init_selection: function(key) {
- Sao.common.selection_mixin.init_selection.call(this, key);
- },
- update_selection: function(record, callback) {
- if (!this.selection) {
- this.init_selection();
- }
- Sao.common.selection_mixin.update_selection.call(this, record,
- this.field, callback);
- },
- get_textual_value: function(record) {
- var related = this.field.name + ':string';
- if (!this.tree.editable && related in record._values) {
- return record._values[related];
- } else {
- var value = this.field.get(record);
- for (const option of this.selection) {
- if (option[0] === value) {
- return option[1];
- }
- }
- return value;
- }
- },
- update_text: function(cell, record) {
- if (!this.tree.editable &&
- (this.field.name + ':string' in record._values)) {
- var text_value = this.get_textual_value(record);
- cell.text(text_value).attr('title', text_value);
- } else {
- this.update_selection(record, () => {
- var value = this.field.get(record);
- var prm, text, found = false;
- for (const option of this.selection) {
- if (option[0] === value) {
- found = true;
- text = option[1];
- break;
- }
- }
- if (!found) {
- prm = Sao.common.selection_mixin.get_inactive_selection
- .call(this, value).then(function(inactive) {
- return inactive[1];
- });
- } else {
- prm = jQuery.when(text);
- }
- prm.done(text_value => {
- cell.text(text_value).attr('title', text_value);
- });
- });
- }
- }
- });
-
- Sao.View.Tree.MultiSelectionColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-multiselection',
- init: function(model, attributes) {
- Sao.View.Tree.MultiSelectionColumn._super.init.call(
- this, model, attributes);
- Sao.common.selection_mixin.init.call(this);
- this.init_selection();
- },
- init_selection: function(key) {
- Sao.common.selection_mixin.init_selection.call(this, key);
- },
- update_selection: function(record, callback) {
- Sao.common.selection_mixin.update_selection.call(this, record,
- this.field, callback);
- },
- get_textual_value: function(record) {
- var related = this.field.name + ':string';
- if (!this.tree.editable && related in record._values) {
- return record._values[related];
- } else {
- var values = this.field.get_eval(record).map(value => {
- for (const option of this.selection) {
- if (option[0] === value) {
- return option[1];
- }
- }
- return '';
- });
- return values.join(';');
- }
- },
- update_text: function(cell, record) {
- if (!this.tree.editable &&
- (this.field_name + ':string' in record._values)) {
- var text_value = this.get_textual_value(record);
- cell.text(text_value).attr('title', text_value);
- } else {
- this.update_selection(record, () => {
- var text_value = this.get_textual_value(record);
- cell.text(text_value).attr('title', text_value);
- });
- }
- },
- });
-
- Sao.View.Tree.ReferenceColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-reference',
- init: function(model, attributes) {
- Sao.View.Tree.ReferenceColumn._super.init.call(this, model,
- attributes);
- Sao.common.selection_mixin.init.call(this);
- this.init_selection();
- },
- init_selection: function(key) {
- Sao.common.selection_mixin.init_selection.call(this, key);
- },
- update_selection: function(record, callback) {
- Sao.common.selection_mixin.update_selection.call(this, record,
- this.field, callback);
- },
- get_cell: function() {
- var cell = Sao.View.Tree.ReferenceColumn._super
- .get_cell.call(this);
- cell.append(jQuery('
', {
- 'href': '#',
- }));
- return cell;
- },
- get_textual_value: function(record) {
- var value = Sao.View.Tree.ReferenceColumn._super
- .get_textual_value.call(this, record);
- var model, name, text;
- if (!value) {
- model = '';
- name = '';
- } else {
- model = value[0];
- name = value[1];
- }
- if (model) {
- for (const option of this.selection) {
- if (option[0] === model) {
- model = option[1];
- break;
- }
- }
- text = model + ',' + name;
- } else {
- text = name;
- }
- return text;
- },
- update_text: function(cell, record) {
- cell = cell.children('a');
- cell.unbind('click');
- this.update_selection(record, () => {
- var text = this.get_textual_value(record);
- cell.text(text).attr('title', text);
- cell.click(event => {
- event.stopPropagation();
- var value = this.field.get(record);
- if (value) {
- var model = value.split(',')[0];
- var id = parseInt(value.split(',')[1], 10);
- var params = {
- model: model,
- res_id: id,
- mode: ['form'],
- name: this.attributes.string,
- context: this.field.get_context(record),
- };
- Sao.Tab.create(params);
- }
- });
- });
- }
- });
-
- Sao.View.Tree.DictColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-dict',
- get_textual_value: function(record) {
- return '(' + Object.keys(this.field.get_client(record)).length + ')';
- },
- });
-
- Sao.View.Tree.DateColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-date',
- get_textual_value: function(record) {
- var value = this.field.get_client(record);
- var date_format = this.field.date_format(record);
- return Sao.common.format_date(date_format, value);
- }
- });
-
- Sao.View.Tree.TimeColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-time',
- get_textual_value: function(record) {
- var value = this.field.get_client(record);
- return Sao.common.format_time(
- this.field.time_format(record), value);
- }
- });
-
- Sao.View.Tree.TimeDeltaColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-timedelta'
- });
-
- Sao.View.Tree.One2ManyColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-one2many',
- get_textual_value: function(record) {
- return '( ' + this.field.get_client(record).length + ' )';
- }
- });
-
- Sao.View.Tree.Many2ManyColumn = Sao.class_(Sao.View.Tree.One2ManyColumn, {
- class_: 'column-many2many'
- });
-
- Sao.View.Tree.BinaryColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-binary',
- init: function(model, attributes) {
- Sao.View.Tree.BinaryColumn._super.init.call(this, model, attributes);
- this.filename = attributes.filename || null;
- },
- get_cell: function() {
- var cell = Sao.View.Tree.BinaryColumn._super.get_cell.call(this);
- jQuery('
').appendTo(cell);
- return cell;
- },
- get_textual_value: function(record) {
- var size;
- if (this.field.get_size) {
- size = this.field.get_size(record);
- } else {
- size = this.field.get(record).length;
- }
- return size? Sao.common.humanize(size, 'B') : '';
- },
- update_text: function(cell, record) {
- var text = this.get_textual_value(record);
- cell.children('span').text(text).attr('title', text);
- var button = cell.children('button');
- if (!button.length) {
- button = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Save As..."),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-download')
- ).appendTo(cell)
- .click(record, event => {
- // Prevent editable tree to start edition
- event.stopPropagation();
- this.save_as(event.data);
- });
- }
- if (!text) {
- button.hide();
- } else {
- button.show();
- }
- },
- save_as: function(record) {
- var filename;
- var filename_field = record.model.fields[this.filename];
- if (filename_field) {
- filename = filename_field.get_client(record);
- }
- var prm;
- if (this.field.get_data) {
- prm = this.field.get_data(record);
- } else {
- prm = jQuery.when(this.field.get(record));
- }
- prm.done(data => {
- Sao.common.download_file(data, filename);
- });
- },
- });
-
- Sao.View.Tree.ImageColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-image',
- get_cell: function() {
- var cell = jQuery('
![]()
', {
- 'class': this.class_ + ' center-block',
- 'tabindex': 0
- });
- this.height = parseInt(this.attributes.height || 100, 10);
- this.width = parseInt(this.attributes.width || 300, 10);
- cell.css('max-height', this.height);
- cell.css('max-width', this.width);
- cell.css('height', 'auto');
- cell.css('width', 'auto');
- return cell;
- },
- render: function(record, cell) {
- if (!cell) {
- cell = this.get_cell();
- }
- const render = () => {
- const set_src = data => {
- cell.attr('src', Sao.common.image_url(data));
- };
-
- var value = this.field.get_client(record);
- if (value) {
- if (value > Sao.config.image_max_size) {
- set_src(null);
- } else {
- this.field.get_data(record).done(set_src);
- }
- } else {
- set_src(null);
- }
- };
- const render_error = () => {
- cell.attr('src', null);
- };
- if (!record.is_loaded(this.attributes.name)) {
- record.load(this.attributes.name, true, false)
- .done(render)
- .fail(render_error);
- } else {
- if (render.exception) {
- render_error();
- } else {
- render();
- }
- }
- return cell;
- }
- });
-
- Sao.View.Tree.URLColumn = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-url',
- render: function(record, cell) {
- cell = Sao.View.Tree.URLColumn._super.render.call(
- this, record, cell);
- this.field.set_state(record);
- var state_attrs = this.field.get_state_attrs(record);
- if (state_attrs.readonly) {
- cell.hide();
- } else {
- cell.show();
- }
- return cell;
- }
- });
-
- Sao.View.Tree.ProgressBar = Sao.class_(Sao.View.Tree.CharColumn, {
- class_: 'column-progressbar',
- get_cell: function() {
- var cell = jQuery('
', {
- 'class': this.class_ + ' progress',
- 'tabindex': 0
- });
- var progressbar = jQuery('
', {
- 'class': 'progress-bar',
- 'role': 'progressbar',
- 'aria-valuemin': 0,
- 'aria-valuemax': 100
- }).appendTo(cell);
- progressbar.css('min-width: 2em');
- return cell;
- },
- get_textual_value: function(record) {
- var text = this.field.get_client(record, 100);
- if (text) {
- text = Sao.i18n.gettext('%1%', text);
- }
- return text;
- },
- update_text: function(cell, record) {
- var text = this.get_textual_value(record);
- var value = this.field.get(record) || 0;
- var progressbar = cell.find('.progress-bar');
- progressbar.attr('aria-valuenow', value * 100);
- progressbar.css('width', value * 100 + '%');
- progressbar.text(text).attr('title', text);
- }
- });
-
- Sao.View.Tree.ButtonColumn = Sao.class_(Object, {
- init: function(view, attributes) {
- this.view = view;
- this.type = 'button';
- this.attributes = attributes;
- this.footers = [];
- },
- render: function(record, el) {
- var button = new Sao.common.Button(this.attributes, el, 'btn-sm');
- if (!el) {
- button.el.click(
- [record, button], this.button_clicked.bind(this));
- }
- jQuery.map(this.view.screen.model.fields,
- function(field, name) {
- if ((field.description.loading || 'eager') ==
- 'eager') {
- return name;
- } else {
- return undefined;
- }
- });
- button.set_state(record);
- return button.el;
- },
- set_visible: function(visible) {
- var cells = this.footers.slice();
- cells.push(this.header);
- for (const cell of cells) {
- if (visible) {
- cell.show();
- cell.removeClass('invisible');
- } else {
- cell.hide();
- cell.addClass('invisible');
- }
- }
- },
- get_visible: function() {
- return !this.header.hasClass('invisible');
- },
- button_clicked: function(event) {
- var record = event.data[0];
- var button = event.data[1];
- if (record != this.view.screen.current_record) {
- // Need to raise the event to get the record selected
- return true;
- }
- var states = record.expr_eval(this.attributes.states || {});
- if (states.invisible || states.readonly) {
- return;
- }
- button.el.prop('disabled', true); // state will be reset at display
- var row = this.view.rows.find(function(row) {
- return row.record == record;
- });
- if (row) {
- row._drawed_record = null; // force redraw the row
- }
- this.view.screen.button(this.attributes);
- }
- });
-
- Sao.View.TreeXMLViewParser.WIDGETS = {
- 'binary': Sao.View.Tree.BinaryColumn,
- 'boolean': Sao.View.Tree.BooleanColumn,
- 'callto': Sao.View.Tree.URLColumn,
- 'char': Sao.View.Tree.CharColumn,
- 'date': Sao.View.Tree.DateColumn,
- 'dict': Sao.View.Tree.DictColumn,
- 'email': Sao.View.Tree.URLColumn,
- 'float': Sao.View.Tree.FloatColumn,
- 'image': Sao.View.Tree.ImageColumn,
- 'integer': Sao.View.Tree.IntegerColumn,
- 'many2many': Sao.View.Tree.Many2ManyColumn,
- 'many2one': Sao.View.Tree.Many2OneColumn,
- 'numeric': Sao.View.Tree.FloatColumn,
- 'one2many': Sao.View.Tree.One2ManyColumn,
- 'one2one': Sao.View.Tree.One2OneColumn,
- 'progressbar': Sao.View.Tree.ProgressBar,
- 'reference': Sao.View.Tree.ReferenceColumn,
- 'selection': Sao.View.Tree.SelectionColumn,
- 'multiselection': Sao.View.Tree.MultiSelectionColumn,
- 'sip': Sao.View.Tree.URLColumn,
- 'text': Sao.View.Tree.TextColum,
- 'time': Sao.View.Tree.TimeColumn,
- 'timedelta': Sao.View.Tree.TimeDeltaColumn,
- 'url': Sao.View.Tree.URLColumn,
- };
-
- Sao.View.EditableTree = {};
-
- Sao.View.EditableTree.editable_mixin = function(widget) {
- var key_press = function(event_) {
- if ((event_.which == Sao.common.TAB_KEYCODE) ||
- (event_.which == Sao.common.UP_KEYCODE) ||
- (event_.which == Sao.common.DOWN_KEYCODE) ||
- (event_.which == Sao.common.ESC_KEYCODE) ||
- (event_.which == Sao.common.RETURN_KEYCODE)) {
- this.focus_out();
- }
- };
- widget.el.on('keydown', key_press.bind(widget));
- };
-
- Sao.View.EditableTree.Char = Sao.class_(Sao.View.Form.Char, {
- class_: 'editabletree-char',
- init: function(view, attributes) {
- Sao.View.EditableTree.Char._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.URL = Sao.class_(Sao.View.EditableTree.Char, {
- class_: 'editable-url',
- set_readonly: function(readonly) {
- Sao.View.EditableTree.URL._super.set_readonly.call(this, readonly);
- if (readonly) {
- this.input.hide();
- } else {
- this.input.show();
- }
- },
- });
-
- Sao.View.EditableTree.Date = Sao.class_(Sao.View.Form.Date, {
- class_: 'editabletree-date',
- init: function(view, attributes) {
- Sao.View.EditableTree.Date._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.Time = Sao.class_(Sao.View.Form.Time, {
- class_: 'editabletree-time',
- init: function(view, attributes) {
- Sao.View.EditableTree.Time._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.TimeDelta = Sao.class_(Sao.View.Form.TimeDelta, {
- class_: 'editabletree-timedelta',
- init: function(view, attributes) {
- Sao.View.EditableTree.TimeDelta._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.Integer = Sao.class_(Sao.View.Form.Integer, {
- class_: 'editabletree-integer',
- init: function(view, attributes) {
- attributes = jQuery.extend({}, attributes);
- delete attributes.symbol;
- Sao.View.EditableTree.Integer._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- },
- get width() {
- return null;
- },
- });
-
- Sao.View.EditableTree.Float = Sao.class_(Sao.View.Form.Float, {
- class_: 'editabletree-float',
- init: function(view, attributes) {
- attributes = jQuery.extend({}, attributes);
- delete attributes.symbol;
- Sao.View.EditableTree.Float._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- },
- get width() {
- return null;
- },
- });
-
- Sao.View.EditableTree.Selection = Sao.class_(Sao.View.Form.Selection, {
- class_: 'editabletree-selection',
- init: function(view, attributes) {
- Sao.View.EditableTree.Selection._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.Boolean = Sao.class_(Sao.View.Form.Boolean, {
- class_: 'editabletree-boolean',
- init: function(view, attributes) {
- Sao.View.EditableTree.Boolean._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.Many2One = Sao.class_(Sao.View.Form.Many2One, {
- class_: 'editabletree-many2one',
- init: function(view, attributes) {
- Sao.View.EditableTree.Many2One._super.init.call(
- this, view, attributes);
- },
- });
-
- Sao.View.EditableTree.Reference = Sao.class_(Sao.View.Form.Reference, {
- class_: 'editabletree-reference',
- init: function(view, attributes) {
- Sao.View.EditableTree.Reference._super.init.call(
- this, view, attributes);
- },
- });
-
- Sao.View.EditableTree.One2One = Sao.class_(Sao.View.Form.One2One, {
- class_: 'editabletree-one2one',
- init: function(view, attributes) {
- Sao.View.EditableTree.One2One._super.init.call(
- this, view, attributes);
- },
- });
-
- Sao.View.EditableTree.One2Many = Sao.class_(Sao.View.EditableTree.Char, {
- class_: 'editabletree-one2many',
- init: function(view, attributes) {
- Sao.View.EditableTree.One2Many._super.init.call(
- this, view, attributes);
- },
- display: function(record, field) {
- if (record) {
- this.el.val('(' + field.get_client(record).length + ')');
- } else {
- this.el.val('');
- }
- },
- key_press: function(event_) {
- // TODO: remove when key_press is implemented
- if (event_.which == Sao.common.TAB_KEYCODE) {
- this.focus_out();
- }
- },
- set_value: function(record, field) {
- }
- });
-
- Sao.View.EditableTree.Binary = Sao.class_(Sao.View.Form.Binary, {
- class_: 'editabletree-binary',
- init: function(view, attributes) {
- Sao.View.EditableTree.Binary._super.init.call(
- this, view, attributes);
- Sao.View.EditableTree.editable_mixin(this);
- }
- });
-
- Sao.View.EditableTree.WIDGETS = {
- 'binary': Sao.View.EditableTree.Binary,
- 'boolean': Sao.View.EditableTree.Boolean,
- 'callto': Sao.View.EditableTree.URL,
- 'char': Sao.View.EditableTree.Char,
- 'date': Sao.View.EditableTree.Date,
- 'email': Sao.View.EditableTree.URL,
- 'float': Sao.View.EditableTree.Float,
- 'integer': Sao.View.EditableTree.Integer,
- 'many2many': Sao.View.EditableTree.Many2Many,
- 'many2one': Sao.View.EditableTree.Many2One,
- 'numeric': Sao.View.EditableTree.Float,
- 'one2many': Sao.View.EditableTree.One2Many,
- 'one2one': Sao.View.EditableTree.One2One,
- 'reference': Sao.View.EditableTree.Reference,
- 'selection': Sao.View.EditableTree.Selection,
- 'sip': Sao.View.EditableTree.URL,
- 'text': Sao.View.EditableTree.Char,
- 'time': Sao.View.EditableTree.Time,
- 'timedelta': Sao.View.EditableTree.TimeDelta,
- 'url': Sao.View.EditableTree.URL,
- };
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.View.GraphXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
- init: function(view, exclude_field, fields) {
- Sao.View.GraphXMLViewParser._super.init.call(
- this, view, exclude_field, fields);
- this._xfield = null;
- this._yfields = [];
- },
- _node_attributes: function(node) {
- var node_attrs = {};
- for (const attribute of node.attributes) {
- node_attrs[attribute.name] = attribute.value;
- }
- if (node_attrs.name) {
- if (!node_attrs.string && (node_attrs.name != '#')) {
- var field = this.field_attrs[node_attrs.name];
- node_attrs.string = field.string;
- }
- }
- return node_attrs;
- },
- _parse_graph: function(node, attributes) {
- for (const child of node.childNodes) {
- this.parse(child);
- }
- var Widget = Sao.View.GraphXMLViewParser.WIDGETS[
- attributes.type || 'vbar'];
- var widget = new Widget(this.view, this._xfield, this._yfields);
- this.view.el.append(widget.el);
- this.view.widgets.root = widget;
- },
- _parse_x: function(node, attributes) {
- for (const child of node.children) {
- this._xfield = this._node_attributes(child);
- }
- },
- _parse_y: function(node, attributes) {
- for (const child of node.children) {
- this._yfields.push(this._node_attributes(child));
- }
- }
- });
-
- Sao.View.Graph = Sao.class_(Sao.View, {
- editable: false,
- view_type: 'graph',
- xml_parser: Sao.View.GraphXMLViewParser,
- init: function(view_id, screen, xml, children_field) {
- this.el = jQuery('
', {
- 'class': 'graph'
- });
-
- Sao.View.Graph._super.init.call(this, view_id, screen, xml);
- },
- display: function() {
- return this.widgets.root.display(this.group);
- }
- });
-
- Sao.View.Graph.Chart = Sao.class_(Object, {
- _chart_type: undefined,
-
- init: function(view, xfield, yfields) {
- this.view = view;
- this.xfield = xfield;
- this.yfields = yfields;
- this.el = jQuery('
');
- this.el.uniqueId();
- },
- update_data: function(group) {
- var data = {};
- var record, yfield, key;
- var i, len, j, y_len;
-
- this.ids = {};
- data.columns = [['labels']];
- data.names = {};
- var key2columns = {};
- var fields2load = [this.xfield.name];
- for (i = 0, len = this.yfields.length; i < len; i++) {
- yfield = this.yfields[i];
- key = yfield.key || yfield.name;
- data.columns.push([key]);
- data.names[key] = yfield.string;
- key2columns[key] = i + 1;
- fields2load.push(yfield.name);
- }
-
- var prms = [];
- const set_data = index => {
- return () => {
- record = group[index];
- var x = record.field_get_client(this.xfield.name);
- // c3 does not support moment
- if (x && (x.isDate || x.isDateTime)) {
- x = x.toString();
- }
- var pos = data.columns[0].indexOf(x);
- if (pos < 0) {
- pos = data.columns[0].push(x) - 1;
- }
- this._add_id(x, record.id);
-
- var column;
- for (j = 0, y_len = this.yfields.length; j < y_len; j++) {
- yfield = this.yfields[j];
- key = yfield.key || yfield.name;
- column = data.columns[key2columns[key]];
- if (column[pos] === undefined) {
- column[pos] = null;
- }
- if (yfield.domain) {
- var ctx = jQuery.extend({},
- Sao.Session.current_session.context);
- ctx.context = ctx;
- ctx._user = Sao.Session.current_session.user_id;
- for (var field in group.model.fields) {
- ctx[field] = record.field_get(field);
- }
- var decoder = new Sao.PYSON.Decoder(ctx);
- if (!decoder.decode(yfield.domain)) {
- continue;
- }
- }
- if (!column[pos]) {
- column[pos] = 0;
- }
- if (yfield.name == '#') {
- column[pos] += 1;
- } else {
- var value = record.field_get(yfield.name);
- if (value && value.isTimeDelta) {
- value = value.asSeconds();
- }
- column[pos] += value || 0;
- }
- }
- };
- };
-
- var r_prms = [];
- for (i = 0, len = group.length; i < len; i++) {
- record = group[i];
- for (const fname of fields2load) {
- prms.push(record.load(fname));
- }
- r_prms.push(
- jQuery.when.apply(jQuery, prms).then(set_data(i)));
- }
- return jQuery.when.apply(jQuery, r_prms).then(function() {
- return data;
- });
- },
- _add_id: function(key, id) {
- if (!(key in this.ids)) {
- this.ids[key] = [];
- }
- this.ids[key].push(id);
- },
- display: function(group) {
- var update_prm = this.update_data(group);
- update_prm.done(data => {
- c3.generate(this._c3_config(data));
- });
- return update_prm;
- },
- _c3_config: function(data) {
- var c3_config = {};
-
- c3_config.bindto = '#' + this.el.attr('id');
- c3_config.data = data;
- c3_config.data.type = this._chart_type;
- c3_config.data.x = 'labels';
- c3_config.data.onclick = this.action.bind(this);
-
- var type = this.view.screen.model.fields[this.xfield.name]
- .description.type;
- if ((type == 'date') || (type == 'datetime')) {
- var format_func, date_format, time_format;
- date_format = Sao.common.date_format(
- this.view.screen.context.date_format);
- time_format = '%X';
- if (type == 'datetime') {
- c3_config.data.xFormat = '%Y-%m-%d %H:%M:%S';
- format_func = function(dt) {
- return Sao.common.format_datetime(
- date_format + ' ' + time_format, moment(dt));
- };
- } else {
- c3_config.data.xFormat = '%Y-%m-%d';
- format_func = function(dt) {
- return Sao.common.format_date(date_format, moment(dt));
- };
- }
- c3_config.axis = {
- x: {
- type: 'timeseries',
- tick: {
- format: format_func,
- }
- }
- };
- } else {
- c3_config.axis = {
- x: {
- type: 'category',
- }
- };
- }
- var color = this.view.attributes.color || Sao.config.graph_color;
- var rgb = Sao.common.hex2rgb(
- Sao.common.COLOR_SCHEMES[color] || color);
- var maxcolor = Math.max.apply(null, rgb);
- var keys = [];
- var i, yfield;
- for (i = 0; i < this.yfields.length; i++) {
- yfield = this.yfields[i];
- keys.push(yfield.key || yfield.name);
- }
- var colors = Sao.common.generateColorscheme(
- color, keys, maxcolor / (keys.length || 1));
- for (i = 0; i < this.yfields.length; i++) {
- yfield = this.yfields[i];
- if (yfield.color) {
- colors[yfield.key || yfield.name] = yfield.color;
- }
- }
- c3_config.data.color = function(color, column) {
- // column is an object when called for legend
- var key = column.id || column;
- return colors[key] || color;
- };
- return c3_config;
- },
- action: function(data, element) {
- var ids = this.ids[this._action_key(data)];
- var ctx = jQuery.extend({}, this.view.screen.group._context);
- delete ctx.active_ids;
- delete ctx.active_id;
- Sao.Action.exec_keyword('graph_open', {
- model: this.view.screen.model_name,
- id: ids[0],
- ids: ids
- }, ctx, false);
- },
- _action_key: function(data) {
- return data.x;
- }
- });
-
- Sao.View.Graph.VerticalBar = Sao.class_(Sao.View.Graph.Chart, {
- _chart_type: 'bar'
- });
-
- Sao.View.Graph.HorizontalBar = Sao.class_(Sao.View.Graph.Chart, {
- _chart_type: 'bar',
- _c3_config: function(data) {
- var config = Sao.View.Graph.HorizontalBar._super._c3_config
- .call(this, data);
- config.axis.rotated = true;
- return config;
- }
- });
-
- Sao.View.Graph.Line = Sao.class_(Sao.View.Graph.Chart, {
- _chart_type: 'line',
- _c3_config: function(data) {
- var config = Sao.View.Graph.Line._super._c3_config
- .call(this, data);
- config.line = {
- connectNull: true,
- };
- return config;
- }
- });
-
- Sao.View.Graph.Pie = Sao.class_(Sao.View.Graph.Chart, {
- _chart_type: 'pie',
- _c3_config: function(data) {
- var config = Sao.View.Graph.Pie._super._c3_config.call(this, data);
- var pie_columns = [], pie_names = {};
- var i, len;
- var labels, values;
-
- for (i = 0, len = data.columns.length; i < len; i++) {
- if (data.columns[i][0] == 'labels') {
- labels = data.columns[i].slice(1);
- } else {
- values = data.columns[i].slice(1);
- }
- }
-
- // Pie chart do not support axis definition.
- delete config.axis;
- delete config.data.x;
- var format_func;
- var type = this.view.screen.model.fields[this.xfield.name]
- .description.type;
- if ((type == 'date') || (type == 'datetime')) {
- var date_format = Sao.common.date_format(
- this.view.screen.context.date_format);
- var datetime_format = date_format + ' %X';
- if (type == 'datetime') {
- format_func = function(dt) {
- return Sao.common.format_datetime(datetime_format, dt);
- };
- } else {
- format_func = function(dt) {
- return Sao.common.format_date(date_format, dt);
- };
- }
- }
- var label;
- for (i = 0, len = labels.length; i < len; i++) {
- label = labels[i];
- if (format_func) {
- label = format_func(label);
- }
- pie_columns.push([i, values[i]]);
- pie_names[i] = label;
- }
-
- config.data.columns = pie_columns;
- config.data.names = pie_names;
- return config;
- },
- _add_id: function(key, id) {
- var type = this.xfield.type;
- if ((type == 'date') || (type == 'datetime')) {
- var date_format = Sao.common.date_format(
- this.view.screen.context.date_format);
- var datetime_format = date_format + ' %X';
- if (type == 'datetime') {
- key = Sao.common.format_datetime(datetime_format, key);
- } else {
- key = Sao.common.format_date(date_format, key);
- }
- }
- Sao.View.Graph.Pie._super._add_id.call(this, key, id);
- },
- _action_key: function(data) {
- return data.id;
- }
- });
-
- Sao.View.GraphXMLViewParser.WIDGETS = {
- 'hbar': Sao.View.Graph.HorizontalBar,
- 'line': Sao.View.Graph.Line,
- 'pie': Sao.View.Graph.Pie,
- 'vbar': Sao.View.Graph.VerticalBar,
- };
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- function set_calendar_height(el) {
- var height = window.innerHeight - 15;
- if (el.parents('.modal-body').length) {
- var modal_height = parseInt(
- el.parents('.modal-body').css('max-height'), 10);
- if (isNaN(modal_height)) {
- height -= 200;
- } else {
- height = modal_height;
- }
- }
- height -= el.find('.fc-toolbar').height();
- el.parents('.panel-body').each(function(i, panel) {
- panel = jQuery(panel);
- height -= parseInt(panel.css('padding-top'), 10);
- height -= parseInt(panel.css('padding-bottom'), 10);
- });
- el.parents('.panel').each(function(i, panel) {
- panel = jQuery(panel);
- var lengths = panel.css('box-shadow').match(/\d+px/g);
- if (lengths && lengths.length) {
- lengths = lengths.map(function(length) {
- length = parseInt(length, 10);
- return isNaN(length) ? 0 : length;
- });
- height -= Math.max.apply(null, lengths);
- }
- });
- height -= el[0].getBoundingClientRect().y;
- el.fullCalendar('option', 'contentHeight', height);
- }
-
- jQuery(window).resize(function() {
- jQuery('.calendar').each(function(i, el) {
- set_calendar_height(jQuery(el));
- });
- });
-
- Sao.View.CalendarXMLViewParser = Sao.class_(Sao.View.XMLViewParser, {
- _parse_calendar: function(node, attributes) {
- for (const child of node.childNodes) {
- this.parse(child);
- }
-
- var view_week;
- if (this.view.screen.model.fields[attributes.dtstart]
- .description.type == "datetime") {
- view_week = 'agendaWeek';
- } else {
- view_week = 'basicWeek';
- }
- var view_day;
- if (this.view.screen.model.fields[attributes.dtstart]
- .description.type == "datetime") {
- view_day = 'agendaDay';
- } else {
- view_day = 'basicDay';
- }
- var defaultview = 'month';
- if (attributes.mode == 'week') {
- defaultview = view_week;
- }
- if (attributes.mode == 'day') {
- defaultview = view_day;
- }
- var header = {
- left: 'today prev,next',
- center: 'title',
- right: 'month,' + view_week + ',' + view_day,
- };
- if (Sao.i18n.rtl) {
- var header_rtl = jQuery.extend({}, header);
- header_rtl.left = header.right;
- header_rtl.right = header.left;
- header = header_rtl;
- }
- this.view.el.fullCalendar({
- defaultView: defaultview,
- header: header,
- timeFormat: 'H:mm',
- scrollTime: (
- this.view.screen.context.calendar_scroll_time ||
- Sao.Time(6)).toString(),
- events: this.view.get_events.bind(this.view),
- locale: Sao.i18n.getlang().slice(0, 2),
- isRTL: Sao.i18n.rtl,
- themeSystem: 'bootstrap3',
- bootstrapGlyphicons: {
- 'prev': 'chevron-' + (Sao.i18n.rtl? 'right' : 'left'),
- 'next': 'chevron-' + (Sao.i18n.rtl? 'left' : 'right'),
- },
- buttonTextOverride: {
- 'today': Sao.i18n.gettext("Today"),
- 'month': Sao.i18n.gettext("Month"),
- 'week': Sao.i18n.gettext("Week"),
- 'day': Sao.i18n.gettext("Day"),
- },
- eventRender: this.view.event_render.bind(this.view),
- eventResize: this.view.event_resize.bind(this.view),
- eventDrop: this.view.event_drop.bind(this.view),
- eventClick: this.view.event_click.bind(this.view),
- dayClick: this.view.day_click.bind(this.view),
- });
-
- if (attributes.height !== undefined) {
- this.view.el.css('min-height', attributes.height + 'px');
- }
- if (attributes.width !== undefined) {
- this.view.el.css('min-width', attributes.width + 'px');
- }
- },
- _parse_field: function(node, attributes) {
- this.view.fields.push(attributes.name);
- },
- });
-
- Sao.View.Calendar = Sao.class_(Sao.View, {
- /* Fullcalendar works with utc date, the default week start day depends on
- the user language, the events dates are handled by moment object. */
- editable: false,
- creatable: false,
- view_type: 'calendar',
- xml_parser: Sao.View.CalendarXMLViewParser,
- init: function(view_id, screen, xml) {
- // Used to check if the events are still processing
- this.processing = true;
- this.fields = [];
- this.el = jQuery('
', {
- 'class': 'calendar'
- });
- Sao.View.Calendar._super.init.call(this, view_id, screen, xml);
- //this.el.fullCalendar('changeView', defaultview);
- },
- get_colors: function(record) {
- var colors = {};
- colors.text_color = Sao.config.calendar_colors[0];
- if (this.attributes.color) {
- colors.text_color = record.field_get(
- this.attributes.color);
- }
- colors.background_color = Sao.config.calendar_colors[1];
- if (this.attributes.background_color) {
- colors.background_color = record.field_get(
- this.attributes.background_color);
- }
- return colors;
- },
- display: function() {
- this.el.fullCalendar('render');
- set_calendar_height(this.el);
- // Don't refetch events from server when get_events is processing
- if (!this.processing) {
- this.el.fullCalendar('refetchEvents');
- }
- },
- insert_event: function(record) {
- var description_fields = jQuery.extend([], this.fields);
- var title_field = description_fields.shift();
- var title = this.screen.model.fields[title_field].get_client(
- record);
- var field_start = record.model.fields[this.attributes.dtstart];
- var date_start = field_start.get_client(record);
- field_start.set_state(record);
- var date_end = null;
- var field_end;
- if (this.attributes.dtend) {
- field_end = record.model.fields[this.attributes.dtend];
- date_end = field_end.get_client(record);
- field_end.set_state(record);
- }
-
- var model_access = Sao.common.MODELACCESS.get(
- this.screen.model_name);
- var editable = (
- parseInt(this.attributes.editable || 1, 10) &&
- model_access.write);
-
- var description = [];
- for (const field of description_fields) {
- description.push(
- this.screen.model.fields[field].get_client( record));
- }
- description = description.join('\n');
- if (date_start) {
- var allDay = date_start.isDate &&
- (!date_end || date_end.isDate);
- if (allDay && date_end && !date_end.isSame(date_start) &&
- this.screen.current_view.view_type == "calendar") {
- // Add one day to allday event that last more than one day.
- // http://github.com/fullcalendar/fullcalendar/issues/2909
- date_end.add(1, 'day');
- }
- // Skip invalid event
- if (date_end && date_start > date_end) {
- return;
- }
- var event_editable = (
- editable &&
- !field_start.get_state_attrs(record).readonly &&
- (!date_end || !field_end.get_state_attrs(record).readonly));
- var colors = this.get_colors(record);
- var values = {
- title: title,
- start: date_start,
- end: date_end,
- allDay: allDay,
- editable: event_editable,
- color: colors.background_color,
- textColor: colors.text_color,
- record: record,
- description: description
- };
- this.events.push(values);
- }
- },
- get_events: function(start, end, timezone, callback) {
- this.processing = true;
- this.start = Sao.DateTime(start.utc());
- this.end = Sao.DateTime(end.utc());
- var prm = jQuery.when();
- if (this.screen.current_view &&
- (this.screen.current_view.view_type != 'form')) {
- var search_string = this.screen.screen_container.get_text();
- prm = this.screen.search_filter(search_string);
- }
- this.events = [];
- var promisses = [];
- prm.then(() => {
- this.group.forEach(record => {
- var record_promisses = [];
- for (const name of this.fields) {
- record_promisses.push(record.load(name));
- }
- var prm = jQuery.when.apply(jQuery, record_promisses).then(
- () => {
- this.insert_event(record);
- });
- promisses.push(prm);
- });
- return jQuery.when.apply(jQuery, promisses).then(() => {
- callback(this.events);
- }).always(() => {
- this.processing = false;
- });
- });
- },
- event_click: function(calEvent, jsEvent, view) {
- // Prevent opening the wrong event while the calendar event clicked
- // when loading
- if (!this.clicked_event) {
- this.clicked_event = true;
- this.screen.current_record = calEvent.record;
- this.screen.switch_view().always(() => {
- this.clicked_event = false;
- });
- }
- },
- event_drop: function(event, delta, revertFunc, jsEvent, ui, view) {
- var dtstart = this.attributes.dtstart;
- var dtend = this.attributes.dtend;
- var record = event.record;
- var previous_start = record.field_get(dtstart);
- var previous_end = previous_start;
- if (dtend) {
- previous_end = record.field_get(dtend);
- }
- var new_start = event.start;
- var new_end = event.end;
- if (new_end == previous_start || !new_end) {
- new_end = new_start;
- }
- if (previous_start.isDateTime) {
- new_end = Sao.DateTime(new_end.format()).utc();
- new_start = Sao.DateTime(new_start.format()).utc();
- } else if (!previous_start.isSame(previous_end)) {
- // Remove the day that was added at the event end.
- new_end.subtract(1, 'day');
- this.el.fullCalendar('refetchEvents');
- }
- if (previous_start <= new_start) {
- if (dtend) {
- record.field_set_client(dtend, new_end);
- }
- record.field_set_client(dtstart, new_start);
- } else {
- record.field_set_client(dtstart, new_start);
- if (dtend) {
- record.field_set_client(dtend, new_end);
- }
- }
- record.save();
- },
- event_resize: function(event, delta, revertFunc, jsEvent, ui, view) {
- var dtend = this.attributes.dtend;
- var record = event.record;
- var previous_end = record.field_get(dtend);
- var new_end = event.end;
- if (previous_end.isDateTime === true) {
- new_end = Sao.DateTime(new_end.format()).utc();
- } else {
- // Remove the day that was added at the event end.
- new_end.subtract(1, 'day');
- this.el.fullCalendar('refetchEvents');
- }
- if (new_end == previous_end || !new_end) {
- new_end = previous_end;
- }
- record.field_set_client(dtend, new_end);
- record.save();
- },
- event_render: function(event, element, view) {
- // The description field is added in the calendar events and the
- // event time is not shown in week view.
- if (this.screen.model.fields.date &&
- this.screen.view_name == 'calendar') {
- element.find('.fc-time').remove();
- }
- element.find('.fc-content')
- .append(jQuery('
', {'class': 'fc-description'})
- .text(event.description));
- element.css('white-space', 'pre')
- .css('overflow', 'hidden')
- .css('text-overflow', 'ellipsis')
- .attr('title', [event.title, event.description]
- .filter(function(e) {
- return e;
- }).join('\n'));
- },
- day_click: function(date, jsEvent, view){
- var model_access = Sao.common.MODELACCESS.get(
- this.screen.model_name);
- if (parseInt(this.attributes.editable || 1, 10) &&
- model_access.create) {
- // Set the calendar date to the clicked date
- this.el.fullCalendar('gotoDate', date);
- this.screen.current_record = null;
- this.screen.new_();
- }
- },
- current_domain: function() {
- if (!this.start && !this.end) {
- return [['id', '=', -1]];
- }
- var start = Sao.DateTime(this.start);
- var end = Sao.DateTime(this.end);
- var dtstart = this.attributes.dtstart;
- var dtend = this.attributes.dtend || dtstart;
- var fields = this.screen.model.fields;
- if (fields[dtstart].description.type == 'date') {
- start = start.todate();
- }
- if (fields[dtend].description.type == 'date') {
- end = end.todate();
- }
- return [
- [dtstart, '!=', null],
- [dtend, '!=', null],
- ['OR',
- ['AND', [dtstart, '>=', start], [dtstart, '<', end]],
- ['AND', [dtend, '>=', start], [dtend, '<', end]],
- ['AND', [dtstart, '<', start], [dtend, '>', end]],
- ],
- ];
- },
- get_displayed_period: function(){
- var DatesPeriod = [];
- if (this.start && this.end) {
- DatesPeriod.push(this.start, this.end);
- }
- return DatesPeriod;
- },
- set_default_date: function(record, selected_date){
- var dtstart = this.attributes.dtstart;
- var field = record.model.fields[dtstart];
- if (field instanceof Sao.field.DateTime) {
- selected_date = Sao.DateTime(selected_date);
- } else if (field instanceof Sao.field.Date) {
- selected_date = Sao.Date(selected_date);
- }
- field.set(record, selected_date);
- record.on_change([dtstart]);
- record.on_change_with([dtstart]);
- },
- get_selected_date: function(){
- return this.el.fullCalendar('getDate');
- },
- get listed_records() {
- return this.events.map(e => e.record);
- },
- });
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.View.ListGroupViewForm = Sao.class_(Sao.View.Form, {
- get record() {
- return this._record;
- },
- set record(value) {
- this._record = value;
- },
- button_clicked: function(event) {
- if (Sao.common.compare(this.screen.selected_records, [this.record])) {
- Sao.View.ListGroupViewForm._super.button_clicked.call(this, event);
- }
- }
- });
-
- Sao.View.ListForm = Sao.class_(Sao.View, {
- editable: true,
- creatable: true,
- view_type: 'list-form',
- init: function(view_id, screen, xml) {
- Sao.View.ListForm._super.init.call(this, view_id, screen, xml);
- if (this.attributes.creatable) {
- this.creatable = Boolean(parseInt(this.attributes.creatable, 10));
- }
-
- this.form_xml = xml;
- this.el = jQuery('
', {
- 'class': 'list-group list-form'
- });
- this._view_forms = [];
- },
- display: function(selected_nodes) {
- var record, view_form, view_form_frame, to_delete;
- var deferreds = [];
- var new_elements = [];
- var selected = new Set();
- if (!jQuery.isEmptyObject(selected_nodes)) {
- for (const id_path of selected_nodes) {
- selected.add(id_path[0]);
- }
- }
- for (var i = 0; i < this.group.length; i++) {
- record = this.group[i];
- view_form = this._view_forms[i];
- if (!view_form) {
- view_form_frame = this._create_form(record);
- new_elements.push(view_form_frame);
- view_form = this._view_forms[this._view_forms.length - 1];
- } else {
- view_form_frame = view_form.el.parent();
- view_form.record = record;
- }
-
- if (~this.group.record_deleted.indexOf(record) ||
- ~this.group.record_removed.indexOf(record)) {
- view_form_frame.addClass('disabled');
- } else {
- view_form_frame.removeClass('disabled');
- }
- if ((this.record === record) || selected.has(record.id)) {
- view_form_frame.addClass('list-group-item-selected');
- } else {
- view_form_frame.removeClass('list-group-item-selected');
- }
- deferreds.push(view_form.display());
- }
- if (new_elements.length > 0) {
- this.el.append(new_elements);
- }
- to_delete = this._view_forms.splice(this.group.length);
- jQuery(to_delete.map(function (vf) { return vf.el[0]; }))
- .parent().detach();
- return jQuery.when.apply(jQuery, deferreds);
- },
- get_selected_paths: function() {
- var paths = [];
- var view_form_frame;
- for (const form of this._view_forms) {
- view_form_frame = form.el.parent();
- if (view_form_frame.hasClass('list-group-item-selected')) {
- paths.push([form.record.id]);
- }
- }
- return paths;
- },
- _create_form: function(record) {
- var view_form = new Sao.View.ListGroupViewForm(
- this.view_id, this.screen, this.form_xml);
- view_form.record = record;
- this._view_forms.push(view_form);
- var frame = jQuery('
', {
- 'class': 'list-group-item list-form-item'
- });
- frame.append(view_form.el);
- frame.click(
- this._view_forms.length - 1, this._select_row.bind(this));
- return frame;
- },
- get selected_records() {
- var records = [];
- for (const view_form of this._view_forms) {
- const frame = view_form.el.parent();
- if (frame.hasClass('list-group-item-selected')) {
- records.push(view_form.record);
- }
- }
- return records;
- },
- get listed_records() {
- return this.group.slice();
- },
- set_cursor: function(new_, reset_view) {
- if (new_) {
- this.el.animate({
- scrollTop: this.el[0].scrollHeight
- });
- }
- },
- select_records: function(from, to) {
- jQuery(this._view_forms.map(function (vf) { return vf.el[0]; }))
- .parent().removeClass('list-group-item-selected');
- if ((from === null) && (to === null)) {
- return;
- }
-
- if (!from) {
- from = 0;
- }
- if (!to) {
- to = 0;
- }
- if (to < from) {
- var tmp = from;
- from = to;
- to = tmp;
- }
-
- for (const form of this._view_forms.slice(from, to + 1)) {
- form.el.parent().addClass('list-group-item-selected');
- }
- },
- _select_row: function(event_) {
- var next_form_idx = event_.data;
- var next_view_form = this._view_forms[next_form_idx];
-
- var prm = jQuery.when();
- if (this.record && (next_view_form.record != this.record)) {
- if (!this.screen.group.parent) {
- if (!this.record.validate(
- this.get_fields(), false, false, false)) {
- prm = jQuery.Deferred().reject();
- } else {
- prm = this.record.save();
- }
- } else if (this.screen.attributes.pre_validate) {
- prm = this.record.pre_validate();
- }
- }
-
- prm.done(() => {
- var current_view_form;
-
- if (event_.shiftKey) {
- let i = 0;
- for (const other_view_form of this._view_forms) {
- if (other_view_form.record === this.record) {
- current_view_form = other_view_form;
- break;
- }
- i++;
- }
- this.select_records(i, next_form_idx);
- } else {
- if (!(event_.ctrlKey || event_.metaKey)) {
- this.select_records(null, null);
- }
- this.record = next_view_form.record;
- next_view_form.el.parent()
- .toggleClass('list-group-item-selected');
- }
- if (current_view_form) {
- this.record = current_view_form.record;
- }
- });
- }
- });
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.Action = {
- report_blob_url: undefined
- };
-
- Sao.Action.exec_action = function(action, data, context) {
- if (!context) {
- context = {};
- } else {
- context = jQuery.extend({}, context);
- }
- var session = Sao.Session.current_session;
- if (data === undefined) {
- data = {};
- } else {
- data = jQuery.extend({}, data);
- }
-
- delete context.active_id;
- delete context.active_ids;
- delete context.active_model;
-
- function add_name_suffix(name, context){
- if (!data.model || !data.ids) {
- return jQuery.when(name);
- }
- var max_records = 5;
- var ids = data.ids.filter(function(id){
- return id >= 0;
- }).slice(0, max_records);
- if (!ids.length) {
- return jQuery.when(name);
- }
- return Sao.rpc({
- 'method': 'model.' + data.model + '.read',
- 'params': [ids, ['rec_name'], context]
- }, Sao.Session.current_session).then(function(result) {
- var name_suffix = result.map(function(record){
- return record.rec_name;
- }).join(Sao.i18n.gettext(', '));
-
- if (data.ids.length > ids.length) {
- name_suffix += Sao.i18n.gettext(',...');
- }
- if (name_suffix) {
- return Sao.i18n.gettext('%1 (%2)', name, name_suffix);
- } else {
- return name;
- }
- });
- }
- data.action_id = action.id;
- var params = {
- 'icon': action['icon.rec_name'] || '',
- };
- var name_prm;
- switch (action.type) {
- case 'ir.action.act_window':
- if (!jQuery.isEmptyObject(action.views)) {
- params.view_ids = [];
- params.mode = [];
- for (const view of action.views) {
- params.view_ids.push(view[0]);
- params.mode.push(view[1]);
- }
- } else if (!jQuery.isEmptyObject(action.view_id)) {
- params.view_ids = [action.view_id[0]];
- }
-
- if (action.pyson_domain === undefined) {
- action.pyson_domain = '[]';
- }
- var ctx = {
- active_model: data.model || null,
- active_id: data.id || null,
- active_ids: data.ids || [],
- };
- ctx = jQuery.extend(ctx, session.context);
- ctx._user = session.user_id;
- var decoder = new Sao.PYSON.Decoder(ctx);
- params.context = jQuery.extend(
- {}, context,
- decoder.decode( action.pyson_context || '{}'));
- ctx = jQuery.extend(ctx, params.context);
-
- ctx.context = ctx;
- decoder = new Sao.PYSON.Decoder(ctx);
- params.domain = decoder.decode(action.pyson_domain);
- params.order = decoder.decode(action.pyson_order);
- params.search_value = decoder.decode(
- action.pyson_search_value || '[]');
- params.tab_domain = [];
- for (const element of action.domains) {
- params.tab_domain.push(
- [element[0], decoder.decode(element[1]), element[2]]);
- }
- name_prm = jQuery.when(action.name);
- params.model = action.res_model || data.res_model;
- params.res_id = action.res_id || data.res_id;
- params.context_model = action.context_model;
- params.context_domain = action.context_domain;
- if ((action.limit !== undefined) && (action.limit !== null)) {
- params.limit = action.limit;
- }
-
- if (action.keyword) {
- name_prm = add_name_suffix(action.name, params.context);
- }
- return name_prm.then(function(name) {
- params.name = name;
- return Sao.Tab.create(params);
- });
- case 'ir.action.wizard':
- params.action = action.wiz_name;
- params.data = data;
- params.context = context;
- params.window = action.window;
- name_prm = jQuery.when(action.name);
- if ((action.keyword || 'form_action') === 'form_action') {
- name_prm = add_name_suffix(action.name, context);
- }
- return name_prm.then(function(name) {
- params.name = name;
- return Sao.Wizard.create(params);
- });
- case 'ir.action.report':
- params.name = action.report_name;
- params.data = data;
- params.direct_print = action.direct_print;
- params.context = context;
- return Sao.Action.exec_report(params);
- case 'ir.action.url':
- window.open(action.url, '_blank', 'noreferrer,noopener');
- return jQuery.when();
- }
- };
-
- Sao.Action.exec_keyword = function(
- keyword, data, context, warning=true, alwaysask=false) {
- var model_id = data.id;
- var args = {
- 'method': 'model.' + 'ir.action.keyword.get_keyword',
- 'params': [keyword, [data.model, model_id], {}]
- };
- var prm = Sao.rpc(args, Sao.Session.current_session);
- var exec_action = function(actions) {
- var keyact = {};
- for (var i in actions) {
- var action = actions[i];
- keyact[action.name.split(' / ').pop()] = action;
- }
- var prm = Sao.common.selection(
- Sao.i18n.gettext('Select your action'),
- keyact, alwaysask);
- return prm.then(function(action) {
- Sao.Action.exec_action(action, data, context);
- }, function() {
- if (jQuery.isEmptyObject(keyact) && warning) {
- alert(Sao.i18n.gettext('No action defined.'));
- }
- });
- };
- return prm.pipe(exec_action);
- };
-
- Sao.Action.exec_report = function(attributes) {
- if (!attributes.context) {
- attributes.context = {};
- }
- var data = jQuery.extend({}, attributes.data);
- var context = jQuery.extend({}, Sao.Session.current_session.context);
- jQuery.extend(context, attributes.context);
- context.direct_print = attributes.direct_print;
-
- var prm = Sao.rpc({
- 'method': 'report.' + attributes.name + '.execute',
- 'params': [data.ids || [], data, context]
- }, Sao.Session.current_session);
- prm.done(function(result) {
- var report_type = result[0];
- var data = result[1];
- // TODO direct print
- var name = result[3];
-
- var file_name = name + '.' + report_type;
- Sao.common.download_file(data, file_name);
- });
- };
-
- Sao.Action.execute = function(action, data, context, keyword) {
- if (typeof action == 'number') {
- action = Sao.rpc({
- 'method': 'model.ir.action.get_action_value',
- 'params': [action, context],
- }, Sao.Session.current_session, false);
- }
- if (keyword) {
- var keywords = {
- 'ir.action.report': 'form_report',
- 'ir.action.wizard': 'form_action',
- 'ir.action.act_window': 'form_relate'
- };
- if (!action.keyword) {
- action.keyword = keywords[action.type];
- }
- }
- return Sao.Action.exec_action(action, data, context);
- };
-
- Sao.Action.evaluate = function(action, atype, record) {
- action = jQuery.extend({}, action);
- var email = {};
- if ('pyson_email' in action) {
- email = record.expr_eval(action.pyson_email);
- if (jQuery.isEmptyObject(email)) {
- email = {};
- }
- }
- if (!('subject' in email)) {
- email.subject = action.name.replace(/_/g, '');
- }
- action.email = email;
- return action;
- };
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- var ENCODINGS = ["866", "ansi_x3.4-1968", "arabic", "ascii",
- "asmo-708", "big5", "big5-hkscs", "chinese", "cn-big5", "cp1250",
- "cp1251", "cp1252", "cp1253", "cp1254", "cp1255", "cp1256",
- "cp1257", "cp1258", "cp819", "cp866", "csbig5", "cseuckr",
- "cseucpkdfmtjapanese", "csgb2312", "csibm866", "csiso2022jp",
- "csiso2022kr", "csiso58gb231280", "csiso88596e", "csiso88596i",
- "csiso88598e", "csiso88598i", "csisolatin1", "csisolatin2",
- "csisolatin3", "csisolatin4", "csisolatin5", "csisolatin6",
- "csisolatin9", "csisolatinarabic", "csisolatincyrillic",
- "csisolatingreek", "csisolatinhebrew", "cskoi8r", "csksc56011987",
- "csmacintosh", "csshiftjis", "cyrillic", "dos-874", "ecma-114",
- "ecma-118", "elot_928", "euc-jp", "euc-kr", "gb18030", "gb2312",
- "gb_2312", "gb_2312-80", "gbk", "greek", "greek8", "hebrew",
- "hz-gb-2312", "ibm819", "ibm866", "iso-2022-cn", "iso-2022-cn-ext",
- "iso-2022-jp", "iso-2022-kr", "iso-8859-1", "iso-8859-10",
- "iso-8859-11", "iso-8859-13", "iso-8859-14", "iso-8859-15",
- "iso-8859-16", "iso-8859-2", "iso-8859-3", "iso-8859-4",
- "iso-8859-5", "iso-8859-6", "iso-8859-6-e", "iso-8859-6-i",
- "iso-8859-7", "iso-8859-8", "iso-8859-8-e", "iso-8859-8-i",
- "iso-8859-9", "iso-ir-100", "iso-ir-101", "iso-ir-109",
- "iso-ir-110", "iso-ir-126", "iso-ir-127", "iso-ir-138",
- "iso-ir-144", "iso-ir-148", "iso-ir-149", "iso-ir-157", "iso-ir-58",
- "iso8859-1", "iso8859-10", "iso8859-11", "iso8859-13", "iso8859-14",
- "iso8859-15", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5",
- "iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso88591",
- "iso885910", "iso885911", "iso885913", "iso885914", "iso885915",
- "iso88592", "iso88593", "iso88594", "iso88595", "iso88596",
- "iso88597", "iso88598", "iso88599", "iso_8859-1", "iso_8859-15",
- "iso_8859-1:1987", "iso_8859-2", "iso_8859-2:1987", "iso_8859-3",
- "iso_8859-3:1988", "iso_8859-4", "iso_8859-4:1988", "iso_8859-5",
- "iso_8859-5:1988", "iso_8859-6", "iso_8859-6:1987", "iso_8859-7",
- "iso_8859-7:1987", "iso_8859-8", "iso_8859-8:1988", "iso_8859-9",
- "iso_8859-9:1989", "koi", "koi8", "koi8-r", "koi8-ru", "koi8-u",
- "koi8_r", "korean", "ks_c_5601-1987", "ks_c_5601-1989", "ksc5601",
- "ksc_5601", "l1", "l2", "l3", "l4", "l5", "l6", "l9", "latin1",
- "latin2", "latin3", "latin4", "latin5", "latin6", "logical", "mac",
- "macintosh", "ms932", "ms_kanji", "shift-jis", "shift_jis", "sjis",
- "sun_eu_greek", "tis-620", "unicode-1-1-utf-8", "us-ascii",
- "utf-16", "utf-16be", "utf-16le", "utf-8", "utf8", "visual",
- "windows-1250", "windows-1251", "windows-1252", "windows-1253",
- "windows-1254", "windows-1255", "windows-1256", "windows-1257",
- "windows-1258", "windows-31j", "windows-874", "windows-949",
- "x-cp1250", "x-cp1251", "x-cp1252", "x-cp1253", "x-cp1254",
- "x-cp1255", "x-cp1256", "x-cp1257", "x-cp1258", "x-euc-jp", "x-gbk",
- "x-mac-cyrillic", "x-mac-roman", "x-mac-ukrainian", "x-sjis",
- "x-user-defined", "x-x-big5"];
-
- Sao.Window = {};
-
- Sao.Window.InfoBar = Sao.class_(Object, {
- init: function() {
- this.el = jQuery('
', {
- 'class': 'infobar',
- });
- this.__messages = new Set();
- },
- add: function(message, type, kind) {
- kind = kind || null;
- if (!message) {
- return;
- }
- var key = JSON.stringify([message, type]);
- if (!this.__messages.has(key)) {
- var infobar = jQuery('
', {
- 'class': 'alert alert-dismissible alert-' + (
- type || 'error'),
- 'role': 'alert',
- }).append(jQuery('
', {
- 'type': 'button',
- 'class': 'close',
- 'aria-label': Sao.i18n.gettext("Close"),
- 'title': Sao.i18n.gettext("Close"),
- 'data-dismiss': 'alert',
- }).append(jQuery('
', {
- 'aria-hidden': true,
- }).append('×'))
- ).append(jQuery('
')
- .css('white-space','pre-wrap')
- .text(message))
- .on('close.bs.alert',
- null, key, this.__response.bind(this));
- this.el.append(infobar);
- infobar.data('kind', kind);
- }
- },
- __response: function(evt) {
- this.__messages.add(evt.data);
- },
- refresh: function(kind) {
- kind = kind || null;
- this.el.children().each((i, el) => {
- el = jQuery(el);
- if (el.data('kind') === kind) {
- el.remove();
- }
- });
- },
- clear: function() {
- var kinds = this.el.children().each(
- (i, el) => jQuery(el).data('kind'));
- new Set(kinds).forEach(kind => {
- this.refresh(kind);
- });
- this.__messages.clear();
- },
- });
-
- Sao.Window.Form = Sao.class_(Object, {
- init: function(screen, callback, kwargs) {
- kwargs = kwargs || {};
- this.screen = screen;
- this.callback = callback;
- this.many = kwargs.many || 0;
- this.domain = kwargs.domain || null;
- this.context = kwargs.context || null;
- this.save_current = kwargs.save_current;
- var title_prm = jQuery.when(kwargs.title || '').then(
- title => {
- if (screen.breadcrumb.length) {
- var breadcrumb = jQuery.extend([], screen.breadcrumb);
- if (title) {
- breadcrumb.push(title);
- }
- this.title = breadcrumb.slice(-3, -1).map(function(x) {
- return Sao.common.ellipsize(x, 30);
- }).concat(breadcrumb.slice(-1)).join(' › ');
- if (breadcrumb.length > 3) {
- this.title = '... › ' + this.title;
- }
- } else {
- if (!title) {
- title = Sao.common.MODELNAME.get(this.screen.model_name);
- }
- this.title = title;
- }
- var revision = this.screen.context._datetime;
- var label;
- if (revision &&
- Sao.common.MODELHISTORY.contains(this.screen.model_name)) {
- var date_format = Sao.common.date_format(
- this.screen.context.date_format);
- var time_format = '%H:%M:%S.%f';
- var revision_label = ' @ ' + Sao.common.format_datetime(
- date_format + ' ' + time_format, revision);
- label = Sao.common.ellipsize(
- this.title, 80 - revision_label.length) +
- revision_label;
- title = this.title + revision_label;
- } else {
- label = Sao.common.ellipsize(this.title, 80);
- }
- return label;
- });
-
- this.prev_view = screen.current_view;
- this.screen.screen_container.alternate_view = true;
- this.info_bar = new Sao.Window.InfoBar();
- var view_type = kwargs.view_type || 'form';
-
- this.switch_prm = this.screen.switch_view(view_type)
- .done(() => {
- if (kwargs.new_ &&
- (this.screen.current_view.view_type == view_type)) {
- this.screen.new_(undefined, kwargs.defaults);
- }
- });
- var dialog = new Sao.Dialog('', 'window-form', 'lg', false);
- this.dialog = dialog;
- this.el = dialog.modal;
- this.el.on('keydown', e => {
- if (e.which == Sao.common.ESC_KEYCODE) {
- e.preventDefault();
- this.response('RESPONSE_CANCEL');
- }
- });
-
- var readonly = this.screen.group.readonly;
-
- this.but_ok = null;
- this.but_new = null;
-
- this._initial_value = null;
- this.view_type = view_type;
- if ((view_type == 'form') && !readonly) {
- let label;
- if (kwargs.new_) {
- label = Sao.i18n.gettext('Delete');
- } else {
- label = Sao.i18n.gettext("Discard changes");
- var record = this.screen.current_record;
- this._initial_value = record.get_on_change_value();
- if (record.group.parent &&
- record.model.fields[record.group.parent_name]) {
- var parent_field = record.model.fields[
- record.group.parent_name];
- this._initial_value[record.group.parent_name] = (
- parent_field.get_eval(record));
- }
- }
-
- dialog.footer.append(jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': label,
- }).text(label).click(() => {
- this.response('RESPONSE_CANCEL');
- }));
- }
-
- if (kwargs.new_ && this.many) {
- let label;
- if (this.save_current && !readonly) {
- label = Sao.i18n.gettext("Save and New");
- } else {
- label = Sao.i18n.gettext("Add and New");
- }
- dialog.footer.append(jQuery('
', {
- 'class': 'btn btn-default',
- 'type': 'button',
- 'title': label,
- }).text(label).click(() => {
- this.response('RESPONSE_ACCEPT');
- }));
- }
-
- if (this.save_current && !readonly) {
- let label = Sao.i18n.gettext("Save");
- this.but_ok = jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': label,
- }).text(label).appendTo(dialog.footer);
- } else {
- let label;
- if (readonly || (view_type == 'tree')) {
- label = Sao.i18n.gettext("Close");
- } else if (kwargs.new_) {
- label = Sao.i18n.gettext("Add");
- } else {
- label = Sao.i18n.gettext("Apply changes");
- }
- this.but_ok = jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': label,
- }).text(label).appendTo(dialog.footer);
- }
- dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- if (view_type == 'tree') {
- var menu = jQuery('
', {
- 'class': 'window-form-toolbar'
- }).appendTo(dialog.body);
- var group = jQuery('
', {
- 'class': 'input-group input-group-sm'
- }).appendTo(menu);
-
- this.wid_text = jQuery('
', {
- type: 'input'
- }).appendTo(menu);
- this.wid_text.hide();
-
- var buttons = jQuery('
', {
- 'class': 'input-group-btn'
- }).appendTo(group);
- var access = Sao.common.MODELACCESS.get(this.screen.model_name);
-
- var disable_during = function(callback) {
- return function(evt) {
- var button = jQuery(evt.target);
- button.prop('disabled', true);
- (callback(evt) || jQuery.when())
- .always(function() {
- button.prop('disabled', false);
- });
- };
- };
-
- this.but_switch = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Switch"),
- 'title': Sao.i18n.gettext("Switch"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-switch')
- ).appendTo(buttons);
- this.but_switch.click(
- disable_during(this.switch_.bind(this)));
-
- this.but_previous = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Previous"),
- 'title': Sao.i18n.gettext("Previous"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-back')
- ).appendTo(buttons);
- this.but_previous.click(
- disable_during(this.previous.bind(this)));
-
- this.label = jQuery('
', {
- 'class': 'badge'
- }).appendTo(jQuery('
', {
- 'class': 'btn hidden-xs',
- }).appendTo(buttons));
-
- this.but_next = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Next"),
- 'title': Sao.i18n.gettext("Next"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-forward')
- ).appendTo(buttons);
- this.but_next.click(disable_during(this.next.bind(this)));
-
- if (this.domain) {
- this.wid_text.show();
-
- this.but_add = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Add"),
- 'title': Sao.i18n.gettext("Add"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-add')
- ).appendTo(buttons);
- this.but_add.click(disable_during(this.add.bind(this)));
- this.but_add.prop('disabled', !access.read || readonly);
-
- this.but_remove = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Remove"),
- 'title': Sao.i18n.gettext("Remove"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-remove')
- ).appendTo(buttons);
- this.but_remove.click(
- disable_during(this.remove.bind(this)));
- this.but_remove.prop('disabled', !access.read || readonly);
- }
-
- this.but_new = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("New"),
- 'title': Sao.i18n.gettext("New"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-create')
- ).appendTo(buttons);
- this.but_new.click(disable_during(this.new_.bind(this)));
- this.but_new.prop('disabled', !access.create || readonly);
-
- this.but_del = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Delete"),
- 'title': Sao.i18n.gettext("Delete"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-delete')
- ).appendTo(buttons);
- this.but_del.click(disable_during(this.delete_.bind(this)));
- this.but_del.prop('disabled', !access['delete'] || readonly);
-
- this.but_undel = jQuery('
', {
- 'class': 'btn btn-default btn-sm',
- 'type': 'button',
- 'aria-label': Sao.i18n.gettext("Undelete"),
- 'title': Sao.i18n.gettext("Undelete"),
- }).append(Sao.common.ICONFACTORY.get_icon_img('tryton-undo')
- ).appendTo(buttons);
- this.but_undel.click(disable_during(this.undelete.bind(this)));
- this.but_undel.prop('disabled', !access['delete'] || readonly);
- }
-
- var content = jQuery('
').appendTo(dialog.body);
-
- dialog.body.append(this.info_bar.el);
-
- this.screen.windows.push(this);
-
- this.switch_prm.done(() => {
- if (this.screen.current_view.view_type != view_type) {
- this.destroy();
- } else {
- title_prm.done(dialog.add_title.bind(dialog));
- content.append(this.screen.screen_container.alternate_viewport);
- this.el.modal('show');
- }
- });
- this.el.on('shown.bs.modal', event => {
- this.screen.display().done(() => {
- this.screen.set_cursor();
- });
- });
- this.el.on('hidden.bs.modal', function(event) {
- jQuery(this).remove();
- });
- },
- record_message: function(position, size) {
- if (this.view_type != 'tree') {
- return;
- }
- var name = '_';
- var access = Sao.common.MODELACCESS.get(this.screen.model_name);
- var deletable = this.screen.deletable;
- var readonly = this.screen.group.readonly;
- if (position >= 1) {
- name = position;
- if (this.domain) {
- this.but_remove.prop('disabled', false);
- }
- this.but_next.prop('disabled', position >= size);
- this.but_previous.prop('disabled', position <= 1);
- this.but_del.prop(
- 'disabled',
- readonly ||
- !access.delete ||
- !deletable);
- this.but_undel.prop('disabled', readonly);
- } else {
- this.but_del.prop('disabled', true);
- this.but_undel.prop('disabled', true);
- this.but_next.prop('disabled', true);
- this.but_previous.prop('disabled', true);
- if (this.domain) {
- this.but_remove.prop('disabled', true);
- }
- }
- var message = name + '/' + size;
- this.label.text(message).attr('title', message);
- },
- record_modified: function() {
- this.info_bar.refresh();
- },
- add: function() {
- var domain = jQuery.extend([], this.domain);
- var model_name = this.screen.model_name;
- var value = this.wid_text.val();
-
- const callback = result => {
- var prm = jQuery.when();
- if (!jQuery.isEmptyObject(result)) {
- var ids = [];
- for (const record of result) {
- ids.push(record[0]);
- }
- this.screen.group.load(ids, true, -1, null);
- prm = this.screen.display();
- }
- prm.done(() => {
- this.screen.set_cursor();
- });
- this.entry.val('');
- };
- var parser = new Sao.common.DomainParser();
- new Sao.Window.Search(model_name, callback, {
- sel_multi: true,
- context: this.context,
- domain: domain,
- search_filter: parser.quote(value)
- });
- },
- remove: function() {
- this.screen.remove(false, true, false);
- },
- new_: function() {
- this.screen.new_();
- this._initial_value = null;
- this.many -= 1;
- if (this.many == 0) {
- this.but_new.addClass('disabled');
- }
- },
- delete_: function() {
- this.screen.remove(false, false, false);
- },
- undelete: function() {
- this.screen.unremove();
- },
- previous: function() {
- return this.screen.display_previous();
- },
- next: function() {
- return this.screen.display_next();
- },
- switch_: function() {
- return this.screen.switch_view();
- },
- response: function(response_id) {
- var result;
- this.screen.current_view.set_value();
- var readonly = this.screen.group.readonly;
- if (~['RESPONSE_OK', 'RESPONSE_ACCEPT'].indexOf(response_id) &&
- !readonly &&
- this.screen.current_record) {
- this.screen.current_record.validate().then(validate => {
- if (validate && this.screen.attributes.pre_validate) {
- return this.screen.current_record.pre_validate().then(
- () => true, () => false);
- }
- return validate;
- }).then(validate => {
- var closing_prm = jQuery.Deferred();
- if (validate && this.save_current) {
- this.screen.save_current().then(closing_prm.resolve,
- closing_prm.reject);
- } else if (validate &&
- this.screen.current_view.view_type == 'form') {
- var view = this.screen.current_view;
- var validate_prms = [];
- for (var name in view.widgets) {
- var widget = view.widgets[name];
- if (widget.screen &&
- widget.screen.attributes.pre_validate) {
- var record = widget.screen.current_record;
- if (record) {
- validate_prms.push(record.pre_validate());
- }
- }
- }
- jQuery.when.apply(jQuery, validate_prms).then(
- closing_prm.resolve, closing_prm.reject);
- } else if (!validate) {
- this.info_bar.add(
- this.screen.invalid_message(), 'danger');
- closing_prm.reject();
- } else {
- this.info_bar.clear();
- closing_prm.resolve();
- }
-
- closing_prm.fail(() => {
- this.screen.display().done(() => {
- this.screen.set_cursor();
- });
- });
-
- // TODO Add support for many
- closing_prm.done(() => {
- if (response_id == 'RESPONSE_ACCEPT') {
- this.screen.new_();
- this.screen.current_view.display().done(() => {
- this.screen.set_cursor();
- });
- this.many -= 1;
- if (this.many === 0) {
- this.but_new.prop('disabled', true);
- }
- } else {
- result = true;
- if (this.callback) {
- this.callback(result);
- }
- this.destroy();
- }
- });
- });
- return;
- }
-
- var cancel_prm = null;
- if (response_id == 'RESPONSE_CANCEL' &&
- !readonly &&
- this.screen.current_record) {
- result = false;
- var record = this.screen.current_record;
- var added = record.modified_fields.id;
- if ((record.id < 0) || this.save_current) {
- cancel_prm = this.screen.cancel_current(
- this._initial_value);
- } else if (record.modified) {
- record.cancel();
- cancel_prm = record.reload().then(() => {
- this.screen.display();
- });
- }
- if (added) {
- record.modified_fields.id = added;
- }
- } else {
- result = (response_id != 'RESPONSE_CANCEL') && !readonly;
- }
- (cancel_prm || jQuery.when()).then(() => {
- if (this.callback) {
- this.callback(result);
- }
- this.destroy();
- });
- },
- destroy: function() {
- this.screen.windows.splice(this.screen.windows.indexOf(this), 1);
- this.screen.screen_container.alternate_view = false;
- this.screen.screen_container.alternate_viewport.children()
- .detach();
- if (this.prev_view) {
- // Empty when opening from Many2One
- this.screen.switch_view(this.prev_view.view_type);
- }
- this.el.modal('hide');
- }
- });
-
- Sao.Window.Attachment = Sao.class_(Sao.Window.Form, {
- init: function(record, callback) {
- this.resource = record.model.name + ',' + record.id;
- this.attachment_callback = callback;
- var context = jQuery.extend({}, record.get_context());
- var screen = new Sao.Screen('ir.attachment', {
- domain: [['resource', '=', this.resource]],
- mode: ['tree', 'form'],
- context: context,
- });
- var title = record.rec_name().then(function(rec_name) {
- return Sao.i18n.gettext('Attachments (%1)', rec_name);
- });
- Sao.Window.Attachment._super.init.call(this, screen, this.callback,
- {view_type: 'tree', title: title});
- this.switch_prm = this.switch_prm.then(function() {
- return screen.search_filter();
- });
- },
- callback: function(result) {
- var prm = jQuery.when();
- if (result) {
- prm = this.screen.save_current();
- }
- if (this.attachment_callback) {
- prm.always(this.attachment_callback.bind(this));
- }
- },
- add_data: function(data, filename) {
- var screen = this.screen;
- this.switch_prm.then(function() {
- screen.new_().then(function(record) {
- var data_field = record.model.fields.data;
- record.field_set_client(
- data_field.description.filename, filename);
- record.field_set_client('data', data);
- screen.display();
- });
- });
- },
- add_uri: function(uri) {
- var screen = this.screen;
- this.switch_prm.then(function() {
- screen.current_record = null;
- screen.switch_view('form').then(function() {
- screen.new_().then(function(record) {
- record.field_set_client('link', uri);
- record.field_set_client('type', 'link');
- screen.display();
- });
- });
- });
- },
- add_text: function(text) {
- var screen = this.screen;
- this.switch_prm.then(function() {
- screen.current_record = null;
- screen.switch_view('form').then(function() {
- screen.new_().then(function(record) {
- record.field_set_client('description', text);
- screen.display();
- });
- });
- });
- },
- });
- Sao.Window.Attachment.get_attachments = function(record) {
- var prm;
- if (record && (record.id >= 0)) {
- var context = record.get_context();
- prm = Sao.rpc({
- 'method': 'model.ir.attachment.search_read',
- 'params': [
- [['resource', '=', record.model.name + ',' + record.id]],
- 0, 20, null, ['rec_name', 'name', 'type', 'link'],
- context],
- }, record.model.session);
- } else {
- prm = jQuery.when([]);
- }
- var partial = function(callback, attachment, context, session) {
- return function() {
- return callback(attachment, context, session);
- };
- };
- return prm.then(function(attachments) {
- return attachments.map(function(attachment) {
- var name = attachment.rec_name;
- if (attachment.type == 'link') {
- return [name, attachment.link];
- } else {
- var callback = Sao.Window.Attachment[
- 'open_' + attachment.type];
- return [name, partial(
- callback, attachment, context, record.model.session)];
- }
- });
- });
- };
- Sao.Window.Attachment.open_data = function(attachment, context, session) {
- Sao.rpc({
- 'method': 'model.ir.attachment.read',
- 'params': [
- [attachment.id], ['data'], context],
- }, session).then(function(values) {
- Sao.common.download_file(values[0].data, attachment.name);
- });
- };
-
- Sao.Window.Note = Sao.class_(Sao.Window.Form, {
- init: function(record, callback) {
- this.resource = record.model.name + ',' + record.id;
- this.note_callback = callback;
- var context = jQuery.extend({}, record.get_context());
- var screen = new Sao.Screen('ir.note', {
- domain: [['resource', '=', this.resource]],
- mode: ['tree', 'form'],
- context: context,
- });
- var title = record.rec_name().then(function(rec_name) {
- return Sao.i18n.gettext('Notes (%1)', rec_name);
- });
- Sao.Window.Note._super.init.call(this, screen, this.callback,
- {view_type: 'tree', title: title});
- this.switch_prm = this.switch_prm.then(function() {
- return screen.search_filter();
- });
- },
- callback: function(result) {
- var prm = jQuery.when();
- if (result) {
- var unread = this.screen.group.model.fields.unread;
- for (const record of this.screen.group) {
- if (record.get_loaded() || record.id < 0) {
- if (!record.modified_fields.unread) {
- unread.set_client(record, false);
- }
- }
- }
- prm = this.screen.save_current();
- }
- if (this.note_callback) {
- prm.always(this.note_callback.bind(this));
- }
- }
- });
-
- Sao.Window.Log = Sao.class_(Sao.Window.Form, {
- init: function(record) {
- this.resource = record.model.name + ',' + record.id;
- var title = record.rec_name().then(function(rec_name) {
- return Sao.i18n.gettext("Logs (%1)", rec_name);
- });
- var context = jQuery.extend({}, record.get_context());
-
- var form = jQuery('
', {
- 'class': 'form',
- });
- var table = jQuery('
', {
- 'class': 'form-container responsive responsive-noheader',
- }).appendTo(form);
- var body = jQuery('
').appendTo(table);
-
- record.model.execute(
- 'read', [[record.id],
- ['create_uid.rec_name', 'create_date',
- 'write_uid.rec_name', 'write_date',
- 'xml_id']], context
- ).then(([log]) => {
- var row, cell, label, input;
-
- row = jQuery('
|
').appendTo(body);
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': Sao.i18n.gettext("Model:"),
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- input = jQuery('
', {
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(record.model.name).appendTo(cell);
- input.uniqueId();
-
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': Sao.i18n.gettext("ID:"),
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- input = jQuery('
', {
- 'type': 'integer',
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(record.id).appendTo(cell);
- input.css('text-align', 'end');
- input.uniqueId();
-
- if (log.xml_id) {
- const [module, xml_id] = log.xml_id.split('.', 2);
-
- row = jQuery('
|
').appendTo(body);
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': Sao.i18n.gettext("Module:"),
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- input = jQuery('
', {
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(module).appendTo(cell);
- input.uniqueId();
-
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': Sao.i18n.gettext("XML ID:"),
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- input = jQuery('
', {
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(xml_id).appendTo(cell);
- input.uniqueId();
- }
-
- [['create_uid.', Sao.i18n.gettext("Created by:"),
- 'create_date', Sao.i18n.gettext("Created at:")],
- ['write_uid.', Sao.i18n.gettext("Last Modified by:"),
- 'write_date', Sao.i18n.gettext("Last Modified at:")],
- ].forEach(([user, user_label, date, date_label]) => {
-
- row = jQuery('
|
').appendTo(body);
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': user_label,
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- user = log[user];
- if (user) {
- user = user.rec_name;
- }
- input = jQuery('
', {
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(user || '').appendTo(cell);
- input.css('width', '50ch');
- input.uniqueId();
-
- cell = jQuery('
| ').css('text-align', 'end').appendTo(row);
- label = jQuery('
', {
- 'class': 'form-label',
- 'text': date_label,
- }).appendTo(cell);
- label.uniqueId();
- cell = jQuery('
| ').appendTo(row);
- date = log[date];
- if (date) {
- date = Sao.common.format_datetime(
- Sao.common.date_format() + ' %H:%M:%S', date);
- }
- input = jQuery('
', {
- 'readonly': true,
- 'class': 'form-control input-sm',
- 'aria-labelledby': label.attr('id'),
- }).val(date || '').appendTo(cell);
- input.css('width', date.length + 'ch');
- input.uniqueId();
- });
- });
-
- var screen = new Sao.Screen('ir.model.log', {
- domain: [['resource', '=', this.resource]],
- mode: ['tree', 'form'],
- context: context,
- });
- Sao.Window.Log._super.init.call(
- this, screen, null, {view_type: 'tree', title: title});
- this.switch_prm = this.switch_prm.then(function() {
- return screen.search_filter();
- });
-
- this.dialog.body.prepend(form);
- },
- });
-
- Sao.Window.Search = Sao.class_(Object, {
- init: function(model, callback, kwargs) {
- kwargs = kwargs || {};
- var views_preload = kwargs.views_preload || {};
- this.model_name = model;
- this.domain = kwargs.domain || [];
- this.context = kwargs.context || {};
- this.order = kwargs.order || null;
- this.view_ids = kwargs.view_ids;
- this.views_preload = views_preload;
- this.sel_multi = kwargs.sel_multi;
- this.callback = callback;
- var title = kwargs.title;
- if (!title) {
- title = Sao.common.MODELNAME.get(model);
- }
- this.title = title;
- this.exclude_field = kwargs.exclude_field || null;
- var dialog = new Sao.Dialog(Sao.i18n.gettext(
- 'Search %1', this.title), '', 'lg', false);
- this.el = dialog.modal;
- this.el.on('keydown', e => {
- if (e.which == Sao.common.ESC_KEYCODE) {
- e.preventDefault();
- this.response('RESPONSE_CANCEL');
- }
- });
-
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Cancel"),
- }).text(Sao.i18n.gettext('Cancel')).click(() => {
- this.response('RESPONSE_CANCEL');
- }).appendTo(dialog.footer);
- jQuery('
', {
- 'class': 'btn btn-default',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Find"),
- }).text(Sao.i18n.gettext('Find')).click(() => {
- this.response('RESPONSE_APPLY');
- }).appendTo(dialog.footer);
- if (kwargs.new_ && Sao.common.MODELACCESS.get(model).create) {
- jQuery('
', {
- 'class': 'btn btn-default',
- 'type': 'button',
- 'title': Sao.i18n.gettext("New"),
- }).text(Sao.i18n.gettext('New')).click(() => {
- this.response('RESPONSE_ACCEPT');
- }).appendTo(dialog.footer);
- }
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("OK"),
- }).text(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
- dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- this.screen = new Sao.Screen(model, {
- mode: ['tree'],
- context: this.context,
- domain: this.domain,
- order: this.order,
- view_ids: kwargs.view_ids,
- views_preload: views_preload,
- row_activate: this.activate.bind(this),
- readonly: true,
- breadcrumb: [this.title],
- });
- this.screen.load_next_view().done(() => {
- this.screen.switch_view().done(() => {
- if (!this.sel_multi) {
- this.screen.current_view.selection_mode = (
- Sao.common.SELECTION_SINGLE);
- } else {
- this.screen.current_view.selection_mode = (
- Sao.common.SELECTION_MULTIPLE);
- }
- dialog.body.append(this.screen.screen_container.el);
- this.el.modal('show');
- this.screen.display();
- if (kwargs.search_filter !== undefined) {
- this.screen.search_filter(kwargs.search_filter);
- }
- });
- });
- this.el.on('hidden.bs.modal', function(event) {
- jQuery(this).remove();
- });
- },
- activate: function() {
- this.response('RESPONSE_OK');
- },
- response: function(response_id) {
- var records;
- var value = [];
- if (response_id == 'RESPONSE_OK') {
- records = this.screen.current_view.selected_records;
- } else if (response_id == 'RESPONSE_APPLY') {
- this.screen.search_filter(
- this.screen.screen_container.get_text());
- return;
- } else if (response_id == 'RESPONSE_ACCEPT') {
- var view_ids = jQuery.extend([], this.view_ids);
- if (!jQuery.isEmptyObject(view_ids)) {
- // Remove the first tree view as mode is form only
- view_ids.shift();
- }
- var screen = new Sao.Screen(this.model_name, {
- domain: this.domain,
- context: this.context,
- order: this.order,
- mode: ['form'],
- view_ids: view_ids,
- views_preload: this.views_preload,
- exclude_field: this.exclude_field,
- });
-
- var callback = function(result) {
- if (result) {
- var record = screen.current_record;
- this.callback([[record.id,
- record._values.rec_name || '']]);
- } else {
- this.callback(null);
- }
- };
- this.el.modal('hide');
- new Sao.Window.Form(screen, callback.bind(this), {
- new_: true,
- save_current: true,
- });
- return;
- }
- if (records) {
- var index, record;
- for (index in records) {
- record = records[index];
- value.push([record.id, record._values.rec_name || '']);
- }
- }
- this.callback(value);
- this.el.modal('hide');
- }
- });
-
- Sao.Window.Preferences = Sao.class_(Object, {
- init: function(callback) {
- var dialog = new Sao.Dialog('Preferences', '', 'lg');
- this.el = dialog.modal;
-
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Cancel"),
- }).text(Sao.i18n.gettext('Cancel')).click(() => {
- this.response('RESPONSE_CANCEL');
- }).appendTo(dialog.footer);
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("OK"),
- }).text(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
- dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- this.screen = new Sao.Screen('res.user', {
- mode: []
- });
- // Reset readonly set automaticly by MODELACCESS
- this.screen.attributes.readonly = false;
- this.screen.group.readonly = false;
- this.screen.group.skip_model_access = true;
-
- const set_view = view => {
- this.screen.add_view(view);
- this.screen.switch_view().done(() => {
- this.screen.new_(false);
- this.screen.model.execute('get_preferences', [false], {})
- .then(set_preferences, this.destroy);
- });
- };
- const set_preferences = preferences => {
- this.screen.current_record.cancel();
- this.screen.current_record.set(preferences);
- this.screen.current_record.id =
- this.screen.model.session.user_id;
- this.screen.current_record.validate(null, true).then(() => {
- this.screen.display(true);
- });
- dialog.body.append(this.screen.screen_container.el);
- this.el.modal('show');
- };
- this.el.on('hidden.bs.modal', function(event) {
- callback();
- jQuery(this).remove();
- });
-
- this.screen.model.execute('get_preferences_fields_view', [], {})
- .then(set_view, this.destroy);
- },
- response: function(response_id) {
- const end = () => {
- this.destroy();
- };
- var prm = jQuery.when();
- if (response_id == 'RESPONSE_OK') {
- prm = this.screen.current_record.validate()
- .then(validate => {
- if (validate) {
- var values = jQuery.extend({}, this.screen.get());
- return this.screen.model.execute(
- 'set_preferences', [values], {});
- }
- });
- }
- prm.done(end);
- },
- destroy: function() {
- this.el.modal('hide');
- }
- });
-
- Sao.Window.Revision = Sao.class_(Object, {
- init: function(revisions, revision, callback) {
- this.callback = callback;
- var dialog = new Sao.Dialog(
- Sao.i18n.gettext('Revision'), '', 'lg');
- this.el = dialog.modal;
-
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Cancel"),
- }).text(Sao.i18n.gettext('Cancel')).click(() => {
- this.response('RESPONSE_CANCEL');
- }).appendTo(dialog.footer);
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("OK"),
- }).text(Sao.i18n.gettext('OK')).appendTo(dialog.footer);
- dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- var group = jQuery('
', {
- 'class': 'form-group'
- }).appendTo(dialog.body);
- jQuery('
', {
- 'for': 'revision',
- 'text': 'Revision'
- }).appendTo(group);
- this.select = jQuery('
', {
- 'class': 'form-control',
- id: 'revision',
- 'placeholder': Sao.i18n.gettext('Revision')
- }).appendTo(group);
- var date_format = Sao.common.date_format();
- var time_format = '%H:%M:%S.%f';
- this.select.append(jQuery('
', {
- value: null,
- text: ''
- }));
- for (let rev of revisions) {
- var name = rev[2];
- rev = rev[0];
- this.select.append(jQuery('
', {
- value: rev.valueOf(),
- text: Sao.common.format_datetime(
- date_format + ' ' + time_format, rev) + ' ' + name,
- }));
- }
- if (revision) {
- this.select.val(revision.valueOf());
- }
- this.el.modal('show');
- this.el.on('hidden.bs.modal', function(event) {
- jQuery(this).remove();
- });
- },
- response: function(response_id) {
- var revision = null;
- if (response_id == 'RESPONSE_OK') {
- revision = this.select.val();
- if (revision) {
- revision = Sao.DateTime(parseInt(revision, 10));
- }
- }
- this.el.modal('hide');
- this.callback(revision);
- }
- });
-
- Sao.Window.CSV = Sao.class_(Object, {
- init: function(title) {
- this.dialog = new Sao.Dialog(title, 'csv', 'lg');
- this.el = this.dialog.modal;
-
- this.fields = {};
- this.fields_model = {};
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Cancel"),
- }).text(Sao.i18n.gettext('Cancel')).click(() => {
- this.response('RESPONSE_CANCEL');
- }).appendTo(this.dialog.footer);
-
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("OK"),
- }).text(Sao.i18n.gettext('OK')).click(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- }).appendTo(this.dialog.footer);
-
- var row_fields = jQuery('
', {
- 'class': 'row'
- }).appendTo(this.dialog.body);
-
- var column_fields_all = jQuery('
', {
- 'class': 'col-md-5',
- }).append(jQuery('
', {
- 'class': 'panel panel-default',
- }).append(jQuery('
', {
- 'class': 'panel-heading',
- }).append(jQuery('
', {
- 'class': 'panel-title',
- 'text': Sao.i18n.gettext('All Fields')
- })))).appendTo(row_fields);
-
- this.fields_all = jQuery('
', {
- 'class': 'list-unstyled column-fields panel-body'
- }).css('cursor', 'pointer')
- .appendTo(column_fields_all.find('.panel'));
-
- this.model_populate(this._get_fields(this.screen.model_name));
- this.view_populate(this.fields_model, this.fields_all);
-
- this.column_buttons = jQuery('
', {
- 'class': 'col-md-2'
- }).appendTo(row_fields);
-
- jQuery('
', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Add"),
- }).text(' ' + Sao.i18n.gettext('Add')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-add')
- ).click(() => {
- this.fields_all.find('.bg-primary').each((i, el_field) => {
- this.sig_sel_add(el_field);
- });
- })
- .appendTo(this.column_buttons);
-
- jQuery('
', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Remove"),
- }).text(' ' + Sao.i18n.gettext('Remove')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-remove')
- ).click(() => {
- // sig_unsel
- this.fields_selected.children('li.bg-primary').remove();
- })
- .appendTo(this.column_buttons);
-
- jQuery('
', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Clear"),
- }).text(' ' + Sao.i18n.gettext('Clear')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-clear')
- ).click(() => {
- this.fields_selected.empty();
- })
- .appendTo(this.column_buttons);
-
- jQuery('
').appendTo(this.column_buttons);
-
- var column_fields_selected = jQuery('
', {
- 'class': 'col-md-5',
- }).append(jQuery('
', {
- 'class': 'panel panel-default',
- }).append(jQuery('
', {
- 'class': 'panel-heading',
- }).append(jQuery('
', {
- 'class': 'panel-title',
- 'text': Sao.i18n.gettext('Fields Selected')
- })))).appendTo(row_fields);
-
- this.fields_selected = jQuery('
', {
- 'class': 'list-unstyled column-fields panel-body',
- }).css('cursor', 'pointer')
- .appendTo(column_fields_selected.find('.panel'));
-
- this.chooser_form = jQuery('
', {
- 'class': 'form-inline'
- }).appendTo(this.dialog.body);
-
- var row_csv_param = jQuery('
', {
- }).appendTo(this.dialog.body);
-
- jQuery('
', {
- 'text': Sao.i18n.gettext('CSV Parameters')
- }).append(jQuery('
', {
- 'class': 'caret',
- }).html(' '))
- .css('cursor', 'pointer')
- .on('click', () => {
- this.expander_csv.collapse('toggle');
- }).appendTo(row_csv_param);
-
- this.expander_csv = jQuery('
', {
- 'id': 'expander_csv',
- 'class': 'collapse form-inline'
- }).appendTo(row_csv_param);
-
- var delimiter_label = jQuery('
', {
- 'text': Sao.i18n.gettext('Delimiter:'),
- 'class': 'control-label',
- 'for': 'input-delimiter'
- });
-
- var separator = ',';
- if (navigator.platform &&
- navigator.platform.slice(0, 3) == 'Win') {
- separator = ';';
- }
- this.el_csv_delimiter = jQuery('
', {
- 'type': 'text',
- 'class': 'form-control',
- 'id': 'input-delimiter',
- 'size': '1',
- 'maxlength': '1',
- 'value': separator
- });
-
- jQuery('
', {
- 'class': 'form-group'
- }).append(delimiter_label)
- .append(this.el_csv_delimiter)
- .appendTo(this.expander_csv);
- this.expander_csv.append(' ');
-
- var quotechar_label = jQuery('
', {
- 'text': Sao.i18n.gettext('Quote Char:'),
- 'class': 'control-label',
- 'for': 'input-quotechar'
- });
-
- this.el_csv_quotechar = jQuery('
', {
- 'type': 'text',
- 'class': 'form-control',
- 'id': 'input-quotechar',
- 'size': '1',
- 'maxlength': '1',
- 'value': '"',
- });
-
- jQuery('
', {
- 'class': 'form-group'
- }).append(quotechar_label)
- .append(this.el_csv_quotechar)
- .appendTo(this.expander_csv);
- this.expander_csv.append(' ');
-
- this.el.modal('show');
- this.el.on('hidden.bs.modal', function() {
- jQuery(this).remove();
- });
- },
- _get_fields: function(model) {
- return Sao.rpc({
- 'method': 'model.' + model + '.fields_get'
- }, this.session, false);
- },
- on_row_expanded: function(node) {
- var container_view = jQuery('
').css('list-style', 'none')
- .insertAfter(node.view);
- this.children_expand(node);
- this.view_populate(node.children, container_view);
- },
- destroy: function() {
- this.el.modal('hide');
- }
- });
-
- Sao.Window.Import = Sao.class_(Sao.Window.CSV, {
- init: function(name, screen) {
- this.name = name;
- this.screen = screen;
- this.session = Sao.Session.current_session;
- this.fields_data = {}; // Ask before Removing this.
- this.fields_invert = {};
- Sao.Window.Import._super.init.call(this,
- Sao.i18n.gettext('CSV Import: %1', name));
-
- jQuery('
', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Auto-Detect"),
- }).text(' ' + Sao.i18n.gettext('Auto-Detect')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-search')
- ).click(() => {
- this.autodetect();
- })
- .appendTo(this.column_buttons);
-
- var chooser_label = jQuery('
', {
- 'text': Sao.i18n.gettext('File to Import'),
- 'class': 'col-sm-6 control-label',
- 'for': 'input-csv-file'
- });
-
- this.file_input = jQuery('
', {
- 'type': 'file',
- 'id': 'input-csv-file'
- });
-
- jQuery('
', {
- 'class': 'form-group'
- }).append(chooser_label).append(jQuery('
', {
- 'class': 'col-sm-6'
- }).append(this.file_input))
- .appendTo(this.chooser_form);
-
- var encoding_label = jQuery('
', {
- 'text': Sao.i18n.gettext('Encoding:'),
- 'class': 'control-label',
- 'for': 'input-encoding'
- });
-
- this.el_csv_encoding = jQuery('
', {
- 'class': 'form-control',
- 'id': 'input-encoding'
- });
-
- for (const encoding of ENCODINGS) {
- jQuery('
', {
- 'val': encoding,
- }).append(encoding).appendTo(this.el_csv_encoding);
- }
-
- var enc = 'utf-8';
- if (navigator.platform &&
- navigator.platform.slice(0, 3) == 'Win') {
- enc = 'cp1252';
- }
- this.el_csv_encoding.children('option[value="' + enc + '"]')
- .attr('selected', 'selected');
-
- jQuery('
', {
- 'class': 'form-group'
- }).append(encoding_label)
- .append(this.el_csv_encoding)
- .appendTo(this.expander_csv);
- this.expander_csv.append(' ');
-
- var skip_label = jQuery('
', {
- 'text': Sao.i18n.gettext('Lines to Skip:'),
- 'class': 'control-label',
- 'for': 'input-skip'
- });
-
- this.el_csv_skip = jQuery('
', {
- 'type': 'number',
- 'class': 'form-control',
- 'id': 'input-skip',
- 'value': '0'
- });
-
- jQuery('
', {
- 'class': 'form-group'
- }).append(skip_label)
- .append(this.el_csv_skip)
- .appendTo(this.expander_csv);
- this.expander_csv.append(' ');
- Sortable.create(this.fields_selected.get(0), {
- handle: '.draggable-handle',
- ghostClass: 'dragged-row'
- });
- },
- sig_sel_add: function(el_field) {
- el_field = jQuery(el_field);
- this._add_node(el_field.attr('field'), el_field.attr('name'));
- },
- _add_node: function(field, name) {
- jQuery('
', {
- 'field': field,
- 'class': 'draggable-handle',
- }).text(name).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-drag')
- ).click(function(evt) {
- const node = jQuery(evt.target);
- if (evt.ctrlKey || evt.metaKey) {
- node.toggleClass('bg-primary');
- } else {
- node.addClass('bg-primary');
- node.siblings().removeClass('bg-primary');
- }
- }).appendTo(this.fields_selected);
- },
- view_populate: function (parent_node, parent_view) {
- var fields_order = Object.keys(parent_node).sort(function(a,b) {
- if (parent_node[b].string < parent_node[a].string) {
- return -1;
- }
- else {
- return 1;
- }
- }).reverse();
-
- fields_order.forEach(field => {
- var name = parent_node[field].string || field;
- var node = jQuery('
', {
- 'field': parent_node[field].field,
- 'name': parent_node[field].name
- }).text(name).click(e => {
- if (e.ctrlKey || e.metaKey) {
- node.toggleClass('bg-primary');
- } else {
- this.fields_all.find('li').removeClass('bg-primary');
- node.addClass('bg-primary');
- }
- }).appendTo(parent_view);
- parent_node[field].view = node;
- var expander_icon = Sao.common.ICONFACTORY
- .get_icon_img('tryton-arrow-right')
- .data('expanded', false)
- .click(e => {
- e.stopPropagation();
- var icon;
- var expanded = expander_icon.data('expanded');
- expander_icon.data('expanded', !expanded);
- if (expanded) {
- icon = 'tryton-arrow-right';
- node.next('ul').remove();
- } else {
- icon = 'tryton-arrow-down';
- this.on_row_expanded(parent_node[field]);
- }
- Sao.common.ICONFACTORY.get_icon_url(icon)
- .then(function(url) {
- expander_icon.attr('src', url);
- });
- }).prependTo(node);
- expander_icon.css(
- 'visibility',
- parent_node[field].relation ? 'visible' : 'hidden');
- });
- },
- model_populate: function (fields, parent_node, prefix_field,
- prefix_name) {
- parent_node = parent_node || this.fields_model;
- prefix_field = prefix_field || '';
- prefix_name = prefix_name || '';
-
- for (const field of Object.keys(fields)) {
- if(!fields[field].readonly || field == 'id') {
- var name = fields[field].string || field;
- name = prefix_name + name;
- // Only One2Many can be nested for import
- var relation;
- if (fields[field].type == 'one2many') {
- relation = fields[field].relation;
- } else {
- relation = null;
- }
- var node = {
- name: name,
- field: prefix_field + field,
- relation: relation,
- string: fields[field].string
- };
- parent_node[field] = node;
- this.fields[prefix_field + field] = node;
- this.fields_invert[name] = prefix_field + field;
- if (relation) {
- node.children = {};
- }
- }
- }
- },
- children_expand: function(node) {
- if (jQuery.isEmptyObject(node.children) && node.relation) {
- this.model_populate(
- this._get_fields(node.relation), node.children,
- node.field + '/', node.name + '/');
- }
- },
- autodetect: function() {
- var fname = this.file_input.val();
- if(!fname) {
- Sao.common.message.run(
- Sao.i18n.gettext('You must select an import file first.'));
- return;
- }
- this.fields_selected.empty();
- this.el_csv_skip.val(1);
- Papa.parse(this.file_input[0].files[0], {
- delimiter: this.el_csv_delimiter.val(),
- quoteChar: this.el_csv_quotechar.val(),
- preview: 1,
- encoding: this.el_csv_encoding.val(),
- error: (err, file, inputElem, reason) => {
- Sao.common.warning.run(
- reason,
- Sao.i18n.gettext("Detection failed"));
- },
- complete: results => {
- results.data[0].every(word => {
- if (!(word in this.fields_invert) && !(word in this.fields)) {
- var fields = this.fields_model;
- var prefix = '';
- var parents = word.split('/').slice(0, -1);
- this._traverse(fields, prefix, parents, 0);
- }
- return this._auto_select(word);
- });
- }
- });
- },
- _auto_select: function(word) {
- var name,field;
- if(word in this.fields_invert) {
- name = word;
- field = this.fields_invert[word];
- }
- else if (word in this.fields) {
- name = this.fields[word].name;
- field = [word];
- }
- else {
- Sao.common.warning.run(
- Sao.i18n.gettext('Unknown column header "%1"', word),
- Sao.i18n.gettext('Error'));
- return false;
- }
- this._add_node(field, name);
- return true;
- },
- _traverse: function(fields, prefix, parents, i) {
- var field, item;
- var names = Object.keys(fields);
- for (item = 0; item
{
- fields.push(field_el.getAttribute('field'));
- });
- var fname = this.file_input.val();
- if(fname) {
- this.import_csv(fname, fields).then(() => {
- this.destroy();
- });
- } else {
- this.destroy();
- }
- }
- else {
- this.destroy();
- }
- },
- import_csv: function(fname, fields) {
- var skip = this.el_csv_skip.val();
- var encoding = this.el_csv_encoding.val();
- var prm = jQuery.Deferred();
-
- Papa.parse(this.file_input[0].files[0], {
- delimiter: this.el_csv_delimiter.val(),
- quoteChar: this.el_csv_quotechar.val(),
- encoding: encoding,
- error: (err, file, inputElem, reason) => {
- Sao.common.warning.run(
- reason,
- Sao.i18n.gettext("Import failed"))
- .always(prm.reject);
- },
- complete: results => {
- var data = results.data.slice(skip, results.data.length - 1);
- Sao.rpc({
- 'method': 'model.' + this.screen.model_name +
- '.import_data',
- 'params': [fields, data, {}]
- }, this.session).then(count => {
- return Sao.common.message.run(
- Sao.i18n.ngettext('%1 record imported',
- '%1 records imported', count));
- }).then(prm.resolve, prm.reject);
- }
- });
- return prm.promise();
- }
- });
-
- Sao.Window.Export = Sao.class_(Sao.Window.CSV, {
- init: function(name, screen, names) {
- this.name = name;
- this.screen = screen;
- this.session = Sao.Session.current_session;
- Sao.Window.Export._super.init.call(this,
- Sao.i18n.gettext('CSV Export: %1',name));
- var fields = this.screen.model.fields;
- for (const name of names) {
- var type = fields[name].description.type;
- if (type == 'selection') {
- this.sel_field(name + '.translated');
- } else if (type == 'reference') {
- this.sel_field(name + '.translated');
- this.sel_field(name + '/rec_name');
- } else {
- this.sel_field(name);
- }
- }
-
- this.predef_exports = {};
- this.fill_predefwin();
-
- jQuery('', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Save Export"),
- }).text(' ' + Sao.i18n.gettext('Save Export')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-save')
- ).click(() => {
- this.addreplace_predef();
- })
- .appendTo(this.column_buttons);
-
- this.button_url = jQuery('', {
- 'class': 'btn btn-default btn-block',
- 'target': '_blank',
- 'rel': 'noreferrer noopener',
- 'title': Sao.i18n.gettext("URL Export"),
- }).text(' ' + Sao.i18n.gettext("URL Export")).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-public')
- )
- .appendTo(this.column_buttons);
- this.dialog.body.on('change click', this.set_url.bind(this));
-
- jQuery('', {
- 'class': 'btn btn-default btn-block',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Delete Export"),
- }).text(' ' + Sao.i18n.gettext('Delete Export')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-delete')
- ).click(() => {
- this.remove_predef();
- })
- .appendTo(this.column_buttons);
-
- var predefined_exports_column = jQuery('', {
- 'class': 'panel panel-default',
- }).append(jQuery('', {
- 'class': 'panel-heading',
- }).append(jQuery('', {
- 'class': 'panel-title',
- 'text': Sao.i18n.gettext('Predefined Exports')
- }))).appendTo(this.column_buttons);
-
- this.predef_exports_list = jQuery('', {
- 'class': 'list-unstyled predef-exports panel-body'
- }).css('cursor', 'pointer')
- .appendTo(predefined_exports_column);
-
- this.selected_records = jQuery('', {
- 'class': 'form-control',
- 'id': 'input-records',
- }).append(jQuery('', {
- 'val': true,
- 'selected': this.screen_is_tree ? false : true,
- }).text(Sao.i18n.gettext("Selected Records")))
- .append(jQuery('', {
- 'val': false,
- 'selected': this.screen_is_tree ? true : false,
- }).text(Sao.i18n.gettext("Listed Records")));
-
- this.ignore_search_limit = jQuery('', {
- 'type': 'checkbox',
- });
-
- this.selected_records.change(() => {
- this.ignore_search_limit.parents('.form-group').first().toggle(
- !JSON.parse(this.selected_records.val()) &&
- !this.screen_is_tree);
- });
-
- jQuery('', {
- 'class': 'form-group',
- }).appendTo(this.chooser_form)
- .append(jQuery('', {
- 'text': Sao.i18n.gettext("Export:"),
- 'class': 'control-label',
- 'for': 'input-records',
- })).append(this.selected_records);
-
- jQuery('', {
- 'class': 'form-group',
- }).appendTo(this.chooser_form)
- .append(jQuery('', {
- 'class': 'checkbox',
- }).append(jQuery('', {
- 'text': ' ' + Sao.i18n.gettext("Ignore search limit")
- }).prepend(this.ignore_search_limit)))
- .hide();
-
- this.el_csv_locale = jQuery('', {
- 'type': 'checkbox',
- 'checked': 'checked',
- });
-
- jQuery('', {
- 'class': 'checkbox',
- }).append(jQuery('', {
- 'text': ' ' + Sao.i18n.gettext("Use locale format"),
- }).prepend(this.el_csv_locale)).appendTo(this.expander_csv);
- this.expander_csv.append(' ');
-
- this.el_add_field_names = jQuery('', {
- 'type': 'checkbox',
- 'checked': 'checked'
- });
-
- jQuery('', {
- 'class': 'checkbox',
- }).append(jQuery('', {
- 'text': ' '+Sao.i18n.gettext('Add Field Names')
- }).prepend(this.el_add_field_names)).appendTo(this.expander_csv);
- this.expander_csv.append(' ');
-
- this.set_url();
- Sortable.create(this.fields_selected.get(0), {
- handle: '.draggable-handle',
- ghostClass: 'dragged-row'
- });
- },
- get context() {
- return this.screen.context;
- },
- get screen_is_tree() {
- return Boolean(
- this.screen.current_view &&
- (this.screen.current_view.view_type == 'tree') &&
- this.screen.current_view.children_field);
- },
- view_populate: function(parent_node, parent_view) {
- var names = Object.keys(parent_node).sort(function(a, b) {
- if (parent_node[b].string < parent_node[a].string) {
- return -1;
- }
- else {
- return 1;
- }
- }).reverse();
-
- names.forEach(name => {
- var path = parent_node[name].path;
- var node = jQuery('', {
- 'path': path
- }).text(parent_node[name].string).click(e => {
- if (e.ctrlKey || e.metaKey) {
- node.toggleClass('bg-primary');
- } else {
- this.fields_all.find('li')
- .removeClass('bg-primary');
- node.addClass('bg-primary');
- }
- }).appendTo(parent_view);
- parent_node[name].view = node;
-
- var expander_icon = Sao.common.ICONFACTORY
- .get_icon_img('tryton-arrow-right')
- .data('expanded', false)
- .click(e => {
- e.stopPropagation();
- var icon;
- var expanded = expander_icon.data('expanded');
- expander_icon.data('expanded', !expanded);
- if (expanded) {
- icon = 'tryton-arrow-right';
- node.next('ul').remove();
- } else {
- icon = 'tryton-arrow-down';
- this.on_row_expanded(parent_node[name]);
- }
- Sao.common.ICONFACTORY.get_icon_url(icon)
- .then(function(url) {
- expander_icon.attr('src', url);
- });
- }).prependTo(node);
- expander_icon.css(
- 'visibility',
- parent_node[name].children ? 'visible' : 'hidden');
- });
- },
- model_populate: function(fields, parent_node, prefix_field,
- prefix_name) {
- parent_node = parent_node || this.fields_model;
- prefix_field = prefix_field || '';
- prefix_name = prefix_name || '';
-
- for (const name of Object.keys(fields)) {
- var field = fields[name];
- var string = field.string || name;
- var items = [{ name: name, field: field, string: string }];
-
- if (field.type == 'selection') {
- items.push({
- name: name+'.translated',
- field: field,
- string: Sao.i18n.gettext('%1 (string)', string)
- });
- } else if (field.type == 'reference') {
- items.push({
- name: name + '.translated',
- field: field,
- string: Sao.i18n.gettext("%1 (model name)", string),
- });
- items.push({
- name: name + '/rec_name',
- field: field,
- string: Sao.i18n.gettext("%1/Record Name", string),
- });
- }
-
- for (const item of items) {
- var path = prefix_field + item.name;
- var long_string = prefix_name + item.string;
-
- var node = {
- path: path,
- string: item.string,
- long_string: long_string,
- relation: item.field.relation
- };
- parent_node[item.name] = node;
- this.fields[path] = node;
-
- // Insert relation only to real field
- if (item.name.indexOf('.') == -1 && item.field.relation) {
- node.children = {};
- }
- }
- }
- },
- children_expand: function(node) {
- if (jQuery.isEmptyObject(node.children) && node.relation) {
- this.model_populate(
- this._get_fields(node.relation), node.children,
- node.path + '/', node.long_string + '/');
- }
- },
- sig_sel_add: function(el_field) {
- el_field = jQuery(el_field);
- var name = el_field.attr('path');
- this.sel_field(name);
- },
- fill_predefwin: function() {
- Sao.rpc({
- 'method': 'model.ir.export.get',
- 'params': [
- this.screen.model_name,
- ['name', 'header', 'records', 'export_fields.name'],
- this.context,
- ],
- }, this.session).done(exports => {
- for (const export_ of exports) {
- this.predef_exports[export_.id] = {
- 'fields': export_['export_fields.'].map(
- field => field.name),
- 'values': export_,
- };
- this.add_to_predef(export_.id, export_.name);
- this.predef_exports_list.children('li').first().focus();
- }
- });
- },
- add_to_predef: function(id, name) {
- var node = jQuery('', {
- 'text': name,
- 'export_id': id,
- 'tabindex': 0
- }).on('keypress', function(e) {
- var keyCode = (e.keyCode ? e.keyCode : e.which);
- if(keyCode == 13 || keyCode == 32) {
- node.click();
- }
- }).click(event => {
- node.toggleClass('bg-primary')
- .siblings().removeClass('bg-primary');
- if (node.hasClass('bg-primary')) {
- this.sel_predef(node.attr('export_id'));
- }
- });
- this.predef_exports_list.append(node);
- },
- addreplace_predef: function() {
- var fields = [];
- var selected_fields = this.fields_selected.children('li');
- for (const field of selected_fields) {
- fields.push(field.getAttribute('path'));
- }
- if(fields.length === 0) {
- return;
- }
- var pref_id;
-
- const save = name => {
- var prm;
- var values = {
- 'header': this.el_add_field_names.is(':checked'),
- 'records': (
- JSON.parse(this.selected_records.val()) ?
- 'selected' : 'listed'),
- };
- if (!pref_id) {
- values.name = name;
- values.resource = this.screen.model_name;
- values.export_fields = fields.map(f => ({'name': f}));
- prm = Sao.rpc({
- method: 'model.ir.export.set',
- params: [values, this.context],
- }, this.session);
- } else {
- prm = Sao.rpc({
- method: 'model.ir.export.update',
- params: [pref_id, values, fields, this.context],
- }, this.session).then(() => pref_id);
- }
- return prm.then(pref_id => {
- this.session.cache.clear(
- 'model.' + this.screen.model_name + '.view_toolbar_get');
- this.predef_exports[pref_id] = {
- 'fields': fields,
- 'values': values,
- };
- if (selection.length === 0) {
- this.add_to_predef(pref_id, name);
- }
- });
- };
-
- var selection = this.predef_exports_list.children('li.bg-primary');
- if (selection.length === 0) {
- pref_id = null;
- Sao.common.ask.run(
- Sao.i18n.gettext('What is the name of this export?'),
- 'export')
- .then(save);
- }
- else {
- pref_id = selection.attr('export_id');
- Sao.common.sur.run(
- Sao.i18n.gettext(
- 'Override %1 definition?', selection.text()))
- .then(save);
- }
- },
- remove_predef: function() {
- var selection = this.predef_exports_list.children('li.bg-primary');
- if (selection.length === 0) {
- return;
- }
- var export_id = jQuery(selection).attr('export_id');
- Sao.rpc({
- 'method': 'model.ir.export.unset',
- 'params': [export_id, this.context]
- }, this.session).then(() => {
- this.session.cache.clear(
- 'model.' + this.screen.model_name + '.view_toolbar_get');
- delete this.predef_exports[export_id];
- selection.remove();
- });
- },
- sel_predef: function(export_id) {
- this.fields_selected.empty();
- const export_ = this.predef_exports[export_id];
- for (const name of export_.fields) {
- if (!(name in this.fields)) {
- var fields = this.fields_model;
- var prefix = '';
- var parents = name.split('/').slice(0, -1);
- this._traverse(fields, prefix, parents, 0);
- }
- if(!(name in this.fields)) {
- return;
- }
- this.sel_field(name);
- }
- this.el_add_field_names.prop('checked', export_.values.header);
- this.selected_records.val(
- JSON.stringify(export_.values.records == 'selected'));
- },
- _traverse: function(fields, prefix, parents, i) {
- var field, item;
- var names = Object.keys(fields);
- for (item = 0; item < names.length; item++) {
- field = fields[names[item]];
- if (field.path == (prefix + parents[i])) {
- this.children_expand(field);
- prefix += parents[i] + '/';
- if (field.children) {
- this._traverse(field.children, prefix, parents, ++i);
- }
- break;
- }
- }
- },
- sel_field: function(name) {
- var long_string = this.fields[name].long_string;
- var relation = this.fields[name].relation;
- if (relation) {
- name += '/rec_name';
- }
- var node = jQuery('', {
- 'path': name,
- 'class': 'draggable-handle',
- }).text(long_string).click(function(e) {
- if (e.ctrlKey || e.metaKey) {
- node.toggleClass('bg-primary');
- } else {
- jQuery(e.target).addClass('bg-primary')
- .siblings().removeClass('bg-primary');
- }
- }).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-drag')
- ).appendTo(this.fields_selected);
- },
- response: function(response_id) {
- if(response_id == 'RESPONSE_OK') {
- var fields = [];
- this.fields_selected.children('li').each(function(i, field) {
- fields.push(field.getAttribute('path'));
- });
- var header = this.el_add_field_names.is(':checked');
- var prm, ids, paths;
- if (JSON.parse(this.selected_records.val())) {
- ids = this.screen.selected_records.map(function(r) {
- return r.id;
- });
- paths = this.screen.selected_paths;
- prm = Sao.rpc({
- 'method': (
- 'model.' + this.screen.model_name +
- '.export_data'),
- 'params': [ids, fields, header, this.context]
- }, this.session);
- } else if (this.screen_is_tree) {
- ids = this.screen.listed_records.map(function(r) {
- return r.id;
- });
- paths = this.screen.listed_paths;
- prm = Sao.rpc({
- 'method': (
- 'model.' + this.screen.model_name +
- '.export_data'),
- 'params': [ids, fields, header, this.context]
- }, this.session);
- } else {
- var domain = this.screen.search_domain(
- this.screen.screen_container.get_text());
- var offset, limit;
- if (this.ignore_search_limit.prop('checked')) {
- offset = 0;
- limit = null;
- } else {
- offset = this.screen.offset;
- limit = this.screen.limit;
- }
- prm = Sao.rpc({
- 'method': (
- 'model.' + this.screen.model_name +
- '.export_data_domain'),
- 'params': [
- domain, fields, offset, limit, this.screen.order,
- header, this.context],
- }, this.session);
- }
- prm.then(data => {
- this.export_csv(data, paths, header).then(() => {
- this.destroy();
- });
- });
- } else {
- this.destroy();
- }
- },
- export_csv: function(data, paths, header=false) {
- var locale_format = this.el_csv_locale.prop('checked');
- var unparse_obj = {};
- unparse_obj.data = data.map(function(row, i) {
- var indent = paths && paths[i] ? paths[i].length -1 : 0;
- return Sao.Window.Export.format_row(
- row, indent, locale_format);
- });
- var csv = Papa.unparse(unparse_obj, {
- quoteChar: this.el_csv_quotechar.val(),
- delimiter: this.el_csv_delimiter.val()
- });
- if (navigator.platform &&
- navigator.platform.slice(0, 3) == 'Win') {
- csv = Sao.BOM_UTF8 + csv;
- }
- Sao.common.download_file(
- csv, this.name + '.csv', {type: 'text/csv;charset=utf-8'});
- var size = data.length;
- if (header) {
- size -= 1;
- }
- return Sao.common.message.run(
- Sao.i18n.ngettext(
- "%1 record saved", "%1 records saved", size));
- },
- set_url: function() {
- var path = [this.session.database, 'data', this.screen.model_name];
- var query_string = [];
- var domain;
- if (JSON.parse(this.selected_records.val())) {
- domain = this.screen.current_view.selected_records.map(function(r) {
- return r.id;
- });
- } else {
- domain = this.screen.search_domain(
- this.screen.screen_container.get_text());
- if (!this.ignore_search_limit.prop('checked') &&
- this.screen.limit !== null) {
- query_string.push(['s', this.screen.limit.toString()]);
- query_string.push(
- ['p', Math.floor(
- this.screen.offset / this.screen.limit).toString()]);
- }
- if (this.screen.order) {
- for (const expr of this.screen.order) {
- query_string.push(['o', expr.map(function(e) {
- return e;
- }).join(',')]);
- }
- }
- }
- query_string.splice(
- 0, 0, ['d', JSON.stringify(Sao.rpc.prepareObject(domain))]);
- if (!jQuery.isEmptyObject(this.screen.local_context)) {
- query_string.push(
- ['c', JSON.stringify(Sao.rpc.prepareObject(
- this.screen.local_context))]);
- }
-
- this.fields_selected.children('li').each(function(i, field) {
- query_string.push(['f', field.getAttribute('path')]);
- });
-
- query_string.push(['dl', this.el_csv_delimiter.val()]);
- query_string.push(['qc', this.el_csv_quotechar.val()]);
-
- if (!this.el_add_field_names.is(':checked')) {
- query_string.push(['h', '0']);
- }
- if (this.el_csv_locale.prop('checked')) {
- query_string.push(['loc', '1']);
- }
-
- query_string = query_string.map(function(param) {
- return param.map(encodeURIComponent).join('=');
- }).join('&');
- this.button_url.attr('href', '/' + path.join('/') + '?' + query_string);
- },
- });
-
- Sao.Window.Export.format_row = function(
- line, indent=0, locale_format=true) {
- var row = [];
- line.forEach(function(val, i) {
- if (locale_format) {
- if (val.isDateTime) {
- val = val.local().format(
- Sao.common.date_format() + ' ' +
- Sao.common.moment_format('%X'));
- } else if (val.isDate) {
- val = val.format(Sao.common.date_format());
- } else if (val.isTimeDelta) {
- val = Sao.common.timedelta.format(
- val, {'s': 1, 'm': 60, 'h': 60 * 60});
- } else if (!isNaN(Number(val))) {
- val = val.toLocaleString(
- Sao.i18n.BC47(Sao.i18n.getlang()), {
- 'minimumFractionDigits': 0,
- 'maximumFractionDigits': 20,
- });
- }
- } else if (val.isDateTime) {
- val = val.utc();
- } else if (val.isTimeDelta) {
- val = val.asSeconds();
- } else if (typeof(val) == 'boolean') {
- val += 0;
- }
- if ((i === 0) && indent && (typeof(val) == 'string')) {
- val = ' '.repeat(indent) + val;
- }
- if (val instanceof Uint8Array) {
- val = Sao.common.btoa(val);
- }
- row.push(val);
- });
- return row;
- };
-
- Sao.Window.EmailEntry = Sao.class_(Sao.common.InputCompletion, {
- init: function(el, session) {
- this.session = session;
- Sao.Window.EmailEntry._super.init.call(
- this, el,
- this._email_source,
- this._email_match_selected,
- this._email_format);
- },
- _email_match_selected: function(value) {
- this.input.val(value[2]);
- },
- _email_source: function(text) {
- if (this.input[0].selectionStart < this.input.val().length) {
- return jQuery.when([]);
- }
- return Sao.rpc({
- 'method': 'model.ir.email.complete',
- 'params': [text, Sao.config.limit, {}],
- }, this.session)
- .fail(function() {
- Sao.Logger.warn("Unable to complete email entry");
- });
- },
- _email_format: function(value) {
- return value[1];
- },
- });
-
- Sao.Window.Email = Sao.class_(Object, {
- init: function(name, record, prints, template) {
- this.record = record;
- this.dialog = new Sao.Dialog(
- Sao.i18n.gettext('E-mail %1', name), 'email', 'lg');
- this.el = this.dialog.modal;
- this.dialog.content.addClass('form-horizontal');
-
- var body = this.dialog.body;
- function add_group(name, label, required) {
- var group = jQuery('', {
- 'class': 'form-group',
- }).appendTo(body);
- jQuery('', {
- 'class': 'control-label col-sm-1',
- 'text': label,
- 'for': 'email-' + name,
- }).appendTo(group);
- var input = jQuery('', {
- 'type': 'text',
- 'class':'form-control input-sm',
- 'id': 'email-' + name,
- }).appendTo(jQuery('', {
- 'class': 'col-sm-11',
- }).appendTo(group));
- if (required) {
- input.attr('required', true);
- }
- return input;
- }
-
- this.to = add_group('to', Sao.i18n.gettext('To:'), true);
- this.cc = add_group('cc', Sao.i18n.gettext('Cc:'));
- this.bcc = add_group('bcc', Sao.i18n.gettext('Bcc:'));
- for (const input of [this.to, this.cc, this.bcc]) {
- new Sao.Window.EmailEntry(input, this.record.model.session);
- }
- this.subject = add_group(
- 'subject', Sao.i18n.gettext('Subject:'), true);
-
- var panel = jQuery('', {
- 'class': 'panel panel-default',
- }).appendTo(body
- ).append(jQuery('', {
- 'class': 'panel-heading',
- }).append(Sao.common.richtext_toolbar()));
- this.body = jQuery('', {
- 'class': 'email-richtext form-control input-sm mousetrap',
- 'contenteditable': true,
- 'spellcheck': true,
- 'id': 'email-body',
- }).appendTo(jQuery('
', {
- 'class': 'panel-body',
- }).appendTo(panel));
-
- var print_frame = jQuery('
', {
- 'class': 'col-md-4',
- }).appendTo(body);
- jQuery('
', {
- 'text': Sao.i18n.gettext("Reports"),
- }).appendTo(print_frame);
- this.print_actions = {};
- for (const print of prints) {
- var print_check = jQuery('
', {
- 'type': 'checkbox',
- });
- jQuery('
', {
- 'class': 'checkbox',
- }).append(jQuery('
'
- ).text(Sao.i18n.gettext(print.name)
- ).prepend(print_check)).appendTo(print_frame);
- this.print_actions[print.id] = print_check;
- }
-
- var attachment_frame = jQuery('
', {
- 'class': 'col-md-4',
- }).appendTo(body);
- jQuery('
', {
- 'text': Sao.i18n.gettext("Attachments"),
- }).appendTo(attachment_frame);
- this.attachments = jQuery('
', {
- 'class': 'form-control input-sm',
- 'name': 'attachments',
- 'multiple': true,
- }).appendTo(attachment_frame);
- Sao.rpc({
- 'method': 'model.ir.attachment.search_read',
- 'params': [
- [
- ['resource', '=', record.model.name + ',' + record.id],
- ['OR',
- ['data', '!=', null],
- ['file_id', '!=', null],
- ],
- ],
- 0, null, null, ['rec_name'], record.get_context()],
- }, record.model.session).then(attachments => {
- for (const attachment of attachments) {
- this.attachments.append(jQuery('
', {
- 'value': JSON.stringify(attachment.id),
- 'text': attachment.rec_name,
- }));
- }
- })
- .fail(function() {
- Sao.Logger.error(
- "Could not fetch attachment for", record);
- });
-
- this.files = jQuery('
', {
- 'class': 'col-md-4',
- }).appendTo(body);
- jQuery('
', {
- 'text': Sao.i18n.gettext("Files"),
- }).appendTo(this.files);
- this._add_file_button();
-
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Cancel"),
- }).text(' ' + Sao.i18n.gettext('Cancel')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-cancel')
- ).click(() => {
- this.response('RESPONSE_CANCEL');
- }).appendTo(this.dialog.footer);
-
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("Send"),
- }).text(' ' + Sao.i18n.gettext('Send')).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-send')
- ).appendTo(this.dialog.footer);
- this.dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- this._fill_with(template);
-
- this.el.modal('show');
- this.el.on('hidden.bs.modal', function() {
- jQuery(this).remove();
- });
- },
- _add_file_button: function() {
- var row = jQuery('
').appendTo(this.files);
- var file = jQuery('
', {
- 'type': 'file',
- }).appendTo(row);
- var button = jQuery('
', {
- 'class': 'close',
- 'title': Sao.i18n.gettext("Remove File"),
- }).append(jQuery('
', {
- 'aria-hidden': true,
- 'text': 'x',
- })).append(jQuery('
', {
- 'class': 'sr-only',
- }).text(Sao.i18n.gettext("Remove")));
- button.hide();
- button.appendTo(row);
-
- file.on('change', () => {
- button.show();
- this._add_file_button();
- });
- button.click(function() {
- row.remove();
- });
- },
- get_files: function() {
- var prms = [];
- var files = [];
- this.files.find('input[type=file]').each(function(i, input) {
- if (input.files.length) {
- var dfd = jQuery.Deferred();
- prms.push(dfd);
- Sao.common.get_file_data(
- input.files[0], function(data, filename) {
- files.push([filename, data]);
- dfd.resolve();
- });
- }
- });
- return jQuery.when.apply(jQuery, prms).then(function() {
- return files;
- });
- },
- get_attachments: function() {
- var attachments = this.attachments.val();
- if (attachments) {
- return attachments.map(function(e) { return JSON.parse(e); });
- }
- return [];
- },
- _fill_with: function(template) {
- var prm;
- if (template) {
- prm = Sao.rpc({
- 'method': 'model.ir.email.template.get',
- 'params': [template, this.record.id, {}],
- }, this.record.model.session);
- } else {
- prm = Sao.rpc({
- 'method': 'model.ir.email.template.get_default',
- 'params': [this.record.model.name, this.record.id, {}],
- }, this.record.model.session);
- }
- prm.then(values => {
- this.to.val((values.to || []).join(', '));
- this.cc.val((values.cc || []).join(', '));
- this.bcc.val((values.bcc || []).join(', '));
- this.subject.val(values.subject || '');
- this.body.html(Sao.HtmlSanitizer.sanitize(values.body || ''));
- var print_ids = (values.reports || []);
- for (var print_id in this.print_actions) {
- var check = this.print_actions[print_id];
- check.prop(
- 'checked', ~print_ids.indexOf(parseInt(print_id, 10)));
- }
- });
- },
- response: function(response_id) {
- if (response_id == 'RESPONSE_OK') {
- var to = this.to.val();
- var cc = this.cc.val();
- var bcc = this.bcc.val();
- var subject = this.subject.val();
- var body = Sao.common.richtext_normalize(this.body.html());
- var reports = [];
- for (var id in this.print_actions) {
- var check = this.print_actions[id];
- if (check.prop('checked')) {
- reports.push(id);
- }
- }
- var attachments = this.get_attachments();
- var record = this.record;
- this.get_files().then(function(files) {
- return Sao.rpc({
- 'method': 'model.ir.email.send',
- 'params': [
- to, cc, bcc, subject, body,
- files,
- [record.model.name, record.id],
- reports,
- attachments,
- {}],
- }, record.model.session);
- }).then(() => {
- this.destroy();
- });
- } else {
- this.destroy();
- }
- },
- destroy: function() {
- this.el.modal('hide');
- },
- });
-
- Sao.Window.CodeScanner = Sao.class_(Object, {
- init: function(callback, loop=false) {
- this.callback = callback;
- this.loop = loop;
- this.submitting = false;
- this.dialog = new Sao.Dialog(
- Sao.i18n.gettext("Code Scanner"), 'code-scanner', 'md');
- this.el = this.dialog.modal;
- this.input = jQuery('
', {
- 'type': 'text',
- 'class': 'form-control input-sm mousetrap',
- 'aria-label': Sao.i18n.gettext("Code"),
- 'placeholder': Sao.i18n.gettext("Code"),
- }).appendTo(jQuery('
', {
- 'class': 'col-sm-12',
- }).appendTo(this.dialog.body));
-
- var sound_btn = jQuery('
', {
- 'class': 'btn btn-default pull-right',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Toggle Sound"),
- }).prependTo(this.dialog.header);
- var sound_icon = jQuery('
![]()
', {
- 'class': 'icon',
- }).appendTo(sound_btn);
-
- var sound_set_state = function(state) {
- var prm;
- if (state) {
- sound_btn.addClass('active');
- sound_btn.attr('aria-pressed', state);
- prm = Sao.common.ICONFACTORY.get_icon_url('tryton-sound-on')
- .done(url => {
- sound_icon.attr('src', url);
- });
- } else {
- sound_btn.removeClass('active');
- sound_btn.attr('aria-pressed', state);
- prm = Sao.common.ICONFACTORY.get_icon_url('tryton-sound-off')
- .done(url => {
- sound_icon.attr('src', url);
- });
- }
- localStorage.setItem('sao_code_scanner_sound', state);
- return prm;
- };
- var sound_state = JSON.parse(
- localStorage.getItem('sao_code_scanner_sound'));
- if (sound_state === null) {
- sound_state = true;
- }
- var sound_prm = sound_set_state(sound_state);
- sound_btn.click(
- () => sound_set_state(!sound_btn.hasClass('active')));
-
- jQuery('
', {
- 'class': 'btn btn-link',
- 'type': 'button',
- 'title': Sao.i18n.gettext("Close"),
- }).text(' ' + Sao.i18n.gettext("Close")).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-close')
- ).click(() => {
- this.response('RESPONSE_CLOSE');
- }).appendTo(this.dialog.footer);
-
- jQuery('
', {
- 'class': 'btn btn-primary',
- 'type': 'submit',
- 'title': Sao.i18n.gettext("OK"),
- }).text(' ' + Sao.i18n.gettext("OK")).prepend(
- Sao.common.ICONFACTORY.get_icon_img('tryton-ok')
- ).appendTo(this.dialog.footer);
- this.dialog.content.submit(e => {
- e.preventDefault();
- this.response('RESPONSE_OK');
- });
-
- this.el.on('shown.bs.modal', () => {
- this.input.on('blur', this._keep_focus);
- this.input.focus();
- });
- this.el.on('hidden.bs.modal', function() {
- jQuery(this).remove();
- });
- // show modal after sound icons have been set
- // because they can trigger a renew session modal
- sound_prm.then(() => this.el.modal('show'));
- },
- _play_sound: function(sound) {
- if (JSON.parse(localStorage.getItem('sao_code_scanner_sound'))) {
- Sao.common.play_sound(sound);
- }
- },
- _keep_focus: function() {
- setTimeout(() => this.focus(), 1);
- },
- response: function(response_id) {
- if (this.submitting) return;
- if (response_id == 'RESPONSE_OK') {
- var code = this.input.val();
- this.input.val(''); // clear input to prevent multiple calls
- if (code) {
- this.submitting = true;
- this.input.off('blur');
- return this.callback(code)
- .always(() => this.submitting = false)
- .then((modified) => {
- this._play_sound('success');
- if (!this.loop || !modified) {
- this.destroy();
- }
- this.input.on('blur', this._keep_focus);
- this.input.focus();
- }, (error) => {
- this._play_sound('danger');
- if (error[0] == 'UserError') {
- Sao.common.warning.run(error[1][1], error[1][0]);
- } else {
- Sao.common.error.run(error[0], error[1]);
- }
- this.destroy();
- });
- }
- }
- if (!this.loop || response_id != 'RESPONSE_OK') {
- this.destroy();
- }
- },
- destroy: function() {
- this.el.modal('hide');
- },
- });
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.Wizard = Sao.class_(Object, {
- init: function(name) {
- this.widget = jQuery('
', {
- 'class': 'wizard'
- });
- this.name = name || Sao.i18n.gettext('Wizard');
- this.action_id = null;
- this.id = null;
- this.ids = null;
- this.action = null;
- this.context = null;
- this.states = {};
- this.session_id = null;
- this.start_state = null;
- this.end_state = null;
- this.screen = null;
- this.screen_state = null;
- this.state = null;
- this.session = Sao.Session.current_session;
- this.__processing = false;
- this.__waiting_response = false;
- this.info_bar = new Sao.Window.InfoBar();
- },
- run: function(attributes) {
- this.action = attributes.action;
- this.action_id = attributes.data.action_id;
- this.id = attributes.data.id;
- this.ids = attributes.data.ids;
- this.model = attributes.data.model;
- this.context = jQuery.extend({}, attributes.context);
- this.context.active_id = this.id;
- this.context.active_ids = this.ids;
- this.context.active_model = this.model;
- this.context.action_id = this.action_id;
- return Sao.rpc({
- 'method': 'wizard.' + this.action + '.create',
- 'params': [this.session.context]
- }, this.session).then(result => {
- this.session_id = result[0];
- this.start_state = this.state = result[1];
- this.end_state = result[2];
- return this.process();
- }, () => {
- this.destroy();
- });
- },
- process: function() {
- if (this.__processing || this.__waiting_response) {
- return jQuery.when();
- }
- var process = function() {
- if (this.state == this.end_state) {
- return this.end();
- }
- var ctx = jQuery.extend({}, this.context);
- var data = {};
- if (this.screen) {
- data[this.screen_state] = this.screen.get_on_change_value();
- }
- return Sao.rpc({
- 'method': 'wizard.' + this.action + '.execute',
- 'params': [this.session_id, data, this.state, ctx]
- }, this.session).then(result => {
- var prms = [];
- if (result.view) {
- this.clean();
- var view = result.view;
- this.update(view.fields_view, view.buttons);
-
- prms.push(this.screen.new_(false).then(() => {
- this.screen.current_record.set_default(
- view.defaults || {})
- .then(() => {
- this.screen.current_record.set(
- view.values || {});
- this.update_buttons();
- this.screen.set_cursor();
- });
- }));
-
- this.screen_state = view.state;
- this.__waiting_response = true;
- } else {
- this.state = this.end_state;
- }
-
- const execute_actions = () => {
- var prms = [];
- if (result.actions) {
- for (const action of result.actions) {
- var context = jQuery.extend({}, this.context);
- // Remove wizard keys added by run
- delete context.active_id;
- delete context.active_ids;
- delete context.active_model;
- delete context.action_id;
- prms.push(Sao.Action.execute(
- action[0], action[1], context));
- }
- }
- return jQuery.when.apply(jQuery, prms);
- };
-
- if (this.state == this.end_state) {
- prms.push(this.end().then(execute_actions));
- } else {
- prms.push(execute_actions());
- }
- this.__processing = false;
- return jQuery.when.apply(jQuery, prms);
- }, result => {
- // TODO end for server error.
- this.__processing = false;
- });
- };
- return process.call(this);
- },
- destroy: function(action) {
- // TODO
- },
- end: function() {
- return Sao.rpc({
- 'method': 'wizard.' + this.action + '.delete',
- 'params': [this.session_id, this.session.context]
- }, this.session).then(action => {
- this.destroy(action);
- })
- .fail(() => {
- Sao.Logger.warn(
- "Unable to delete session %s of wizard %s",
- this.session_id, this.action);
- });
- },
- clean: function() {
- this.widget.empty();
- this.states = {};
- },
- response: function(definition) {
- this.__waiting_response = false;
- this.screen.current_view.set_value();
- if (definition.validate && !this.screen.current_record.validate(
- null, null, null, true)) {
- this.screen.display(true);
- this.info_bar.add(this.screen.invalid_message(), 'danger');
- return;
- }
- this.info_bar.clear();
- this.state = definition.state;
- this.process();
- },
- _get_button: function(definition) {
- var style = 'btn-default';
- if (definition.default) {
- style = 'btn-primary';
- } else if (definition.state == this.end_state) {
- style = 'btn-link';
- }
- var button = new Sao.common.Button(
- definition, undefined, undefined, style);
- this.states[definition.state] = button;
- return button;
- },
- record_modified: function() {
- this.update_buttons();
- this.info_bar.refresh();
- },
- update_buttons: function() {
- var record = this.screen.current_record;
- for (var state in this.states) {
- var button = this.states[state];
- button.set_state(record);
- }
- },
- update: function(view, buttons) {
- for (const button of buttons) {
- this._get_button(button);
- }
- if (this.screen) {
- this.screen.windows.splice(
- this.screen.windows.indexOf(this), 1);
- }
- this.screen = new Sao.Screen(view.model,
- {mode: [], context: this.context});
- this.screen.add_view(view);
- this.screen.switch_view();
- this.screen.windows.push(this);
- this.header.append(jQuery('
', {
- 'class': 'model-title',
- 'title': this.name,
- }).text(Sao.common.ellipsize(this.name, 80)));
- this.widget.append(this.screen.screen_container.el);
- }
- });
-
- Sao.Wizard.create = function(attributes) {
- var win;
- if (attributes.window) {
- win = new Sao.Wizard.Form(attributes.name);
- var tab = new Sao.Tab.Wizard(win);
- Sao.Tab.add(tab);
- } else {
- win = new Sao.Wizard.Dialog(attributes.name);
- }
- return win.run(attributes);
- };
-
- Sao.Wizard.Form = Sao.class_(Sao.Wizard, {
- init: function(name) {
- Sao.Wizard.Form._super.init.call(this, name);
- this.tab = null; // Filled by Sao.Tab.Wizard
-
- this.header = jQuery('
', {
- 'class': 'modal-header',
- });
- this.form = jQuery('
', {
- 'class': 'wizard-form',
- }).append(this.header).append(this.widget);
- this.footer = jQuery('
', {
- 'class': 'modal-footer'
- }).appendTo(this.form);
- },
- clean: function() {
- Sao.Wizard.Form._super.clean.call(this);
- this.header.empty();
- this.footer.empty();
- },
- _get_button: function(definition) {
- var button = Sao.Wizard.Form._super._get_button.call(this,
- definition);
- this.footer.append(button.el);
- button.el.click(() => {
- this.response(definition);
- });
- return button;
- },
- destroy: function(action) {
- Sao.Wizard.Form._super.destroy.call(this, action);
- switch (action) {
- case 'reload menu':
- Sao.Session.current_session.reload_context()
- .then(function() {
- Sao.menu();
- });
- break;
- case 'reload context':
- Sao.Session.current_session.reload_context();
- break;
- }
- },
- end: function() {
- return Sao.Wizard.Form._super.end.call(this).always(
- () => this.tab.close());
- }
- });
-
- Sao.Wizard.Dialog = Sao.class_(Sao.Wizard, { // TODO nomodal
- init: function(name) {
- Sao.Wizard.Dialog._super.init.call(this, name);
- var dialog = new Sao.Dialog(name, 'wizard-dialog', 'md', false);
- this.dialog = dialog.modal;
- this.header = dialog.header;
- this.content = dialog.content;
- this.footer = dialog.footer;
- this.dialog.on('keydown', e => {
- if (e.which == Sao.common.ESC_KEYCODE) {
- e.preventDefault();
- if (this.end_state in this.states) {
- this.response(this.states[this.end_state].attributes);
- }
- }
- });
- dialog.body.append(this.widget).append(this.info_bar.el);
- },
- clean: function() {
- Sao.Wizard.Dialog._super.clean.call(this);
- this.header.empty();
- this.footer.empty();
- },
- _get_button: function(definition) {
- var button = Sao.Wizard.Dialog._super._get_button.call(this,
- definition);
- this.footer.append(button.el);
- if (definition['default']) {
- this.content.unbind('submit');
- this.content.submit(e => {
- this.response(definition);
- e.preventDefault();
- });
- button.el.attr('type', 'submit');
- } else {
- button.el.click(() => {
- this.response(definition);
- });
- }
- return button;
- },
- update: function(view, buttons) {
- this.content.unbind('submit');
- Sao.Wizard.Dialog._super.update.call(this, view, buttons);
- this.show();
- },
- destroy: function(action) {
- Sao.Wizard.Dialog._super.destroy.call(this, action);
- const destroy = () => {
- this.dialog.remove();
- var dialog = jQuery('.wizard-dialog').filter(':visible')[0];
- var is_menu = false;
- var screen;
- if (!dialog) {
- dialog = Sao.Tab.tabs.get_current();
- }
- if (!dialog ||
- !this.model ||
- (Sao.main_menu_screen &&
- (Sao.main_menu_screen.model_name == this.model))) {
- is_menu = true;
- screen = Sao.main_menu_screen;
- } else {
- screen = dialog.screen;
- }
- if (screen) {
- var prm = jQuery.when();
- if (screen.current_record && !is_menu) {
- var ids;
- if (screen.model_name == this.model) {
- ids = this.ids;
- } else {
- // Wizard run form a children record so reload
- // parent record
- ids = [screen.current_record.id];
- }
- prm = screen.reload(ids, true);
- }
- if (action) {
- prm.then(function() {
- screen.client_action(action);
- });
- }
- }
- };
- if ((this.dialog.data('bs.modal') || {}).isShown) {
- this.dialog.on('hidden.bs.modal', destroy);
- this.dialog.modal('hide');
- } else {
- destroy();
- }
- },
- show: function() {
- var view = this.screen.current_view;
- var expand;
- if (view.view_type == 'form') {
- expand = false;
- var fields = view.get_fields();
- for (const name of fields) {
- var widgets = view.widgets[name];
- for (const widget of widgets) {
- if (widget.expand) {
- expand = true;
- break;
- }
- }
- if (expand) {
- break;
- }
- }
- } else {
- expand = true;
- }
- if (expand) {
- this.dialog.find('.modal-dialog')
- .removeClass('modal-md modal-sm')
- .addClass('modal-lg');
- } else {
- this.dialog.find('.modal-dialog')
- .removeClass('modal-lg modal-sm')
- .addClass('modal-md');
- }
- this.dialog.modal('show');
- },
- hide: function() {
- this.dialog.modal('hide');
- },
- state_changed: function() {
- this.process();
- }
- });
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.View.BoardXMLViewParser = Sao.class_(Sao.View.FormXMLViewParser, {
- _parse_board: function(node, attributes) {
- var container = new Sao.View.Form.Container(
- Number(node.getAttribute('col') || 4));
- this.view.el.append(container.el);
- this.parse_child(node, container);
- if (this._containers.length > 0) {
- throw 'AssertionError';
- }
- },
- _parse_action: function(node, attributes) {
- var action;
- if (attributes.yexpand === undefined) {
- attributes.yexpand = true;
- }
- if (attributes.yfill === undefined) {
- attributes.yfill = true;
- }
- action = new Sao.View.Board.Action(this.view, attributes);
- this.view.actions.push(action);
- this.container.add(action, attributes);
- },
- });
-
- Sao.View.Board = Sao.class_(Object, {
- xml_parser: Sao.View.BoardXMLViewParser,
- init: function(xml, context) {
- this.context = context;
- this.actions = [];
- this.containers = [];
- this.state_widgets = [];
- this.el = jQuery('
', {
- 'class': 'board'
- });
- new this.xml_parser(this, null, {}).parse(xml.children()[0]);
-
- var actions_prms = [];
- for (const action of this.actions) {
- actions_prms.push(action.action_prm);
- }
- this.actions_prms = jQuery.when.apply(jQuery, actions_prms);
- },
- reload: function() {
- var i;
- for (i = 0; i < this.actions.length; i++) {
- this.actions[i].display();
- }
- for (i = 0; i < this.state_widgets.length; i++) {
- this.state_widgets[i].set_state(null);
- }
- },
- active_changed: function(event_action) {
- for (const action of this.actions) {
- if (action !== event_action) {
- action.update_domain(this.actions);
- }
- }
- },
- });
-
- Sao.View.Board.Action = Sao.class_(Object, {
- init: function(view, attributes) {
- var session = Sao.Session.current_session;
- this.name = attributes.name;
- this.view = view;
-
- this.el = jQuery('
', {
- 'class': 'board-action panel panel-default',
- });
- this.title = jQuery('
', {
- 'class': 'panel-heading',
- });
- this.el.append(this.title);
- this.body = jQuery('
', {
- 'class': 'panel-body',
- });
- this.el.append(this.body);
-
- var act_window = new Sao.Model('ir.action.act_window');
- this.action_prm = act_window.execute('get', [this.name], {});
- this.action_prm.done(action => {
- var params = {};
- this.action = action;
- params.view_ids = [];
- params.mode = null;
- if (!jQuery.isEmptyObject(action.views)) {
- params.view_ids = [];
- params.mode = [];
- for (const view of action.views) {
- params.view_ids.push(view[0]);
- params.mode.push(view[1]);
- }
- } else if (!jQuery.isEmptyObject(action.view_id)) {
- params.view_ids = [action.view_id[0]];
- }
-
- if (!('pyson_domain' in this.action)) {
- this.action.pyson_domain = '[]';
- }
- var ctx = {};
- ctx = jQuery.extend(ctx, session.context);
- ctx._user = session.user_id;
- var decoder = new Sao.PYSON.Decoder(ctx);
- params.context = jQuery.extend(
- {}, this.view.context,
- decoder.decode(action.pyson_context || '{}'));
- ctx = jQuery.extend(ctx, params.context);
-
- ctx.context = ctx;
- decoder = new Sao.PYSON.Decoder(ctx);
- params.order = decoder.decode(action.pyson_order);
- params.search_value = decoder.decode(
- action.pyson_search_value || '[]');
- params.tab_domain = [];
- for (const element of action.domains) {
- params.tab_domain.push(
- [element[0], decoder.decode(element[1]), element[2]]);
- }
- params.context_model = action.context_model;
- params.context_domain = action.context_domain;
- if (action.limit !== null) {
- params.limit = action.limit;
- } else {
- params.limit = Sao.config.limit;
- }
-
- this.context = ctx;
- this.domain = [];
- this.update_domain([]);
-
- params.row_activate = this.row_activate.bind(this);
-
- this.screen = new Sao.Screen(this.action.res_model,
- params);
-
- if (attributes.string) {
- this.title.text(attributes.string);
- } else {
- this.title.text(this.action.name);
- }
- this.screen.switch_view().done(() => {
- this.body.append(this.screen.screen_container.el);
- this.screen.search_filter();
- });
- });
- },
- row_activate: function() {
- if (!this.screen.current_record) {
- return;
- }
-
- if (this.screen.current_view.view_type == 'tree' &&
- (this.screen.current_view.attributes.keyword_open == 1)) {
- const record_ids = this.screen.current_view.selected_records.map(
- function(record) { return record.id; });
- Sao.Action.exec_keyword('tree_open', {
- model: this.screen.model_name,
- id: this.screen.current_record.id,
- ids: record_ids
- }, jQuery.extend({}, this.screen.group._context), false);
- } else {
- new Sao.Window.Form(this.screen, result => {
- if (result) {
- this.screen.current_record.save();
- } else {
- this.screen.current_record.cancel();
- }
- }, {
- 'title': this.title.text(),
- });
- }
- },
- display: function() {
- this.screen.search_filter(this.screen.screen_container.get_text());
- },
- record_message: function() {
- this.view.active_changed(this);
- },
- get active() {
- if (this.screen && this.screen.current_record) {
- return {
- 'active_id': this.screen.current_record.id,
- 'active_ids': this.screen.selected_records.map(
- function(r) {
- return r.id;
- }),
- };
- } else {
- return {
- 'active_id': null,
- 'active_ids': [],
- };
- }
- },
- update_domain: function(actions) {
- const domain_ctx = jQuery.extend({}, this.context);
- domain_ctx._actions = {};
- for (var i = 0, len = actions.length; i < len; i++) {
- domain_ctx._actions[actions[i].name] = actions[i].active;
- }
- const decoder = new Sao.PYSON.Decoder(domain_ctx);
- const new_domain = decoder.decode(this.action.pyson_domain);
- if (Sao.common.compare(this.domain, new_domain)) {
- return;
- }
- this.domain.splice(0, this.domain.length);
- jQuery.extend(this.domain, new_domain);
- if (this.screen) {
- this.display();
- }
- }
- });
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-(function() {
- 'use strict';
-
- Sao.Bus = {};
-
- // Bus Identifier
- Sao.Bus.id = Sao.common.uuid4();
- Sao.Bus.channels = ['client:' + Sao.Bus.id];
-
- Sao.Bus.listen = function(last_message, wait) {
- wait = wait || 1;
- var session = Sao.Session.current_session;
- if (!session) {
- return;
- }
-
- var prm = jQuery.ajax({
- headers: {
- Authorization: 'Session ' + session.get_auth(),
- },
- contentType: 'application/json',
- data: JSON.stringify({
- last_message: last_message,
- channels: Sao.Bus.channels
- }),
- dataType: 'json',
- url: '/' + session.database + '/bus',
- type: 'POST',
- timeout: Sao.config.bus_timeout,
- });
-
- prm.done(function(response) {
- if (Sao.Session.current_session != session) {
- return;
- }
- if (response.message) {
- last_message = response.message.message_id;
- Sao.Logger.info(
- "poll channels %s with last message",
- Sao.Bus.channels, last_message);
- Sao.Bus.handle(response.message);
- }
- Sao.Bus.listen(last_message, 1);
- });
-
- prm.fail(function(response, status, error) {
- if (Sao.Session.current_session != session) {
- return;
- }
- if (error === "timeout") {
- Sao.Bus.listen(last_message, 1);
- } else if (response.status == 501) {
- Sao.Logger.info("Bus not supported");
- return;
- } else {
- Sao.Logger.error(
- "An exception occured while connection to the bus. " +
- "Sleeping for %s seconds",
- wait, error);
- window.setTimeout(
- Sao.Bus.listen,
- Math.min(wait * 1000, Sao.config.bus_timeout),
- last_message, wait * 2);
- }
- });
- };
-
- Sao.Bus.handle = function(message) {
- var notify = function(message) {
- try {
- if (Notification.permission != "granted") {
- return;
- }
- } catch (e) {
- Sao.Logger.error(e.message, e.stack);
- return;
- }
-
- new Notification(message.title, {
- body: message.body || '',
- });
- };
-
- switch (message.type) {
- case 'notification':
- notify(message);
- break;
- }
- };
-
-}());
-
-/* This file is part of Tryton. The COPYRIGHT file at the top level of
- this repository contains the full copyright notices and license terms. */
-
-(function() {
- 'use strict';
-
- var Translate = {};
- Translate.translate_view = function(data) {
- var model = data.model;
- Sao.Tab.create({
- model: 'ir.translation',
- domain: [['model', '=', model]],
- mode: ['tree', 'form'],
- name: Sao.i18n.gettext('Translate view'),
- });
- };
- Translate.get_plugins = function(model) {
- var access = Sao.common.MODELACCESS.get('ir.translation');
- if (access.read && access.write) {
- return [
- [Sao.i18n.gettext('Translate view'), Translate.translate_view],
- ];
- } else {
- return [];
- }
- };
-
- Sao.Plugins.push(Translate);
-}());
-
-/*
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-(function () {
- 'use strict';
-
- var tag_whitelist = {
- B: true,
- BODY: true,
- BR: true,
- DIV: true,
- FONT: true,
- I: true,
- U: true,
- };
-
- var attribute_whitelist = {
- align: true,
- color: true,
- face: true,
- size: true,
- };
-
- Sao.HtmlSanitizer = {};
- Sao.HtmlSanitizer.sanitize = function(input) {
- input = input.trim();
- // to save performance and not create iframe
- if (input == "") return "";
-
- // firefox "bogus node" workaround
- if (input == "
") return "";
-
- var iframe = document.createElement('iframe');
- if (iframe.sandbox === undefined) {
- // Browser does not support sandboxed iframes
- Sao.Logger.warn("Your browser do not support sandboxed iframes," +
- " unable to sanitize HTML.");
- return input;
- }
- iframe.sandbox = 'allow-same-origin';
- iframe.style.display = 'none';
- // necessary so the iframe contains a document
- document.body.appendChild(iframe);
- var iframedoc = (iframe.contentDocument ||
- iframe.contentWindow.document);
- // null in IE
- if (iframedoc.body == null) {
- iframedoc.write("");
- }
- iframedoc.body.innerHTML = input;
-
- function make_sanitized_copy(node) {
- var new_node;
- if (node.nodeType == Node.TEXT_NODE) {
- new_node = node.cloneNode(true);
- } else if (node.nodeType == Node.ELEMENT_NODE &&
- tag_whitelist[node.tagName]) {
- //remove useless empty tags
- if ((node.tagName != "BR") && node.innerHTML.trim() == "") {
- return document.createDocumentFragment();
- }
-
- new_node = iframedoc.createElement(node.tagName);
-
- for (var i = 0; i < node.attributes.length; i++) {
- var attr = node.attributes[i];
- if (attribute_whitelist[attr.name]) {
- new_node.setAttribute(attr.name, attr.value);
- }
- }
- for (i = 0; i < node.childNodes.length; i++) {
- var sub_copy = make_sanitized_copy(node.childNodes[i]);
- new_node.appendChild(sub_copy, false);
- }
- } else {
- new_node = document.createDocumentFragment();
- if (node.tagName != 'SCRIPT') {
- new_node.textContent = node.textContent;
- }
- }
- return new_node;
- }
-
- var result_element = make_sanitized_copy(iframedoc.body);
- document.body.removeChild(iframe);
- // replace is just for cleaner code
- return result_element.innerHTML
- .replace(/
]*>(\S)/g, "
\n$1")
- .replace(/div>