flectra/addons/web/static/src/js/chrome/search_menus.js

466 lines
17 KiB
JavaScript

flectra.define('web.FavoriteMenu', function (require) {
"use strict";
var core = require('web.core');
var data_manager = require('web.data_manager');
var pyeval = require('web.pyeval');
var session = require('web.session');
var Widget = require('web.Widget');
var _t = core._t;
return Widget.extend({
template: 'SearchView.FavoriteMenu',
events: {
'click li': function (event) {
event.stopImmediatePropagation();
},
'click li a': function (event) {
event.preventDefault();
},
'click .o_save_search a': function (event) {
event.preventDefault();
this.toggle_save_menu();
},
'click .o_save_name button': 'save_favorite',
'hidden.bs.dropdown': '_closeMenus',
'keyup .o_save_name input': function (ev) {
if (ev.which === $.ui.keyCode.ENTER) {
this.save_favorite();
}
},
},
init: function (parent, query, target_model, action_id, filters) {
this._super.apply(this,arguments);
this.searchview = parent;
this.query = query;
this.target_model = target_model;
this.action_id = action_id;
this.filters = {};
_.each(filters, this.add_filter.bind(this));
},
start: function () {
var self = this;
this.$filters = {};
this.$save_search = this.$('.o_save_search');
this.$save_name = this.$('.o_save_name');
this.$inputs = this.$save_name.find('input');
this.$user_divider = this.$('.divider.user_filter');
this.$shared_divider = this.$('.divider.shared_filter');
this.$inputs.eq(0).val(this.searchview.get_title());
var $shared_filter = this.$inputs.eq(1),
$default_filter = this.$inputs.eq(2);
$shared_filter.click(function () {$default_filter.prop('checked', false);});
$default_filter.click(function () {$shared_filter.prop('checked', false);});
this.query
.on('remove', function (facet) {
if (facet.get('is_custom_filter')) {
self.clear_selection();
}
})
.on('reset', this.proxy('clear_selection'));
_.each(this.filters, this.append_filter.bind(this));
return this._super();
},
toggle_save_menu: function (is_open) {
this.$save_search
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
.toggleClass('o_open_menu', is_open);
this.$save_name.toggle(is_open);
if (this.$save_search.hasClass('o_open_menu')) {
this.$save_name.find('input').first().focus();
}
},
_closeMenus: function () {
this.toggle_save_menu(false);
},
save_favorite: function () {
var self = this,
filter_name = this.$inputs[0].value,
default_filter = this.$inputs[1].checked,
shared_filter = this.$inputs[2].checked;
if (!filter_name.length){
this.do_warn(_t("Error"), _t("Filter name is required."));
this.$inputs.first().focus();
return;
}
if (_.chain(this.filters)
.pluck('name')
.contains(filter_name).value()) {
this.do_warn(_t("Error"), _t("Filter with same name already exists."));
this.$inputs.first().focus();
return;
}
var user_context = this.getSession().user_context;
var search = this.searchview.build_search_data();
// DO NOT FORWARDPORT THIS
var controllerContext;
this.trigger_up('get_controller_context', {
callback: function (ctx) {
controllerContext = ctx;
},
});
var results = pyeval.eval_domains_and_contexts({
domains: search.domains,
contexts: [user_context].concat(search.contexts.concat(controllerContext || [])),
group_by_seq: search.groupbys || [],
});
if (!_.isEmpty(results.group_by)) {
results.context.group_by = results.group_by;
}
// Don't save user_context keys in the custom filter, otherwise end
// up with e.g. wrong uid or lang stored *and used in subsequent
// reqs*
var ctx = results.context;
_(_.keys(session.user_context)).each(function (key) {
delete ctx[key];
});
var filter = {
name: filter_name,
user_id: shared_filter ? false : session.uid,
model_id: this.target_model,
context: results.context,
domain: results.domain,
sort: JSON.stringify(this.searchview.dataset._sort),
is_default: default_filter,
action_id: this.action_id,
};
return data_manager.create_filter(filter).done(function (id) {
filter.id = id;
self.toggle_save_menu(false);
self.$save_name.find('input').val('').prop('checked', false);
self.add_filter(filter);
self.append_filter(filter);
self.toggle_filter(filter, true);
});
},
get_default_filter: function () {
var personal_filter = _.find(this.filters, function (filter) {
return filter.user_id && filter.is_default;
});
if (personal_filter) {
return personal_filter;
}
return _.find(this.filters, function (filter) {
return !filter.user_id && filter.is_default;
});
},
/**
* Generates a mapping key (in the filters and $filter mappings) for the
* filter descriptor object provided (as returned by ``get_filters``).
*
* The mapping key is guaranteed to be unique for a given (user_id, name)
* pair.
*
* @param {Object} filter
* @param {String} filter.name
* @param {Number|Pair<Number, String>} [filter.user_id]
* @return {String} mapping key corresponding to the filter
*/
key_for: function (filter) {
var user_id = filter.user_id,
action_id = filter.action_id,
uid = (user_id instanceof Array) ? user_id[0] : user_id,
act_id = (action_id instanceof Array) ? action_id[0] : action_id;
return _.str.sprintf('(%s)(%s)%s', uid, act_id, filter.name);
},
/**
* Generates a :js:class:`~instance.web.search.Facet` descriptor from a
* filter descriptor
*
* @param {Object} filter
* @param {String} filter.name
* @param {Object} [filter.context]
* @param {Array} [filter.domain]
* @return {Object}
*/
facet_for: function (filter) {
return {
category: _t("Custom Filter"),
icon: 'fa-star',
field: {
get_context: function () { return filter.context; },
get_groupby: function () { return [filter.context]; },
get_domain: function () { return filter.domain; }
},
_id: filter.id,
is_custom_filter: true,
values: [{label: filter.name, value: null}]
};
},
clear_selection: function () {
this.$('li.selected').removeClass('selected');
},
/**
* Adds a filter description to the filters dict
* @param {Object} [filter] the filter description
*/
add_filter: function (filter) {
this.filters[this.key_for(filter)] = filter;
},
/**
* Creates a $filter JQuery node, adds it to the $filters dict and appends it to the filter menu
* @param {Object} [filter] the filter description
*/
append_filter: function (filter) {
var self = this;
var key = this.key_for(filter);
if (filter.user_id) {
this.$user_divider.show();
} else {
this.$shared_divider.show();
}
if (!(key in this.$filters)) {
var $filter = $('<li>')
.addClass(filter.user_id ? 'o-searchview-custom-private'
: 'o-searchview-custom-public')
.append($('<a>', {'href': '#'}).text(filter.name))
.append($('<span>', {
class: 'fa fa-trash-o o-remove-filter',
on: {
click: function (event) {
event.stopImmediatePropagation();
self.remove_filter(filter, $filter, key);
},
},
}))
.insertBefore(filter.user_id ? this.$user_divider : this.$shared_divider);
this.$filters[key] = $filter;
}
this.$filters[key].unbind('click').click(function () {
self.toggle_filter(filter);
});
},
toggle_filter: function (filter, preventSearch) {
var current = this.query.find(function (facet) {
return facet.get('_id') === filter.id;
});
if (current) {
this.query.remove(current);
this.$filters[this.key_for(filter)].removeClass('selected');
return;
}
this.query.reset([this.facet_for(filter)], {
preventSearch: preventSearch || false});
// Load sort settings on view
if (!_.isUndefined(filter.sort)){
var sort_items = JSON.parse(filter.sort);
this.searchview.dataset.set_sort(sort_items);
}
this.$filters[this.key_for(filter)].addClass('selected');
},
remove_filter: function (filter, $filter, key) {
var self = this;
var global_warning = _t("This filter is global and will be removed for everybody if you continue."),
warning = _t("Are you sure that you want to remove this filter?");
if (!confirm(filter.user_id ? warning : global_warning)) {
return;
}
return data_manager
.delete_filter(filter)
.done(function () {
$filter.remove();
delete self.$filters[key];
delete self.filters[key];
var has_user_filter = _.find(self.filters, function(filter) { return filter.user_id; });
var has_shared_filer = _.find(self.filters, function(filter) { return !filter.user_id; });
if (!has_user_filter) {
self.$user_divider.hide();
}
if (!has_shared_filer) {
self.$shared_divider.hide();
}
});
},
});
});
flectra.define('web.FilterMenu', function (require) {
"use strict";
var search_filters = require('web.search_filters');
var search_inputs = require('web.search_inputs');
var Widget = require('web.Widget');
return Widget.extend({
template: 'SearchView.FilterMenu',
events: {
'click .o_add_filter': function (event) {
event.preventDefault();
this.toggle_custom_filter_menu();
},
'click li': function (event) {
event.preventDefault();
event.stopImmediatePropagation();
},
'hidden.bs.dropdown': function () {
this.toggle_custom_filter_menu(false);
},
'click .o_add_condition': 'append_proposition',
'click .o_apply_filter': 'commit_search',
'keyup .o_searchview_extended_prop_value': function (ev) {
if (ev.which === $.ui.keyCode.ENTER) {
this.commit_search();
}
},
},
init: function (parent, filters, fields) {
this._super(parent);
this.filters = filters || [];
this.searchview = parent;
this.propositions = [];
this.custom_filters_open = false;
this.fields = _.pick(fields, function (field, name) {
return field.selectable !== false && name !== 'id';
});
this.fields.id = { string: 'ID', type: 'id', searchable: true };
},
start: function () {
var self = this;
this.$menu = this.$('.o_filters_menu');
this.$add_filter = this.$('.o_add_filter');
this.$apply_filter = this.$('.o_apply_filter');
this.$add_filter_menu = this.$('.o_add_filter_menu');
_.each(this.filters, function (group) {
if (group.is_visible()) {
group.insertBefore(self.$add_filter);
$('<li class="divider">').insertBefore(self.$add_filter);
}
});
},
toggle_custom_filter_menu: function (is_open) {
var self = this;
this.custom_filters_open = !_.isUndefined(is_open) ? is_open : !this.custom_filters_open;
var def;
if (this.custom_filters_open && !this.propositions.length) {
def = this.append_proposition();
}
$.when(def).then(function () {
self.$add_filter
.toggleClass('o_closed_menu', !self.custom_filters_open)
.toggleClass('o_open_menu', self.custom_filters_open);
self.$add_filter_menu.toggle(self.custom_filters_open);
self.$('.o_filter_condition').toggle(self.custom_filters_open);
});
},
append_proposition: function () {
var prop = new search_filters.ExtendedSearchProposition(this, this.fields);
this.propositions.push(prop);
this.$apply_filter.prop('disabled', false);
return prop.insertBefore(this.$add_filter_menu);
},
remove_proposition: function (prop) {
this.propositions = _.without(this.propositions, prop);
if (!this.propositions.length) {
this.$apply_filter.prop('disabled', true);
}
prop.destroy();
},
commit_search: function () {
var filters = _.invoke(this.propositions, 'get_filter'),
filters_widgets = _.map(filters, function (filter) {
return new search_inputs.Filter(filter, this);
}),
filter_group = new search_inputs.FilterGroup(filters_widgets, this.searchview),
facets = filters_widgets.map(function (filter) {
return filter_group.make_facet([filter_group.make_value(filter)]);
});
filter_group.insertBefore(this.$add_filter);
$('<li class="divider">').insertBefore(this.$add_filter);
this.searchview.query.add(facets, {silent: true});
this.searchview.query.trigger('reset');
_.invoke(this.propositions, 'destroy');
this.propositions = [];
this.append_proposition();
this.toggle_custom_filter_menu(false);
},
});
});
flectra.define('web.GroupByMenu', function (require) {
"use strict";
var core = require('web.core');
var search_inputs = require('web.search_inputs');
var Widget = require('web.Widget');
var QWeb = core.qweb;
return Widget.extend({
template: 'SearchView.GroupByMenu',
events: {
'click li': function (event) {
event.stopImmediatePropagation();
},
'hidden.bs.dropdown': function () {
this.toggle_add_menu(false);
},
'click .o_add_custom_group a': function (event) {
event.preventDefault();
this.toggle_add_menu();
},
},
init: function (parent, groups, fields) {
var self = this;
this._super(parent);
this.searchview = parent;
this.groups = groups || [];
this.groupableFields = [];
var groupable_types = ['many2one', 'char', 'boolean', 'selection', 'date', 'datetime'];
_.each(fields, function (field, name) {
if (field.sortable && _.contains(groupable_types, field.type)) {
self.groupableFields.push(_.extend({}, field, {name: name}));
}
});
self.groupableFields = _.sortBy(this.groupableFields, 'string');
},
start: function () {
var self = this;
this.$menu = this.$('.o_group_by_menu');
var divider = this.$menu.find('.divider');
_.invoke(this.groups, 'insertBefore', divider);
if (this.groups.length) {
divider.show();
}
this.$add_group = this.$menu.find('.o_add_custom_group');
this.$menu.append(QWeb.render('GroupByMenuSelector', this));
this.$add_group_menu = this.$('.o_add_group');
this.$group_selector = this.$('.o_group_selector');
this.$('.o_select_group').click(function () {
self.toggle_add_menu(false);
var field = self.$group_selector.find(':selected').data('name');
self.add_groupby_to_menu(field);
});
},
toggle_add_menu: function (is_open) {
this.$add_group
.toggleClass('o_closed_menu', !(_.isUndefined(is_open)) ? !is_open : undefined)
.toggleClass('o_open_menu', is_open);
this.$add_group_menu.toggle(is_open);
if (this.$add_group.hasClass('o_open_menu')) {
this.$group_selector.focus();
}
},
add_groupby_to_menu: function (field_name) {
var filter = new search_inputs.Filter({attrs:{
context:"{'group_by':'" + field_name + "''}",
name: _.find(this.groupableFields, {name: field_name}).string,
}}, this.searchview);
var group = new search_inputs.FilterGroup([filter], this.searchview),
divider = this.$('.divider').show();
group.insertBefore(divider);
group.toggle(filter);
},
});
});