2011-03-30 14:00:48 +00:00
|
|
|
/*---------------------------------------------------------
|
2011-09-05 11:03:09 +00:00
|
|
|
* OpenERP web library
|
2011-03-30 14:00:48 +00:00
|
|
|
*---------------------------------------------------------*/
|
|
|
|
|
2013-08-06 12:50:22 +00:00
|
|
|
(function() {
|
|
|
|
|
|
|
|
var instance = openerp;
|
|
|
|
openerp.web.views = {};
|
2012-04-17 12:15:59 +00:00
|
|
|
var QWeb = instance.web.qweb,
|
|
|
|
_t = instance.web._t;
|
2011-08-24 15:13:57 +00:00
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.ActionManager = instance.web.Widget.extend({
|
2011-08-10 00:38:57 +00:00
|
|
|
init: function(parent) {
|
|
|
|
this._super(parent);
|
2011-12-14 18:04:42 +00:00
|
|
|
this.inner_action = null;
|
2012-07-11 12:47:43 +00:00
|
|
|
this.inner_widget = null;
|
2011-08-10 00:38:57 +00:00
|
|
|
this.dialog = null;
|
2012-07-11 13:05:25 +00:00
|
|
|
this.dialog_widget = null;
|
2012-07-11 13:38:30 +00:00
|
|
|
this.breadcrumbs = [];
|
2012-08-02 16:54:40 +00:00
|
|
|
this.on('history_back', this, function() {
|
|
|
|
return this.history_back();
|
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2012-07-03 16:17:20 +00:00
|
|
|
start: function() {
|
|
|
|
this._super.apply(this, arguments);
|
2012-09-12 12:54:45 +00:00
|
|
|
this.$el.on('click', 'a.oe_breadcrumb_item', this.on_breadcrumb_clicked);
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2014-04-02 16:50:08 +00:00
|
|
|
dialog_stop: function (reason) {
|
2011-08-10 00:38:57 +00:00
|
|
|
if (this.dialog) {
|
2014-04-02 16:50:08 +00:00
|
|
|
this.dialog.destroy(reason);
|
2011-08-10 00:38:57 +00:00
|
|
|
}
|
2012-11-19 11:11:30 +00:00
|
|
|
this.dialog = null;
|
2011-08-10 00:38:57 +00:00
|
|
|
},
|
2012-07-12 10:27:25 +00:00
|
|
|
/**
|
|
|
|
* Add a new item to the breadcrumb
|
|
|
|
*
|
2012-07-12 15:05:48 +00:00
|
|
|
* If the title of an item is an array, the multiple title mode is in use.
|
|
|
|
* (eg: a widget with multiple views might need to display a title for each view)
|
|
|
|
* In multiple title mode, the show() callback can check the index it receives
|
|
|
|
* in order to detect which of its titles has been clicked on by the user.
|
|
|
|
*
|
2012-07-12 10:27:25 +00:00
|
|
|
* @param {Object} item breadcrumb item
|
|
|
|
* @param {Object} item.widget widget containing the view(s) to be added to the breadcrumb added
|
2012-07-12 15:05:48 +00:00
|
|
|
* @param {Function} [item.show] triggered whenever the widget should be shown back
|
|
|
|
* @param {Function} [item.hide] triggered whenever the widget should be shown hidden
|
|
|
|
* @param {Function} [item.destroy] triggered whenever the widget should be destroyed
|
|
|
|
* @param {String|Array} [item.title] title(s) of the view(s) to be displayed in the breadcrumb
|
|
|
|
* @param {Function} [item.get_title] should return the title(s) of the view(s) to be displayed in the breadcrumb
|
2012-07-12 10:27:25 +00:00
|
|
|
*/
|
2012-07-11 13:38:30 +00:00
|
|
|
push_breadcrumb: function(item) {
|
2012-07-12 09:17:56 +00:00
|
|
|
var last = this.breadcrumbs.slice(-1)[0];
|
|
|
|
if (last) {
|
|
|
|
last.hide();
|
|
|
|
}
|
2013-07-25 10:07:49 +00:00
|
|
|
item = _.extend({
|
2012-07-12 09:55:17 +00:00
|
|
|
show: function(index) {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.widget.$el.show();
|
2012-07-12 09:37:08 +00:00
|
|
|
},
|
|
|
|
hide: function() {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.widget.$el.hide();
|
2012-07-12 09:37:08 +00:00
|
|
|
},
|
|
|
|
destroy: function() {
|
2012-07-12 14:46:23 +00:00
|
|
|
this.widget.destroy();
|
2012-07-12 09:37:08 +00:00
|
|
|
},
|
|
|
|
get_title: function() {
|
2012-07-12 14:46:23 +00:00
|
|
|
return this.title || this.widget.get('title');
|
2012-07-12 09:37:08 +00:00
|
|
|
}
|
2012-07-12 14:46:23 +00:00
|
|
|
}, item);
|
|
|
|
item.id = _.uniqueId('breadcrumb_');
|
2012-07-11 13:38:30 +00:00
|
|
|
this.breadcrumbs.push(item);
|
|
|
|
},
|
2012-08-02 16:54:40 +00:00
|
|
|
history_back: function() {
|
|
|
|
var last = this.breadcrumbs.slice(-1)[0];
|
|
|
|
if (!last) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var title = last.get_title();
|
2012-08-06 10:32:42 +00:00
|
|
|
if (_.isArray(title) && title.length > 1) {
|
2012-08-02 16:54:40 +00:00
|
|
|
return this.select_breadcrumb(this.breadcrumbs.length - 1, title.length - 2);
|
2012-08-06 10:32:42 +00:00
|
|
|
} else if (this.breadcrumbs.length === 1) {
|
|
|
|
// Only one single titled item in breadcrumb, most of the time you want to trigger back to home
|
|
|
|
return false;
|
2012-08-02 16:54:40 +00:00
|
|
|
} else {
|
2012-08-06 10:32:42 +00:00
|
|
|
var prev = this.breadcrumbs[this.breadcrumbs.length - 2];
|
|
|
|
title = prev.get_title();
|
|
|
|
return this.select_breadcrumb(this.breadcrumbs.length - 2, _.isArray(title) ? title.length - 1 : undefined);
|
2012-08-02 16:54:40 +00:00
|
|
|
}
|
|
|
|
},
|
2012-07-11 13:38:30 +00:00
|
|
|
on_breadcrumb_clicked: function(ev) {
|
|
|
|
var $e = $(ev.target);
|
2012-07-12 12:12:46 +00:00
|
|
|
var id = $e.data('id');
|
2012-08-02 16:54:40 +00:00
|
|
|
var index;
|
2012-07-12 12:12:46 +00:00
|
|
|
for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
|
2012-08-02 16:54:40 +00:00
|
|
|
if (this.breadcrumbs[i].id == id) {
|
|
|
|
index = i;
|
2012-07-12 12:12:46 +00:00
|
|
|
break;
|
2012-07-11 13:38:30 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-12 12:54:45 +00:00
|
|
|
var subindex = $e.parent().find('a.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
|
2012-08-02 16:54:40 +00:00
|
|
|
this.select_breadcrumb(index, subindex);
|
|
|
|
},
|
|
|
|
select_breadcrumb: function(index, subindex) {
|
2012-10-04 13:28:32 +00:00
|
|
|
var next_item = this.breadcrumbs[index + 1];
|
|
|
|
if (next_item && next_item.on_reverse_breadcrumb) {
|
|
|
|
next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget);
|
|
|
|
}
|
2012-08-02 16:54:40 +00:00
|
|
|
for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
|
|
|
|
if (i > index) {
|
2012-09-05 15:03:41 +00:00
|
|
|
if (this.remove_breadcrumb(i) === false) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-08-02 16:54:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
var item = this.breadcrumbs[index];
|
|
|
|
item.show(subindex);
|
2012-07-11 13:38:30 +00:00
|
|
|
this.inner_widget = item.widget;
|
2012-10-17 13:55:10 +00:00
|
|
|
this.inner_action = item.action;
|
2012-08-02 16:54:40 +00:00
|
|
|
return true;
|
2012-07-11 13:38:30 +00:00
|
|
|
},
|
|
|
|
clear_breadcrumbs: function() {
|
2012-09-05 15:03:41 +00:00
|
|
|
for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
|
|
|
|
if (this.remove_breadcrumb(0) === false) {
|
|
|
|
break;
|
|
|
|
}
|
2011-08-10 01:34:48 +00:00
|
|
|
}
|
2012-07-11 13:38:30 +00:00
|
|
|
},
|
|
|
|
remove_breadcrumb: function(index) {
|
|
|
|
var item = this.breadcrumbs.splice(index, 1)[0];
|
|
|
|
if (item) {
|
|
|
|
var dups = _.filter(this.breadcrumbs, function(it) {
|
|
|
|
return item.widget === it.widget;
|
|
|
|
});
|
|
|
|
if (!dups.length) {
|
2012-09-06 10:25:26 +00:00
|
|
|
if (this.getParent().has_uncommitted_changes()) {
|
2012-09-05 15:03:41 +00:00
|
|
|
this.inner_widget = item.widget;
|
2012-10-17 13:55:10 +00:00
|
|
|
this.inner_action = item.action;
|
2012-09-05 15:03:41 +00:00
|
|
|
this.breadcrumbs.splice(index, 0, item);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
item.destroy();
|
|
|
|
}
|
2012-07-11 13:38:30 +00:00
|
|
|
}
|
2011-08-19 14:39:47 +00:00
|
|
|
}
|
2012-09-05 15:03:41 +00:00
|
|
|
var last_widget = this.breadcrumbs.slice(-1)[0];
|
2012-10-17 13:55:10 +00:00
|
|
|
if (last_widget) {
|
|
|
|
this.inner_widget = last_widget.widget;
|
|
|
|
this.inner_action = last_widget.action;
|
|
|
|
}
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2013-11-18 11:22:11 +00:00
|
|
|
add_breadcrumb_url: function (url, label) {
|
|
|
|
// Add a pseudo breadcrumb that will redirect to an url
|
|
|
|
this.push_breadcrumb({
|
2013-11-18 12:38:08 +00:00
|
|
|
show: function() {
|
2013-11-18 11:22:11 +00:00
|
|
|
instance.web.redirect(url);
|
|
|
|
},
|
|
|
|
hide: function() {},
|
|
|
|
destroy: function() {},
|
|
|
|
get_title: function() {
|
|
|
|
return label;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2012-07-11 13:38:30 +00:00
|
|
|
get_title: function() {
|
|
|
|
var titles = [];
|
|
|
|
for (var i = 0; i < this.breadcrumbs.length; i += 1) {
|
|
|
|
var item = this.breadcrumbs[i];
|
|
|
|
var tit = item.get_title();
|
2013-03-14 10:52:13 +00:00
|
|
|
if (item.hide_breadcrumb) {
|
|
|
|
continue;
|
|
|
|
}
|
2012-07-11 13:38:30 +00:00
|
|
|
if (!_.isArray(tit)) {
|
|
|
|
tit = [tit];
|
|
|
|
}
|
|
|
|
for (var j = 0; j < tit.length; j += 1) {
|
|
|
|
var label = _.escape(tit[j]);
|
|
|
|
if (i === this.breadcrumbs.length - 1 && j === tit.length - 1) {
|
2012-09-12 12:54:45 +00:00
|
|
|
titles.push(_.str.sprintf('<span class="oe_breadcrumb_item">%s</span>', label));
|
2012-07-11 13:38:30 +00:00
|
|
|
} else {
|
|
|
|
titles.push(_.str.sprintf('<a href="#" class="oe_breadcrumb_item" data-id="%s">%s</a>', item.id, label));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-07-16 17:31:17 +00:00
|
|
|
return titles.join(' <span class="oe_fade">/</span> ');
|
2012-07-11 13:38:30 +00:00
|
|
|
},
|
2011-12-14 16:09:58 +00:00
|
|
|
do_push_state: function(state) {
|
2012-08-08 14:16:41 +00:00
|
|
|
state = state || {};
|
2012-02-21 15:30:23 +00:00
|
|
|
if (this.getParent() && this.getParent().do_push_state) {
|
2011-12-21 14:18:26 +00:00
|
|
|
if (this.inner_action) {
|
2012-10-12 12:54:18 +00:00
|
|
|
if (this.inner_action._push_me === false) {
|
|
|
|
// this action has been explicitly marked as not pushable
|
|
|
|
return;
|
|
|
|
}
|
2012-03-08 16:52:25 +00:00
|
|
|
state['title'] = this.inner_action.name;
|
2012-07-26 00:42:27 +00:00
|
|
|
if(this.inner_action.type == 'ir.actions.act_window') {
|
|
|
|
state['model'] = this.inner_action.res_model;
|
|
|
|
}
|
2012-10-23 12:28:33 +00:00
|
|
|
if (this.inner_action.menu_id) {
|
|
|
|
state['menu_id'] = this.inner_action.menu_id;
|
|
|
|
}
|
2011-12-21 14:18:26 +00:00
|
|
|
if (this.inner_action.id) {
|
2012-08-08 14:16:41 +00:00
|
|
|
state['action'] = this.inner_action.id;
|
|
|
|
} else if (this.inner_action.type == 'ir.actions.client') {
|
|
|
|
state['action'] = this.inner_action.tag;
|
2012-10-12 14:08:58 +00:00
|
|
|
var params = {};
|
|
|
|
_.each(this.inner_action.params, function(v, k) {
|
|
|
|
if(_.isString(v) || _.isNumber(v)) {
|
|
|
|
params[k] = v;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
state = _.extend(params || {}, state);
|
2011-12-21 14:18:26 +00:00
|
|
|
}
|
2013-02-11 14:10:56 +00:00
|
|
|
if (this.inner_action.context) {
|
2013-02-20 09:12:26 +00:00
|
|
|
var active_id = this.inner_action.context.active_id;
|
|
|
|
if (active_id) {
|
|
|
|
state["active_id"] = active_id;
|
2013-02-11 14:10:56 +00:00
|
|
|
}
|
2013-02-20 09:12:26 +00:00
|
|
|
var active_ids = this.inner_action.context.active_ids;
|
|
|
|
if (active_ids && !(active_ids.length === 1 && active_ids[0] === active_id)) {
|
|
|
|
// We don't push active_ids if it's a single element array containing the active_id
|
|
|
|
// This makes the url shorter in most cases.
|
2013-02-20 08:44:37 +00:00
|
|
|
state["active_ids"] = this.inner_action.context.active_ids.join(',');
|
2013-02-11 14:10:56 +00:00
|
|
|
}
|
|
|
|
}
|
2011-12-14 16:09:58 +00:00
|
|
|
}
|
2012-08-16 23:55:45 +00:00
|
|
|
if(!this.dialog) {
|
|
|
|
this.getParent().do_push_state(state);
|
|
|
|
}
|
2011-12-14 16:09:58 +00:00
|
|
|
}
|
2011-12-12 14:20:12 +00:00
|
|
|
},
|
2012-02-01 15:44:42 +00:00
|
|
|
do_load_state: function(state, warm) {
|
2011-12-15 14:29:39 +00:00
|
|
|
var self = this,
|
|
|
|
action_loaded;
|
2013-11-18 11:22:11 +00:00
|
|
|
if (!warm && 'return_label' in state) {
|
|
|
|
var return_url = state.return_url || document.referrer;
|
|
|
|
if (return_url) {
|
|
|
|
this.add_breadcrumb_url(return_url, state.return_label);
|
|
|
|
}
|
|
|
|
}
|
2012-08-08 14:16:41 +00:00
|
|
|
if (state.action) {
|
|
|
|
if (_.isString(state.action) && instance.web.client_actions.contains(state.action)) {
|
2012-11-08 17:59:10 +00:00
|
|
|
var action_client = {
|
|
|
|
type: "ir.actions.client",
|
|
|
|
tag: state.action,
|
|
|
|
params: state,
|
|
|
|
_push_me: state._push_me,
|
|
|
|
};
|
2013-11-18 11:22:11 +00:00
|
|
|
if (warm) {
|
|
|
|
this.null_action();
|
|
|
|
}
|
2012-08-08 14:16:41 +00:00
|
|
|
action_loaded = this.do_action(action_client);
|
|
|
|
} else {
|
|
|
|
var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action;
|
|
|
|
if (run_action) {
|
2013-02-11 14:10:56 +00:00
|
|
|
var add_context = {};
|
|
|
|
if (state.active_id) {
|
|
|
|
add_context.active_id = state.active_id;
|
|
|
|
}
|
|
|
|
if (state.active_ids) {
|
2013-02-12 11:57:40 +00:00
|
|
|
// The jQuery BBQ plugin does some parsing on values that are valid integers.
|
|
|
|
// It means that if there's only one item, it will do parseInt() on it,
|
|
|
|
// otherwise it will keep the comma seperated list as string.
|
2013-02-11 14:42:41 +00:00
|
|
|
add_context.active_ids = state.active_ids.toString().split(',').map(function(id) {
|
2013-02-12 13:30:25 +00:00
|
|
|
return parseInt(id, 10) || id;
|
2013-02-11 14:42:41 +00:00
|
|
|
});
|
2013-02-20 09:12:26 +00:00
|
|
|
} else if (state.active_id) {
|
|
|
|
add_context.active_ids = [state.active_id];
|
2013-02-11 14:10:56 +00:00
|
|
|
}
|
2013-04-08 10:01:36 +00:00
|
|
|
add_context.params = state;
|
2013-11-18 11:22:11 +00:00
|
|
|
if (warm) {
|
|
|
|
this.null_action();
|
|
|
|
}
|
2013-02-11 14:10:56 +00:00
|
|
|
action_loaded = this.do_action(state.action, { additional_context: add_context });
|
2012-11-13 14:00:02 +00:00
|
|
|
$.when(action_loaded || null).done(function() {
|
|
|
|
instance.webclient.menu.has_been_loaded.done(function() {
|
|
|
|
if (self.inner_action && self.inner_action.id) {
|
|
|
|
instance.webclient.menu.open_action(self.inner_action.id);
|
|
|
|
}
|
|
|
|
});
|
2012-08-08 14:16:41 +00:00
|
|
|
});
|
|
|
|
}
|
2011-12-15 14:29:39 +00:00
|
|
|
}
|
2012-01-27 13:24:38 +00:00
|
|
|
} else if (state.model && state.id) {
|
2011-12-21 14:18:26 +00:00
|
|
|
// TODO handle context & domain ?
|
2013-11-18 11:22:11 +00:00
|
|
|
if (warm) {
|
|
|
|
this.null_action();
|
|
|
|
}
|
2011-12-22 15:34:49 +00:00
|
|
|
var action = {
|
2011-12-21 14:18:26 +00:00
|
|
|
res_model: state.model,
|
|
|
|
res_id: state.id,
|
|
|
|
type: 'ir.actions.act_window',
|
2012-04-09 20:53:34 +00:00
|
|
|
views: [[false, 'form']]
|
2011-12-21 14:18:26 +00:00
|
|
|
};
|
|
|
|
action_loaded = this.do_action(action);
|
2012-01-31 20:51:52 +00:00
|
|
|
} else if (state.sa) {
|
2012-01-13 11:36:08 +00:00
|
|
|
// load session action
|
2013-11-18 11:22:11 +00:00
|
|
|
if (warm) {
|
|
|
|
this.null_action();
|
|
|
|
}
|
2012-10-30 14:06:30 +00:00
|
|
|
action_loaded = this.rpc('/web/session/get_session_action', {key: state.sa}).then(function(action) {
|
2012-01-13 11:36:08 +00:00
|
|
|
if (action) {
|
|
|
|
return self.do_action(action);
|
|
|
|
}
|
|
|
|
});
|
2011-12-09 16:31:14 +00:00
|
|
|
}
|
|
|
|
|
2012-10-30 14:06:30 +00:00
|
|
|
$.when(action_loaded || null).done(function() {
|
2012-07-11 12:47:43 +00:00
|
|
|
if (self.inner_widget && self.inner_widget.do_load_state) {
|
|
|
|
self.inner_widget.do_load_state(state, warm);
|
2011-12-15 14:29:39 +00:00
|
|
|
}
|
|
|
|
});
|
2011-08-18 22:57:21 +00:00
|
|
|
},
|
2013-02-11 14:10:56 +00:00
|
|
|
/**
|
|
|
|
* Execute an OpenERP action
|
|
|
|
*
|
|
|
|
* @param {Number|String|Object} Can be either an action id, a client action or an action descriptor.
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Boolean} [options.clear_breadcrumbs=false] Clear the breadcrumbs history list
|
|
|
|
* @param {Function} [options.on_reverse_breadcrumb] Callback to be executed whenever an anterior breadcrumb item is clicked on.
|
2013-03-14 10:52:13 +00:00
|
|
|
* @param {Function} [options.hide_breadcrumb] Do not display this widget's title in the breadcrumb
|
2013-02-11 14:10:56 +00:00
|
|
|
* @param {Function} [options.on_close] Callback to be executed when the dialog is closed (only relevant for target=new actions)
|
|
|
|
* @param {Function} [options.action_menu_id] Manually set the menu id on the fly.
|
|
|
|
* @param {Object} [options.additional_context] Additional context to be merged with the action's context.
|
|
|
|
* @return {jQuery.Deferred} Action loaded
|
|
|
|
*/
|
2012-10-17 14:55:48 +00:00
|
|
|
do_action: function(action, options) {
|
|
|
|
options = _.defaults(options || {}, {
|
|
|
|
clear_breadcrumbs: false,
|
|
|
|
on_reverse_breadcrumb: function() {},
|
2013-03-14 10:52:13 +00:00
|
|
|
hide_breadcrumb: false,
|
2012-10-17 14:55:48 +00:00
|
|
|
on_close: function() {},
|
2012-10-23 12:28:33 +00:00
|
|
|
action_menu_id: null,
|
2013-02-11 14:10:56 +00:00
|
|
|
additional_context: {},
|
2012-10-17 14:55:48 +00:00
|
|
|
});
|
2014-01-13 17:43:38 +00:00
|
|
|
|
2012-11-29 10:11:37 +00:00
|
|
|
if (action === false) {
|
|
|
|
action = { type: 'ir.actions.act_window_close' };
|
|
|
|
} else if (_.isString(action) && instance.web.client_actions.contains(action)) {
|
2012-11-05 13:00:53 +00:00
|
|
|
var action_client = { type: "ir.actions.client", tag: action, params: {} };
|
2012-10-17 14:55:48 +00:00
|
|
|
return this.do_action(action_client, options);
|
2012-07-26 01:36:28 +00:00
|
|
|
} else if (_.isNumber(action) || _.isString(action)) {
|
2011-12-09 16:31:14 +00:00
|
|
|
var self = this;
|
2012-10-30 14:06:30 +00:00
|
|
|
return self.rpc("/web/action/load", { action_id: action }).then(function(result) {
|
2012-10-17 14:55:48 +00:00
|
|
|
return self.do_action(result, options);
|
2011-12-09 16:31:14 +00:00
|
|
|
});
|
|
|
|
}
|
2012-11-26 14:05:25 +00:00
|
|
|
|
|
|
|
// Ensure context & domain are evaluated and can be manipulated/used
|
2013-02-11 14:10:56 +00:00
|
|
|
var ncontext = new instance.web.CompoundContext(options.additional_context, action.context || {});
|
|
|
|
action.context = instance.web.pyeval.eval('context', ncontext);
|
|
|
|
if (action.context.active_id || action.context.active_ids) {
|
|
|
|
// Here we assume that when an `active_id` or `active_ids` is used
|
|
|
|
// in the context, we are in a `related` action, so we disable the
|
|
|
|
// searchview's default custom filters.
|
|
|
|
action.context.search_disable_custom_filters = true;
|
2012-11-26 14:05:25 +00:00
|
|
|
}
|
|
|
|
if (action.domain) {
|
|
|
|
action.domain = instance.web.pyeval.eval(
|
2012-12-04 09:33:26 +00:00
|
|
|
'domain', action.domain, action.context || {});
|
2012-11-26 14:05:25 +00:00
|
|
|
}
|
|
|
|
|
2011-12-07 16:13:53 +00:00
|
|
|
if (!action.type) {
|
|
|
|
console.error("No type for action", action);
|
2012-09-05 13:45:53 +00:00
|
|
|
return $.Deferred().reject();
|
2011-12-07 16:13:53 +00:00
|
|
|
}
|
2011-08-10 00:38:57 +00:00
|
|
|
var type = action.type.replace(/\./g,'_');
|
|
|
|
var popup = action.target === 'new';
|
2012-08-11 17:58:52 +00:00
|
|
|
var inline = action.target === 'inline' || action.target === 'inlineview';
|
2014-11-04 16:17:45 +00:00
|
|
|
var form = _.str.startsWith(action.view_mode, 'form');
|
2013-01-14 09:30:14 +00:00
|
|
|
action.flags = _.defaults(action.flags || {}, {
|
2012-04-05 10:18:23 +00:00
|
|
|
views_switcher : !popup && !inline,
|
|
|
|
search_view : !popup && !inline,
|
|
|
|
action_buttons : !popup && !inline,
|
|
|
|
sidebar : !popup && !inline,
|
2014-11-04 16:17:45 +00:00
|
|
|
pager : (!popup || !form) && !inline,
|
2013-01-14 09:30:14 +00:00
|
|
|
display_title : !popup,
|
|
|
|
search_disable_custom_filters: action.context && action.context.search_disable_custom_filters
|
|
|
|
});
|
2012-10-23 12:28:33 +00:00
|
|
|
action.menu_id = options.action_menu_id;
|
2011-08-09 22:31:15 +00:00
|
|
|
if (!(type in this)) {
|
2011-12-07 16:13:53 +00:00
|
|
|
console.error("Action manager can't handle action of type " + action.type, action);
|
2012-09-05 13:45:53 +00:00
|
|
|
return $.Deferred().reject();
|
2011-06-30 13:01:33 +00:00
|
|
|
}
|
2012-10-17 14:55:48 +00:00
|
|
|
return this[type](action, options);
|
2011-06-30 13:01:33 +00:00
|
|
|
},
|
2011-12-07 10:50:08 +00:00
|
|
|
null_action: function() {
|
|
|
|
this.dialog_stop();
|
2012-07-11 13:38:30 +00:00
|
|
|
this.clear_breadcrumbs();
|
2011-12-07 10:50:08 +00:00
|
|
|
},
|
2012-10-03 11:01:12 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {Object} executor
|
|
|
|
* @param {Object} executor.action original action
|
|
|
|
* @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance
|
|
|
|
* @param {String} executor.klass CSS class to add on the dialog root, if action.target=new
|
|
|
|
* @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget
|
2012-10-17 14:55:48 +00:00
|
|
|
* @param {Object} options
|
2012-10-03 11:01:12 +00:00
|
|
|
* @return {*}
|
|
|
|
*/
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_common: function(executor, options) {
|
2012-10-03 11:01:12 +00:00
|
|
|
if (this.inner_widget && executor.action.target !== 'new') {
|
2012-09-06 10:25:26 +00:00
|
|
|
if (this.getParent().has_uncommitted_changes()) {
|
2012-09-05 13:45:53 +00:00
|
|
|
return $.Deferred().reject();
|
2012-10-17 14:55:48 +00:00
|
|
|
} else if (options.clear_breadcrumbs) {
|
2012-09-05 12:35:32 +00:00
|
|
|
this.clear_breadcrumbs();
|
|
|
|
}
|
|
|
|
}
|
2012-10-03 11:01:12 +00:00
|
|
|
var widget = executor.widget();
|
|
|
|
if (executor.action.target === 'new') {
|
2014-09-02 15:32:37 +00:00
|
|
|
var pre_dialog = this.dialog;
|
|
|
|
if (pre_dialog){
|
2014-09-02 17:42:24 +00:00
|
|
|
// prevent previous dialog to consider itself closed,
|
|
|
|
// right now, as we're opening a new one (prevents
|
|
|
|
// reload of original form view)
|
2014-09-02 15:32:37 +00:00
|
|
|
pre_dialog.off('closing', null, pre_dialog.on_close);
|
|
|
|
}
|
2012-11-15 15:10:46 +00:00
|
|
|
if (this.dialog_widget && !this.dialog_widget.isDestroyed()) {
|
2012-10-19 13:28:27 +00:00
|
|
|
this.dialog_widget.destroy();
|
2011-07-14 10:22:43 +00:00
|
|
|
}
|
2014-09-02 17:42:24 +00:00
|
|
|
// explicitly passing a closing action to dialog_stop() prevents
|
|
|
|
// it from reloading the original form view
|
2014-04-02 16:50:08 +00:00
|
|
|
this.dialog_stop(executor.action);
|
2012-11-15 15:10:46 +00:00
|
|
|
this.dialog = new instance.web.Dialog(this, {
|
|
|
|
dialogClass: executor.klass,
|
|
|
|
});
|
2014-09-02 17:42:24 +00:00
|
|
|
|
|
|
|
// chain on_close triggers with previous dialog, if any
|
2014-09-02 15:32:37 +00:00
|
|
|
this.dialog.on_close = function(){
|
|
|
|
options.on_close.apply(null, arguments);
|
|
|
|
if (pre_dialog && pre_dialog.on_close){
|
2014-09-02 17:42:24 +00:00
|
|
|
// no parameter passed to on_close as this will
|
|
|
|
// only be called when the last dialog is truly
|
|
|
|
// closing, and *should* trigger a reload of the
|
|
|
|
// underlying form view (see comments above)
|
2014-09-02 15:32:37 +00:00
|
|
|
pre_dialog.on_close();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
this.dialog.on("closing", null, this.dialog.on_close);
|
2012-10-03 11:01:12 +00:00
|
|
|
this.dialog.dialog_title = executor.action.name;
|
2012-10-16 12:41:35 +00:00
|
|
|
if (widget instanceof instance.web.ViewManager) {
|
2012-10-16 12:56:27 +00:00
|
|
|
_.extend(widget.flags, {
|
|
|
|
$buttons: this.dialog.$buttons,
|
|
|
|
footer_to_buttons: true,
|
|
|
|
});
|
2012-10-16 12:41:35 +00:00
|
|
|
}
|
2012-07-26 16:16:38 +00:00
|
|
|
this.dialog_widget = widget;
|
2012-10-19 13:28:27 +00:00
|
|
|
this.dialog_widget.setParent(this.dialog);
|
2012-10-03 11:01:12 +00:00
|
|
|
var initialized = this.dialog_widget.appendTo(this.dialog.$el);
|
2011-08-10 23:02:13 +00:00
|
|
|
this.dialog.open();
|
2012-10-03 11:01:12 +00:00
|
|
|
return initialized;
|
2011-08-10 00:38:57 +00:00
|
|
|
} else {
|
2014-09-02 17:42:24 +00:00
|
|
|
// explicitly passing a closing action to dialog_stop() prevents
|
|
|
|
// it from reloading the original form view - we're opening a
|
|
|
|
// completely new action anyway
|
2014-04-02 16:50:08 +00:00
|
|
|
this.dialog_stop(executor.action);
|
2012-10-03 11:01:12 +00:00
|
|
|
this.inner_action = executor.action;
|
2012-07-26 16:16:38 +00:00
|
|
|
this.inner_widget = widget;
|
2012-10-03 11:01:12 +00:00
|
|
|
executor.post_process(widget);
|
|
|
|
return this.inner_widget.appendTo(this.$el);
|
2011-08-10 00:38:57 +00:00
|
|
|
}
|
2011-07-13 14:25:02 +00:00
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_act_window: function (action, options) {
|
2012-10-03 11:01:12 +00:00
|
|
|
var self = this;
|
|
|
|
|
|
|
|
return this.ir_actions_common({
|
|
|
|
widget: function () { return new instance.web.ViewManagerAction(self, action); },
|
|
|
|
action: action,
|
|
|
|
klass: 'oe_act_window',
|
2013-03-14 10:52:13 +00:00
|
|
|
post_process: function (widget) {
|
|
|
|
widget.add_breadcrumb({
|
|
|
|
on_reverse_breadcrumb: options.on_reverse_breadcrumb,
|
|
|
|
hide_breadcrumb: options.hide_breadcrumb,
|
|
|
|
});
|
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
}, options);
|
2012-07-26 16:16:38 +00:00
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_client: function (action, options) {
|
2012-10-03 11:01:12 +00:00
|
|
|
var self = this;
|
|
|
|
var ClientWidget = instance.web.client_actions.get_object(action.tag);
|
2012-10-03 11:42:58 +00:00
|
|
|
|
|
|
|
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
|
|
|
|
var next;
|
2013-07-25 10:07:49 +00:00
|
|
|
if ((next = ClientWidget(this, action))) {
|
2012-10-17 14:55:48 +00:00
|
|
|
return this.do_action(next, options);
|
2012-10-03 11:42:58 +00:00
|
|
|
}
|
|
|
|
return $.when();
|
|
|
|
}
|
|
|
|
|
2012-10-03 11:01:12 +00:00
|
|
|
return this.ir_actions_common({
|
2012-11-05 13:00:53 +00:00
|
|
|
widget: function () { return new ClientWidget(self, action); },
|
2012-10-03 11:01:12 +00:00
|
|
|
action: action,
|
|
|
|
klass: 'oe_act_client',
|
|
|
|
post_process: function(widget) {
|
|
|
|
self.push_breadcrumb({
|
|
|
|
widget: widget,
|
2012-10-04 13:28:32 +00:00
|
|
|
title: action.name,
|
2012-10-17 14:55:48 +00:00
|
|
|
on_reverse_breadcrumb: options.on_reverse_breadcrumb,
|
2013-03-14 10:52:13 +00:00
|
|
|
hide_breadcrumb: options.hide_breadcrumb,
|
2012-10-03 11:01:12 +00:00
|
|
|
});
|
|
|
|
if (action.tag !== 'reload') {
|
|
|
|
self.do_push_state({});
|
|
|
|
}
|
|
|
|
}
|
2012-10-17 14:55:48 +00:00
|
|
|
}, options);
|
2012-07-26 16:16:38 +00:00
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_act_window_close: function (action, options) {
|
|
|
|
if (!this.dialog) {
|
|
|
|
options.on_close();
|
2011-10-05 10:17:14 +00:00
|
|
|
}
|
2011-08-11 01:52:32 +00:00
|
|
|
this.dialog_stop();
|
2012-11-28 15:25:24 +00:00
|
|
|
return $.when();
|
2011-06-30 13:01:33 +00:00
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_server: function (action, options) {
|
2011-06-30 13:01:33 +00:00
|
|
|
var self = this;
|
2011-09-05 11:03:09 +00:00
|
|
|
this.rpc('/web/action/run', {
|
2011-06-30 13:01:33 +00:00
|
|
|
action_id: action.id,
|
2011-09-12 13:07:27 +00:00
|
|
|
context: action.context || {}
|
2012-10-30 14:06:30 +00:00
|
|
|
}).done(function (action) {
|
2013-07-25 10:33:01 +00:00
|
|
|
self.do_action(action, options);
|
2011-06-30 13:01:33 +00:00
|
|
|
});
|
2011-07-01 11:57:26 +00:00
|
|
|
},
|
2012-10-17 14:55:48 +00:00
|
|
|
ir_actions_report_xml: function(action, options) {
|
2011-09-15 13:12:01 +00:00
|
|
|
var self = this;
|
2012-07-24 14:12:20 +00:00
|
|
|
instance.web.blockUI();
|
2012-11-28 15:25:24 +00:00
|
|
|
return instance.web.pyeval.eval_domains_and_contexts({
|
2011-09-15 13:12:01 +00:00
|
|
|
contexts: [action.context],
|
|
|
|
domains: []
|
2012-11-28 15:25:24 +00:00
|
|
|
}).then(function(res) {
|
2011-09-15 13:12:01 +00:00
|
|
|
action = _.clone(action);
|
|
|
|
action.context = res.context;
|
2013-06-21 12:44:49 +00:00
|
|
|
|
|
|
|
// iOS devices doesn't allow iframe use the way we do it,
|
|
|
|
// opening a new window seems the best way to workaround
|
|
|
|
if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
|
|
|
|
var params = {
|
|
|
|
action: JSON.stringify(action),
|
|
|
|
token: new Date().getTime()
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
|
|
|
var url = self.session.url('/web/report', params);
|
2013-06-21 12:44:49 +00:00
|
|
|
instance.web.unblockUI();
|
|
|
|
$('<a href="'+url+'" target="_blank"></a>')[0].click();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-14 13:06:00 +00:00
|
|
|
var c = instance.webclient.crashmanager;
|
2012-11-28 15:25:24 +00:00
|
|
|
return $.Deferred(function (d) {
|
|
|
|
self.session.get_file({
|
|
|
|
url: '/web/report',
|
|
|
|
data: {action: JSON.stringify(action)},
|
|
|
|
complete: instance.web.unblockUI,
|
|
|
|
success: function(){
|
|
|
|
if (!self.dialog) {
|
|
|
|
options.on_close();
|
|
|
|
}
|
|
|
|
self.dialog_stop();
|
|
|
|
d.resolve();
|
|
|
|
},
|
|
|
|
error: function () {
|
|
|
|
c.rpc_error.apply(c, arguments);
|
|
|
|
d.reject();
|
2011-11-21 09:14:51 +00:00
|
|
|
}
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-11-28 15:25:24 +00:00
|
|
|
});
|
2011-08-29 15:24:38 +00:00
|
|
|
});
|
2011-10-19 12:56:04 +00:00
|
|
|
},
|
|
|
|
ir_actions_act_url: function (action) {
|
2013-11-18 10:21:12 +00:00
|
|
|
if (action.target === 'self') {
|
|
|
|
instance.web.redirect(action.url);
|
|
|
|
} else {
|
|
|
|
window.open(action.url, '_blank');
|
|
|
|
}
|
2012-11-28 15:25:24 +00:00
|
|
|
return $.when();
|
2011-12-12 10:39:05 +00:00
|
|
|
},
|
2011-07-19 10:27:07 +00:00
|
|
|
});
|
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.ViewManager = instance.web.Widget.extend({
|
2011-09-12 10:53:25 +00:00
|
|
|
template: "ViewManager",
|
2011-12-14 15:29:03 +00:00
|
|
|
init: function(parent, dataset, views, flags) {
|
2011-08-10 00:38:57 +00:00
|
|
|
this._super(parent);
|
2012-10-17 13:55:10 +00:00
|
|
|
this.url_states = {};
|
2011-09-28 15:16:13 +00:00
|
|
|
this.model = dataset ? dataset.model : undefined;
|
2011-04-06 21:10:37 +00:00
|
|
|
this.dataset = dataset;
|
2011-03-30 14:00:48 +00:00
|
|
|
this.searchview = null;
|
2011-03-31 12:25:22 +00:00
|
|
|
this.active_view = null;
|
2011-12-16 11:20:54 +00:00
|
|
|
this.views_src = _.map(views, function(x) {
|
|
|
|
if (x instanceof Array) {
|
2012-12-21 11:36:57 +00:00
|
|
|
var view_type = x[1];
|
|
|
|
var View = instance.web.views.get_object(view_type, true);
|
|
|
|
var view_label = View ? View.prototype.display_name : (void 'nope');
|
2011-12-16 11:20:54 +00:00
|
|
|
return {
|
|
|
|
view_id: x[0],
|
2012-12-21 11:36:57 +00:00
|
|
|
view_type: view_type,
|
|
|
|
label: view_label,
|
|
|
|
button_label: View ? _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}) : (void 'nope'),
|
2011-12-16 11:20:54 +00:00
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
});
|
2013-04-17 13:34:38 +00:00
|
|
|
this.ActionManager = parent;
|
2011-03-30 14:00:48 +00:00
|
|
|
this.views = {};
|
2011-12-14 15:29:03 +00:00
|
|
|
this.flags = flags || {};
|
2012-04-17 12:15:59 +00:00
|
|
|
this.registry = instance.web.views;
|
2011-12-07 10:45:03 +00:00
|
|
|
this.views_history = [];
|
2012-12-13 14:38:49 +00:00
|
|
|
this.view_completely_inited = $.Deferred();
|
2011-04-05 10:24:40 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* @returns {jQuery.Deferred} initial view loading promise
|
|
|
|
*/
|
2011-04-06 00:51:36 +00:00
|
|
|
start: function() {
|
2011-07-29 14:01:06 +00:00
|
|
|
this._super();
|
2011-04-05 10:24:40 +00:00
|
|
|
var self = this;
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('.oe_view_manager_switch a').click(function() {
|
2012-10-10 11:30:25 +00:00
|
|
|
self.switch_mode($(this).data('view-type'));
|
2012-04-27 22:33:54 +00:00
|
|
|
}).tipsy();
|
2011-08-08 17:32:30 +00:00
|
|
|
var views_ids = {};
|
2011-04-06 00:51:36 +00:00
|
|
|
_.each(this.views_src, function(view) {
|
2011-07-26 21:00:05 +00:00
|
|
|
self.views[view.view_type] = $.extend({}, view, {
|
2011-12-06 14:04:52 +00:00
|
|
|
deferred : $.Deferred(),
|
2011-07-26 21:00:05 +00:00
|
|
|
controller : null,
|
|
|
|
options : _.extend({
|
2012-08-24 18:27:07 +00:00
|
|
|
$buttons : self.$el.find('.oe_view_manager_buttons'),
|
|
|
|
$sidebar : self.flags.sidebar ? self.$el.find('.oe_view_manager_sidebar') : undefined,
|
|
|
|
$pager : self.$el.find('.oe_view_manager_pager'),
|
2011-08-08 17:32:30 +00:00
|
|
|
action : self.action,
|
|
|
|
action_views_ids : views_ids
|
2011-11-17 11:18:57 +00:00
|
|
|
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
|
2011-07-26 21:00:05 +00:00
|
|
|
});
|
2014-02-19 16:31:34 +00:00
|
|
|
|
2011-08-08 17:32:30 +00:00
|
|
|
views_ids[view.view_type] = view.view_id;
|
2011-04-05 10:10:08 +00:00
|
|
|
});
|
2011-05-17 09:26:27 +00:00
|
|
|
if (this.flags.views_switcher === false) {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('.oe_view_manager_switch').hide();
|
2011-05-17 09:26:27 +00:00
|
|
|
}
|
2011-12-12 09:41:52 +00:00
|
|
|
// If no default view defined, switch to the first one in sequence
|
|
|
|
var default_view = this.flags.default_view || this.views_src[0].view_type;
|
2014-02-19 16:31:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
return this.switch_mode(default_view, null, this.flags[default_view] && this.flags[default_view].options);
|
|
|
|
|
|
|
|
|
2011-04-06 00:51:36 +00:00
|
|
|
},
|
2012-10-09 14:51:13 +00:00
|
|
|
switch_mode: function(view_type, no_store, view_options) {
|
2012-04-09 18:44:45 +00:00
|
|
|
var self = this;
|
|
|
|
var view = this.views[view_type];
|
|
|
|
var view_promise;
|
2012-09-05 12:35:32 +00:00
|
|
|
var form = this.views['form'];
|
|
|
|
if (!view || (form && form.controller && !form.controller.can_be_discarded())) {
|
2011-12-08 05:59:09 +00:00
|
|
|
return $.Deferred().reject();
|
2012-09-05 12:35:32 +00:00
|
|
|
}
|
2011-12-07 10:45:03 +00:00
|
|
|
if (!no_store) {
|
|
|
|
this.views_history.push(view_type);
|
|
|
|
}
|
2011-03-31 12:25:22 +00:00
|
|
|
this.active_view = view_type;
|
2011-12-09 14:46:38 +00:00
|
|
|
|
2011-03-31 13:20:58 +00:00
|
|
|
if (!view.controller) {
|
2012-07-05 19:22:27 +00:00
|
|
|
view_promise = this.do_create_view(view_type);
|
2012-01-18 14:48:26 +00:00
|
|
|
} else if (this.searchview
|
2012-01-25 14:19:45 +00:00
|
|
|
&& self.flags.auto_search
|
2012-01-18 14:48:26 +00:00
|
|
|
&& view.controller.searchable !== false) {
|
2012-10-30 14:06:30 +00:00
|
|
|
this.searchview.ready.done(this.searchview.do_search);
|
2011-03-31 13:20:58 +00:00
|
|
|
}
|
2011-04-01 09:06:53 +00:00
|
|
|
|
2011-09-28 15:32:54 +00:00
|
|
|
if (this.searchview) {
|
2013-01-14 09:30:14 +00:00
|
|
|
this.searchview[(view.controller.searchable === false || this.searchview.options.hidden) ? 'hide' : 'show']();
|
2011-04-01 10:45:00 +00:00
|
|
|
}
|
|
|
|
|
2012-10-10 11:30:25 +00:00
|
|
|
this.$el.find('.oe_view_manager_switch a').parent().removeClass('active');
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el
|
2012-04-09 22:10:56 +00:00
|
|
|
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
|
|
|
|
.parent().addClass('active');
|
2014-03-18 16:56:22 +00:00
|
|
|
this.$el.attr("data-view-type", view_type);
|
2012-11-13 15:36:10 +00:00
|
|
|
return $.when(view_promise).done(function () {
|
2011-12-20 17:37:42 +00:00
|
|
|
_.each(_.keys(self.views), function(view_name) {
|
|
|
|
var controller = self.views[view_name].controller;
|
|
|
|
if (controller) {
|
2013-02-20 13:26:34 +00:00
|
|
|
var container = self.$el.find("> .oe_view_manager_body > .oe_view_manager_view_" + view_name);
|
2011-12-20 17:37:42 +00:00
|
|
|
if (view_name === view_type) {
|
2012-06-18 11:36:59 +00:00
|
|
|
container.show();
|
2012-06-07 16:08:01 +00:00
|
|
|
controller.do_show(view_options || {});
|
2011-12-20 17:37:42 +00:00
|
|
|
} else {
|
2012-06-18 11:36:59 +00:00
|
|
|
container.hide();
|
2011-12-20 17:37:42 +00:00
|
|
|
controller.do_hide();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2012-10-10 11:30:25 +00:00
|
|
|
self.trigger('switch_mode', view_type, no_store, view_options);
|
2011-10-25 14:37:38 +00:00
|
|
|
});
|
2011-03-30 14:00:48 +00:00
|
|
|
},
|
2012-07-05 19:22:27 +00:00
|
|
|
do_create_view: function(view_type) {
|
|
|
|
// Lazy loading of views
|
|
|
|
var self = this;
|
|
|
|
var view = this.views[view_type];
|
2012-08-11 17:58:52 +00:00
|
|
|
var viewclass = this.registry.get_object(view_type);
|
2012-07-05 19:22:27 +00:00
|
|
|
var options = _.clone(view.options);
|
2012-08-11 17:58:52 +00:00
|
|
|
if (view_type === "form" && this.action && (this.action.target == 'new' || this.action.target == 'inline')) {
|
|
|
|
options.initial_mode = 'edit';
|
2012-07-05 19:22:27 +00:00
|
|
|
}
|
2012-08-11 17:58:52 +00:00
|
|
|
var controller = new viewclass(this, this.dataset, view.view_id, options);
|
2012-07-05 19:22:27 +00:00
|
|
|
|
2012-08-02 16:54:40 +00:00
|
|
|
controller.on('history_back', this, function() {
|
|
|
|
var am = self.getParent();
|
|
|
|
if (am && am.trigger) {
|
|
|
|
return am.trigger('history_back');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2012-07-05 19:22:27 +00:00
|
|
|
controller.on("change:title", this, function() {
|
|
|
|
if (self.active_view === view_type) {
|
|
|
|
self.set_title(controller.get('title'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (view.embedded_view) {
|
|
|
|
controller.set_embedded_view(view.embedded_view);
|
|
|
|
}
|
2012-10-10 11:30:25 +00:00
|
|
|
controller.on('switch_mode', self, this.switch_mode);
|
2012-10-11 13:00:11 +00:00
|
|
|
controller.on('previous_view', self, this.prev_view);
|
|
|
|
|
2013-02-20 13:26:34 +00:00
|
|
|
var container = this.$el.find("> .oe_view_manager_body > .oe_view_manager_view_" + view_type);
|
2012-07-05 19:22:27 +00:00
|
|
|
var view_promise = controller.appendTo(container);
|
|
|
|
this.views[view_type].controller = controller;
|
2012-10-30 14:06:30 +00:00
|
|
|
return $.when(view_promise).done(function() {
|
2014-07-29 09:35:19 +00:00
|
|
|
self.views[view_type].deferred.resolve(view_type);
|
2012-07-05 19:22:27 +00:00
|
|
|
if (self.searchview
|
|
|
|
&& self.flags.auto_search
|
|
|
|
&& view.controller.searchable !== false) {
|
2012-10-30 14:06:30 +00:00
|
|
|
self.searchview.ready.done(self.searchview.do_search);
|
2012-12-13 14:38:49 +00:00
|
|
|
} else {
|
|
|
|
self.view_completely_inited.resolve();
|
2012-07-05 19:22:27 +00:00
|
|
|
}
|
2012-10-12 08:54:17 +00:00
|
|
|
self.trigger("controller_inited",view_type,controller);
|
2012-07-05 19:22:27 +00:00
|
|
|
});
|
|
|
|
},
|
2013-02-04 12:43:48 +00:00
|
|
|
/**
|
|
|
|
* @returns {Number|Boolean} the view id of the given type, false if not found
|
|
|
|
*/
|
|
|
|
get_view_id: function(view_type) {
|
|
|
|
return this.views[view_type] && this.views[view_type].view_id || false;
|
|
|
|
},
|
2012-07-05 19:22:27 +00:00
|
|
|
set_title: function(title) {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('.oe_view_title_text:first').text(title);
|
2012-07-05 19:22:27 +00:00
|
|
|
},
|
2013-03-14 10:52:13 +00:00
|
|
|
add_breadcrumb: function(options) {
|
2013-07-25 10:07:49 +00:00
|
|
|
options = options || {};
|
2012-07-11 13:38:30 +00:00
|
|
|
var self = this;
|
|
|
|
var views = [this.active_view || this.views_src[0].view_type];
|
2012-10-10 09:37:37 +00:00
|
|
|
this.on('switch_mode', self, function(mode) {
|
2012-07-11 13:38:30 +00:00
|
|
|
var last = views.slice(-1)[0];
|
|
|
|
if (mode !== last) {
|
|
|
|
if (mode !== 'form') {
|
|
|
|
views.length = 0;
|
|
|
|
}
|
|
|
|
views.push(mode);
|
|
|
|
}
|
|
|
|
});
|
2013-03-14 10:52:13 +00:00
|
|
|
var item = _.extend({
|
2012-07-11 13:38:30 +00:00
|
|
|
widget: this,
|
2012-07-30 13:58:00 +00:00
|
|
|
action: this.action,
|
2012-08-02 16:54:40 +00:00
|
|
|
show: function(index) {
|
2012-07-11 13:38:30 +00:00
|
|
|
var view_to_select = views[index];
|
2012-10-17 13:55:10 +00:00
|
|
|
var state = self.url_states[view_to_select];
|
|
|
|
self.do_push_state(state || {});
|
2012-10-30 14:06:30 +00:00
|
|
|
$.when(self.switch_mode(view_to_select)).done(function() {
|
2012-10-17 15:55:25 +00:00
|
|
|
self.$el.show();
|
|
|
|
});
|
2012-07-11 13:38:30 +00:00
|
|
|
},
|
|
|
|
get_title: function() {
|
2012-07-30 13:58:00 +00:00
|
|
|
var id;
|
|
|
|
var currentIndex;
|
|
|
|
_.each(self.getParent().breadcrumbs, function(bc, i) {
|
|
|
|
if (bc.widget === self) {
|
|
|
|
currentIndex = i;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
var next = self.getParent().breadcrumbs.slice(currentIndex + 1)[0];
|
|
|
|
var titles = _.map(views, function(v) {
|
|
|
|
var controller = self.views[v].controller;
|
|
|
|
if (v === 'form') {
|
|
|
|
id = controller.datarecord.id;
|
|
|
|
}
|
|
|
|
return controller.get('title');
|
2012-07-11 13:38:30 +00:00
|
|
|
});
|
2012-09-12 16:29:11 +00:00
|
|
|
if (next && next.action && next.action.res_id && self.dataset &&
|
|
|
|
self.active_view === 'form' && self.dataset.model === next.action.res_model && id === next.action.res_id) {
|
2012-07-30 13:58:00 +00:00
|
|
|
// If the current active view is a formview and the next item in the breadcrumbs
|
|
|
|
// is an action on same object (model / res_id), then we omit the current formview's title
|
|
|
|
titles.pop();
|
|
|
|
}
|
|
|
|
return titles;
|
2013-03-14 10:52:13 +00:00
|
|
|
}
|
|
|
|
}, options);
|
|
|
|
this.getParent().push_breadcrumb(item);
|
2012-07-11 13:38:30 +00:00
|
|
|
},
|
2011-12-07 15:31:01 +00:00
|
|
|
/**
|
|
|
|
* Returns to the view preceding the caller view in this manager's
|
|
|
|
* navigation history (the navigation history is appended to via
|
2012-10-10 09:37:37 +00:00
|
|
|
* switch_mode)
|
2011-12-07 15:31:01 +00:00
|
|
|
*
|
2012-01-24 09:30:33 +00:00
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Boolean} [options.created=false] resource was created
|
|
|
|
* @param {String} [options.default=null] view to switch to if no previous view
|
2011-12-07 15:31:01 +00:00
|
|
|
* @returns {$.Deferred} switching end signal
|
|
|
|
*/
|
2012-10-11 13:00:11 +00:00
|
|
|
prev_view: function (options) {
|
2012-04-18 15:58:31 +00:00
|
|
|
options = options || {};
|
2011-12-07 15:31:01 +00:00
|
|
|
var current_view = this.views_history.pop();
|
2012-01-24 09:30:33 +00:00
|
|
|
var previous_view = this.views_history[this.views_history.length - 1] || options['default'];
|
|
|
|
if (options.created && current_view === 'form' && previous_view === 'list') {
|
2012-01-09 14:15:34 +00:00
|
|
|
// APR special case: "If creation mode from list (and only from a list),
|
|
|
|
// after saving, go to page view (don't come back in list)"
|
2012-10-10 11:30:25 +00:00
|
|
|
return this.switch_mode('form');
|
2012-01-24 09:30:33 +00:00
|
|
|
} else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
|
2012-01-09 14:15:34 +00:00
|
|
|
// APR special case: "If creation from dashboard, we have no previous view
|
2012-10-10 11:30:25 +00:00
|
|
|
return this.switch_mode('form');
|
2011-12-07 15:31:01 +00:00
|
|
|
}
|
2012-10-10 11:30:25 +00:00
|
|
|
return this.switch_mode(previous_view, true);
|
2011-12-07 10:45:03 +00:00
|
|
|
},
|
2011-09-28 15:32:54 +00:00
|
|
|
/**
|
|
|
|
* Sets up the current viewmanager's search view.
|
|
|
|
*
|
|
|
|
* @param {Number|false} view_id the view to use or false for a default one
|
|
|
|
* @returns {jQuery.Deferred} search view startup deferred
|
|
|
|
*/
|
|
|
|
setup_search_view: function(view_id, search_defaults) {
|
|
|
|
var self = this;
|
|
|
|
if (this.searchview) {
|
2012-02-21 16:29:12 +00:00
|
|
|
this.searchview.destroy();
|
2011-09-28 15:32:54 +00:00
|
|
|
}
|
2013-01-14 09:30:14 +00:00
|
|
|
var options = {
|
|
|
|
hidden: this.flags.search_view === false,
|
|
|
|
disable_custom_filters: this.flags.search_disable_custom_filters,
|
|
|
|
};
|
|
|
|
this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, options);
|
2011-09-28 15:32:54 +00:00
|
|
|
|
2012-10-18 12:46:07 +00:00
|
|
|
this.searchview.on('search_data', self, this.do_searchview_search);
|
2012-08-24 18:27:07 +00:00
|
|
|
return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
|
2011-09-28 15:32:54 +00:00
|
|
|
},
|
|
|
|
do_searchview_search: function(domains, contexts, groupbys) {
|
2011-09-29 09:53:05 +00:00
|
|
|
var self = this,
|
2011-12-12 14:16:56 +00:00
|
|
|
controller = this.views[this.active_view].controller,
|
|
|
|
action_context = this.action.context || {};
|
2012-11-23 11:39:32 +00:00
|
|
|
instance.web.pyeval.eval_domains_and_contexts({
|
2011-12-06 11:36:58 +00:00
|
|
|
domains: [this.action.domain || []].concat(domains || []),
|
2011-12-12 14:16:56 +00:00
|
|
|
contexts: [action_context].concat(contexts || []),
|
2011-12-06 11:36:58 +00:00
|
|
|
group_by_seq: groupbys || []
|
2012-10-30 14:06:30 +00:00
|
|
|
}).done(function (results) {
|
2014-02-12 14:13:46 +00:00
|
|
|
if (results.error) {
|
|
|
|
throw new Error(
|
|
|
|
_.str.sprintf(_t("Failed to evaluate search criterions")+": \n%s",
|
|
|
|
JSON.stringify(results.error)));
|
|
|
|
}
|
2012-04-23 09:02:32 +00:00
|
|
|
self.dataset._model = new instance.web.Model(
|
|
|
|
self.dataset.model, results.context, results.domain);
|
2011-12-12 14:16:56 +00:00
|
|
|
var groupby = results.group_by.length
|
|
|
|
? results.group_by
|
|
|
|
: action_context.group_by;
|
2012-03-06 20:59:55 +00:00
|
|
|
if (_.isString(groupby)) {
|
|
|
|
groupby = [groupby];
|
|
|
|
}
|
2012-12-13 14:38:49 +00:00
|
|
|
$.when(controller.do_search(results.domain, results.context, groupby || [])).then(function() {
|
|
|
|
self.view_completely_inited.resolve();
|
|
|
|
});
|
2011-12-06 11:36:58 +00:00
|
|
|
});
|
2011-09-28 15:32:54 +00:00
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
/**
|
2011-04-06 00:51:36 +00:00
|
|
|
* Called when one of the view want to execute an action
|
2011-03-30 14:00:48 +00:00
|
|
|
*/
|
2011-04-06 00:51:36 +00:00
|
|
|
on_action: function(action) {
|
|
|
|
},
|
|
|
|
on_create: function() {
|
|
|
|
},
|
|
|
|
on_remove: function() {
|
|
|
|
},
|
|
|
|
on_edit: function() {
|
2011-09-14 12:13:05 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Called by children view after executing an action
|
|
|
|
*/
|
2011-12-14 18:04:42 +00:00
|
|
|
on_action_executed: function () {
|
|
|
|
},
|
2011-04-06 00:51:36 +00:00
|
|
|
});
|
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.ViewManagerAction = instance.web.ViewManager.extend({
|
2011-09-13 06:33:46 +00:00
|
|
|
template:"ViewManagerAction",
|
2011-09-13 06:32:38 +00:00
|
|
|
/**
|
2012-04-17 12:15:59 +00:00
|
|
|
* @constructs instance.web.ViewManagerAction
|
|
|
|
* @extends instance.web.ViewManager
|
2011-09-13 06:32:38 +00:00
|
|
|
*
|
2012-04-17 12:15:59 +00:00
|
|
|
* @param {instance.web.ActionManager} parent parent object/widget
|
2011-09-13 06:32:38 +00:00
|
|
|
* @param {Object} action descriptor for the action this viewmanager needs to manage its views.
|
|
|
|
*/
|
2011-09-13 06:33:46 +00:00
|
|
|
init: function(parent, action) {
|
2011-09-13 06:47:45 +00:00
|
|
|
// dataset initialization will take the session from ``this``, so if we
|
|
|
|
// do not have it yet (and we don't, because we've not called our own
|
|
|
|
// ``_super()``) rpc requests will blow up.
|
2011-12-14 15:29:03 +00:00
|
|
|
var flags = action.flags || {};
|
2012-01-25 14:20:43 +00:00
|
|
|
if (!('auto_search' in flags)) {
|
|
|
|
flags.auto_search = action.auto_search !== false;
|
|
|
|
}
|
2011-12-07 15:59:13 +00:00
|
|
|
if (action.res_model == 'board.board' && action.view_mode === 'form') {
|
|
|
|
// Special case for Dashboards
|
2011-12-14 15:29:03 +00:00
|
|
|
_.extend(flags, {
|
2011-12-07 15:59:13 +00:00
|
|
|
views_switcher : false,
|
|
|
|
display_title : false,
|
|
|
|
search_view : false,
|
|
|
|
pager : false,
|
|
|
|
sidebar : false,
|
|
|
|
action_buttons : false
|
|
|
|
});
|
2011-05-18 12:52:51 +00:00
|
|
|
}
|
2011-12-14 15:29:03 +00:00
|
|
|
this._super(parent, null, action.views, flags);
|
|
|
|
this.session = parent.session;
|
|
|
|
this.action = action;
|
2012-04-17 12:15:59 +00:00
|
|
|
var dataset = new instance.web.DataSetSearch(this, action.res_model, action.context, action.domain);
|
2011-12-14 15:29:03 +00:00
|
|
|
if (action.res_id) {
|
|
|
|
dataset.ids.push(action.res_id);
|
|
|
|
dataset.index = 0;
|
|
|
|
}
|
|
|
|
this.dataset = dataset;
|
2011-04-06 00:51:36 +00:00
|
|
|
},
|
2011-09-13 06:32:38 +00:00
|
|
|
/**
|
|
|
|
* Initializes the ViewManagerAction: sets up the searchview (if the
|
|
|
|
* searchview is enabled in the manager's action flags), calls into the
|
|
|
|
* parent to initialize the primary view and (if the VMA has a searchview)
|
|
|
|
* launches an initial search after both views are done rendering.
|
|
|
|
*/
|
2011-04-06 00:51:36 +00:00
|
|
|
start: function() {
|
2011-09-28 10:44:58 +00:00
|
|
|
var self = this,
|
|
|
|
searchview_loaded,
|
|
|
|
search_defaults = {};
|
|
|
|
_.each(this.action.context, function (value, key) {
|
|
|
|
var match = /^search_default_(.*)$/.exec(key);
|
|
|
|
if (match) {
|
|
|
|
search_defaults[match[1]] = value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// init search view
|
|
|
|
var searchview_id = this.action['search_view_id'] && this.action['search_view_id'][0];
|
2011-04-12 07:50:37 +00:00
|
|
|
|
2011-09-28 10:44:58 +00:00
|
|
|
searchview_loaded = this.setup_search_view(searchview_id || false, search_defaults);
|
2011-08-19 05:53:54 +00:00
|
|
|
|
2011-09-13 06:32:38 +00:00
|
|
|
var main_view_loaded = this._super();
|
2011-09-06 13:05:25 +00:00
|
|
|
|
2012-12-13 14:38:49 +00:00
|
|
|
var manager_ready = $.when(searchview_loaded, main_view_loaded, this.view_completely_inited);
|
2011-09-14 10:08:33 +00:00
|
|
|
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('.oe_debug_view').change(this.on_debug_changed);
|
|
|
|
this.$el.addClass("oe_view_manager_" + (this.action.target || 'current'));
|
2011-09-13 06:38:30 +00:00
|
|
|
return manager_ready;
|
2011-04-06 00:51:36 +00:00
|
|
|
},
|
2011-12-13 11:35:21 +00:00
|
|
|
on_debug_changed: function (evt) {
|
2012-01-10 11:23:28 +00:00
|
|
|
var self = this,
|
|
|
|
$sel = $(evt.currentTarget),
|
2011-12-13 11:35:21 +00:00
|
|
|
$option = $sel.find('option:selected'),
|
2012-01-10 11:23:28 +00:00
|
|
|
val = $sel.val(),
|
|
|
|
current_view = this.views[this.active_view].controller;
|
2011-12-13 11:35:21 +00:00
|
|
|
switch (val) {
|
|
|
|
case 'fvg':
|
2012-04-17 12:15:59 +00:00
|
|
|
var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
|
2012-08-24 18:27:07 +00:00
|
|
|
$('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$el);
|
2011-12-13 11:35:21 +00:00
|
|
|
break;
|
2012-09-06 10:29:52 +00:00
|
|
|
case 'tests':
|
|
|
|
this.do_action({
|
2012-11-29 00:22:00 +00:00
|
|
|
name: _t("JS Tests"),
|
2012-09-06 10:29:52 +00:00
|
|
|
target: 'new',
|
|
|
|
type : 'ir.actions.act_url',
|
2012-10-25 15:47:45 +00:00
|
|
|
url: '/web/tests?mod=*'
|
|
|
|
});
|
2012-09-06 10:29:52 +00:00
|
|
|
break;
|
2012-01-24 17:01:11 +00:00
|
|
|
case 'perm_read':
|
|
|
|
var ids = current_view.get_selected_ids();
|
|
|
|
if (ids.length === 1) {
|
2012-10-30 14:06:30 +00:00
|
|
|
this.dataset.call('perm_read', [ids]).done(function(result) {
|
2012-04-17 12:15:59 +00:00
|
|
|
var dialog = new instance.web.Dialog(this, {
|
2012-01-24 17:01:11 +00:00
|
|
|
title: _.str.sprintf(_t("View Log (%s)"), self.dataset.model),
|
|
|
|
width: 400
|
|
|
|
}, QWeb.render('ViewManagerDebugViewLog', {
|
|
|
|
perm : result[0],
|
2012-04-17 12:15:59 +00:00
|
|
|
format : instance.web.format_value
|
2012-01-24 17:01:11 +00:00
|
|
|
})).open();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
break;
|
2012-04-10 11:28:34 +00:00
|
|
|
case 'toggle_layout_outline':
|
|
|
|
current_view.rendering_engine.toggle_layout_debugging();
|
|
|
|
break;
|
2012-11-22 09:07:54 +00:00
|
|
|
case 'set_defaults':
|
|
|
|
current_view.open_defaults_dialog();
|
|
|
|
break;
|
2012-09-03 13:29:23 +00:00
|
|
|
case 'translate':
|
|
|
|
this.do_action({
|
2012-11-29 00:22:00 +00:00
|
|
|
name: _t("Technical Translation"),
|
2012-09-03 13:29:23 +00:00
|
|
|
res_model : 'ir.translation',
|
|
|
|
domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
|
|
|
|
views: [[false, 'list'], [false, 'form']],
|
|
|
|
type : 'ir.actions.act_window',
|
|
|
|
view_type : "list",
|
|
|
|
view_mode : "list"
|
|
|
|
});
|
|
|
|
break;
|
2012-01-13 17:54:05 +00:00
|
|
|
case 'fields':
|
2012-10-30 14:06:30 +00:00
|
|
|
this.dataset.call('fields_get', [false, {}]).done(function (fields) {
|
2012-01-13 17:54:05 +00:00
|
|
|
var $root = $('<dl>');
|
|
|
|
_(fields).each(function (attributes, name) {
|
|
|
|
$root.append($('<dt>').append($('<h4>').text(name)));
|
2012-10-06 16:20:30 +00:00
|
|
|
var $attrs = $('<dl>').appendTo($('<dd>').appendTo($root));
|
2012-01-13 17:54:05 +00:00
|
|
|
_(attributes).each(function (def, name) {
|
|
|
|
if (def instanceof Object) {
|
|
|
|
def = JSON.stringify(def);
|
|
|
|
}
|
|
|
|
$attrs
|
|
|
|
.append($('<dt>').text(name))
|
|
|
|
.append($('<dd style="white-space: pre-wrap;">').text(def));
|
|
|
|
});
|
|
|
|
});
|
2012-04-17 12:15:59 +00:00
|
|
|
new instance.web.Dialog(self, {
|
2012-01-13 17:54:05 +00:00
|
|
|
title: _.str.sprintf(_t("Model %s fields"),
|
|
|
|
self.dataset.model),
|
|
|
|
width: '95%'}, $root).open();
|
|
|
|
});
|
|
|
|
break;
|
2012-01-10 11:23:28 +00:00
|
|
|
case 'edit_workflow':
|
|
|
|
return this.do_action({
|
|
|
|
res_model : 'workflow',
|
|
|
|
domain : [['osv', '=', this.dataset.model]],
|
|
|
|
views: [[false, 'list'], [false, 'form'], [false, 'diagram']],
|
|
|
|
type : 'ir.actions.act_window',
|
|
|
|
view_type : 'list',
|
|
|
|
view_mode : 'list'
|
|
|
|
});
|
|
|
|
case 'edit':
|
|
|
|
this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() });
|
2011-12-13 11:35:21 +00:00
|
|
|
break;
|
2012-05-18 14:00:01 +00:00
|
|
|
case 'manage_filters':
|
|
|
|
this.do_action({
|
|
|
|
res_model: 'ir.filters',
|
|
|
|
views: [[false, 'list'], [false, 'form']],
|
|
|
|
type: 'ir.actions.act_window',
|
|
|
|
context: {
|
|
|
|
search_default_my_filters: true,
|
|
|
|
search_default_model_id: this.dataset.model
|
|
|
|
}
|
|
|
|
});
|
|
|
|
break;
|
2012-09-11 04:42:31 +00:00
|
|
|
case 'print_workflow':
|
2012-09-13 19:27:59 +00:00
|
|
|
if (current_view.get_selected_ids && current_view.get_selected_ids().length == 1) {
|
2012-09-12 10:42:14 +00:00
|
|
|
instance.web.blockUI();
|
2012-09-13 19:27:59 +00:00
|
|
|
var action = {
|
|
|
|
context: { active_ids: current_view.get_selected_ids() },
|
|
|
|
report_name: "workflow.instance.graph",
|
|
|
|
datas: {
|
|
|
|
model: this.dataset.model,
|
|
|
|
id: current_view.get_selected_ids()[0],
|
|
|
|
nested: true,
|
|
|
|
}
|
|
|
|
};
|
2012-11-26 10:54:50 +00:00
|
|
|
this.session.get_file({
|
|
|
|
url: '/web/report',
|
|
|
|
data: {action: JSON.stringify(action)},
|
|
|
|
complete: instance.web.unblockUI
|
|
|
|
});
|
2012-09-12 10:42:14 +00:00
|
|
|
}
|
2012-09-11 04:42:31 +00:00
|
|
|
break;
|
2011-12-13 11:35:21 +00:00
|
|
|
default:
|
|
|
|
if (val) {
|
|
|
|
console.log("No debug handler for ", val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
evt.currentTarget.selectedIndex = 0;
|
|
|
|
},
|
2012-01-10 11:23:28 +00:00
|
|
|
do_edit_resource: function(model, id, action) {
|
2013-07-25 10:07:49 +00:00
|
|
|
action = _.extend({
|
2012-01-10 11:23:28 +00:00
|
|
|
res_model : model,
|
|
|
|
res_id : id,
|
|
|
|
type : 'ir.actions.act_window',
|
|
|
|
view_type : 'form',
|
|
|
|
view_mode : 'form',
|
|
|
|
views : [[false, 'form']],
|
|
|
|
target : 'new',
|
|
|
|
flags : {
|
|
|
|
action_buttons : true,
|
|
|
|
form : {
|
|
|
|
resize_textareas : true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, action || {});
|
|
|
|
this.do_action(action);
|
|
|
|
},
|
2012-10-09 14:51:13 +00:00
|
|
|
switch_mode: function (view_type, no_store, options) {
|
2011-09-14 09:13:19 +00:00
|
|
|
var self = this;
|
2011-12-09 14:46:38 +00:00
|
|
|
|
2013-03-22 16:23:10 +00:00
|
|
|
return this.alive($.when(this._super.apply(this, arguments))).done(function () {
|
2012-10-23 08:52:20 +00:00
|
|
|
var controller = self.views[self.active_view].controller;
|
2012-08-24 18:27:07 +00:00
|
|
|
self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
|
2011-12-13 11:35:21 +00:00
|
|
|
view: controller,
|
|
|
|
view_manager: self
|
|
|
|
}));
|
2012-07-12 09:56:54 +00:00
|
|
|
self.set_title();
|
2011-09-14 09:13:19 +00:00
|
|
|
});
|
2011-08-16 12:35:10 +00:00
|
|
|
},
|
2012-07-05 19:22:27 +00:00
|
|
|
do_create_view: function(view_type) {
|
2013-02-18 11:49:11 +00:00
|
|
|
var self = this;
|
|
|
|
return this._super.apply(this, arguments).then(function() {
|
|
|
|
var view = self.views[view_type].controller;
|
|
|
|
view.set({ 'title': self.action.name });
|
|
|
|
});
|
2012-07-05 19:22:27 +00:00
|
|
|
},
|
2012-10-19 13:28:27 +00:00
|
|
|
get_action_manager: function() {
|
|
|
|
var cur = this;
|
2013-07-25 10:07:49 +00:00
|
|
|
while ((cur = cur.getParent())) {
|
2012-10-19 13:28:27 +00:00
|
|
|
if (cur instanceof instance.web.ActionManager) {
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
},
|
2012-07-05 19:22:27 +00:00
|
|
|
set_title: function(title) {
|
2012-10-19 13:28:27 +00:00
|
|
|
this.$el.find('.oe_breadcrumb_title:first').html(this.get_action_manager().get_title());
|
2012-07-05 19:22:27 +00:00
|
|
|
},
|
2011-12-14 16:09:58 +00:00
|
|
|
do_push_state: function(state) {
|
2012-02-21 15:30:23 +00:00
|
|
|
if (this.getParent() && this.getParent().do_push_state) {
|
2011-12-14 18:04:42 +00:00
|
|
|
state["view_type"] = this.active_view;
|
2012-10-17 13:55:10 +00:00
|
|
|
this.url_states[this.active_view] = state;
|
2012-02-21 15:30:23 +00:00
|
|
|
this.getParent().do_push_state(state);
|
2011-12-14 16:09:58 +00:00
|
|
|
}
|
2011-12-12 14:20:12 +00:00
|
|
|
},
|
2012-02-01 15:44:42 +00:00
|
|
|
do_load_state: function(state, warm) {
|
2011-12-15 14:29:39 +00:00
|
|
|
var self = this,
|
|
|
|
defs = [];
|
|
|
|
if (state.view_type && state.view_type !== this.active_view) {
|
2011-12-20 17:37:42 +00:00
|
|
|
defs.push(
|
2012-10-30 14:06:30 +00:00
|
|
|
this.views[this.active_view].deferred.then(function() {
|
2012-10-10 11:30:25 +00:00
|
|
|
return self.switch_mode(state.view_type, true);
|
2011-12-20 17:37:42 +00:00
|
|
|
})
|
|
|
|
);
|
2011-12-15 14:29:39 +00:00
|
|
|
}
|
|
|
|
|
2014-07-29 09:35:19 +00:00
|
|
|
$.when(this.views[this.active_view] ? this.views[this.active_view].deferred : $.when(), defs).done(function() {
|
2012-02-01 15:44:42 +00:00
|
|
|
self.views[self.active_view].controller.do_load_state(state, warm);
|
2011-12-12 16:44:55 +00:00
|
|
|
});
|
2011-12-12 14:20:12 +00:00
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
});
|
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.Sidebar = instance.web.Widget.extend({
|
2012-04-01 23:12:12 +00:00
|
|
|
init: function(parent) {
|
2012-06-13 14:10:16 +00:00
|
|
|
var self = this;
|
2012-04-01 23:12:12 +00:00
|
|
|
this._super(parent);
|
2012-04-09 13:14:38 +00:00
|
|
|
var view = this.getParent();
|
|
|
|
this.sections = [
|
|
|
|
{ 'name' : 'print', 'label' : _t('Print'), },
|
|
|
|
{ 'name' : 'other', 'label' : _t('More'), }
|
|
|
|
];
|
|
|
|
this.items = {
|
|
|
|
'print' : [],
|
2012-06-07 14:39:26 +00:00
|
|
|
'other' : []
|
|
|
|
};
|
2012-06-13 14:10:16 +00:00
|
|
|
this.fileupload_id = _.uniqueId('oe_fileupload');
|
|
|
|
$(window).on(this.fileupload_id, function() {
|
|
|
|
var args = [].slice.call(arguments).slice(1);
|
2012-11-27 10:02:10 +00:00
|
|
|
self.do_attachement_update(self.dataset, self.model_id,args);
|
2012-07-24 14:12:20 +00:00
|
|
|
instance.web.unblockUI();
|
2012-06-13 14:10:16 +00:00
|
|
|
});
|
2011-04-04 16:08:13 +00:00
|
|
|
},
|
2011-07-26 21:00:05 +00:00
|
|
|
start: function() {
|
2012-04-02 15:56:50 +00:00
|
|
|
var self = this;
|
2011-08-26 12:44:18 +00:00
|
|
|
this._super(this);
|
2012-04-09 13:14:38 +00:00
|
|
|
this.redraw();
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.on('click','.oe_dropdown_menu li a', function(event) {
|
2012-04-09 13:14:38 +00:00
|
|
|
var section = $(this).data('section');
|
|
|
|
var index = $(this).data('index');
|
|
|
|
var item = self.items[section][index];
|
|
|
|
if (item.callback) {
|
|
|
|
item.callback.apply(self, [item]);
|
2012-04-17 01:19:33 +00:00
|
|
|
} else if (item.action) {
|
2012-04-09 13:14:38 +00:00
|
|
|
self.on_item_action_clicked(item);
|
2012-04-17 01:19:33 +00:00
|
|
|
} else if (item.url) {
|
|
|
|
return true;
|
2012-04-09 13:14:38 +00:00
|
|
|
}
|
2012-06-20 10:21:26 +00:00
|
|
|
event.preventDefault();
|
2012-04-02 15:56:50 +00:00
|
|
|
});
|
2011-07-26 21:00:05 +00:00
|
|
|
},
|
2012-04-09 13:14:38 +00:00
|
|
|
redraw: function() {
|
|
|
|
var self = this;
|
2012-08-24 18:27:07 +00:00
|
|
|
self.$el.html(QWeb.render('Sidebar', {widget: self}));
|
2012-06-07 14:49:35 +00:00
|
|
|
|
|
|
|
// Hides Sidebar sections when item list is empty
|
|
|
|
this.$('.oe_form_dropdown_section').each(function() {
|
|
|
|
$(this).toggle(!!$(this).find('li').length);
|
|
|
|
});
|
2012-12-07 08:09:46 +00:00
|
|
|
|
|
|
|
self.$("[title]").tipsy({
|
|
|
|
'html': true,
|
|
|
|
'delayIn': 500,
|
2013-07-25 10:33:01 +00:00
|
|
|
});
|
2012-04-09 13:14:38 +00:00
|
|
|
},
|
2012-02-08 12:21:53 +00:00
|
|
|
/**
|
|
|
|
* For each item added to the section:
|
|
|
|
*
|
|
|
|
* ``label``
|
2012-04-09 13:14:38 +00:00
|
|
|
* will be used as the item's name in the sidebar, can be html
|
2012-02-08 12:21:53 +00:00
|
|
|
*
|
|
|
|
* ``action``
|
|
|
|
* descriptor for the action which will be executed, ``action`` and
|
|
|
|
* ``callback`` should be exclusive
|
|
|
|
*
|
|
|
|
* ``callback``
|
|
|
|
* function to call when the item is clicked in the sidebar, called
|
|
|
|
* with the item descriptor as its first argument (so information
|
|
|
|
* can be stored as additional keys on the object passed to
|
|
|
|
* ``add_items``)
|
|
|
|
*
|
|
|
|
* ``classname`` (optional)
|
|
|
|
* ``@class`` set on the sidebar serialization of the item
|
|
|
|
*
|
|
|
|
* ``title`` (optional)
|
|
|
|
* will be set as the item's ``@title`` (tooltip)
|
|
|
|
*
|
|
|
|
* @param {String} section_code
|
|
|
|
* @param {Array<{label, action | callback[, classname][, title]}>} items
|
|
|
|
*/
|
2011-10-12 09:54:26 +00:00
|
|
|
add_items: function(section_code, items) {
|
2012-04-09 13:14:38 +00:00
|
|
|
var self = this;
|
2011-07-26 21:00:05 +00:00
|
|
|
if (items) {
|
2012-04-09 18:44:45 +00:00
|
|
|
this.items[section_code].push.apply(this.items[section_code],items);
|
2012-04-09 13:14:38 +00:00
|
|
|
this.redraw();
|
2011-07-26 21:00:05 +00:00
|
|
|
}
|
|
|
|
},
|
2012-04-27 23:18:52 +00:00
|
|
|
add_toolbar: function(toolbar) {
|
|
|
|
var self = this;
|
|
|
|
_.each(['print','action','relate'], function(type) {
|
|
|
|
var items = toolbar[type];
|
|
|
|
if (items) {
|
|
|
|
for (var i = 0; i < items.length; i++) {
|
|
|
|
items[i] = {
|
|
|
|
label: items[i]['name'],
|
|
|
|
action: items[i],
|
|
|
|
classname: 'oe_sidebar_' + type
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
2012-04-27 23:18:52 +00:00
|
|
|
}
|
|
|
|
self.add_items(type=='print' ? 'print' : 'other', items);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2011-11-14 11:02:39 +00:00
|
|
|
on_item_action_clicked: function(item) {
|
|
|
|
var self = this;
|
2012-11-20 10:06:20 +00:00
|
|
|
self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
|
2012-02-21 15:30:23 +00:00
|
|
|
var ids = self.getParent().get_selected_ids();
|
2013-11-08 16:07:47 +00:00
|
|
|
var domain;
|
2013-08-08 10:47:33 +00:00
|
|
|
if (self.getParent().get_active_domain) {
|
2013-11-08 16:07:47 +00:00
|
|
|
domain = self.getParent().get_active_domain();
|
2013-08-08 10:47:33 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-11-08 16:07:47 +00:00
|
|
|
domain = $.Deferred().resolve(undefined);
|
2013-08-08 10:47:33 +00:00
|
|
|
}
|
2013-07-25 10:07:49 +00:00
|
|
|
if (ids.length === 0) {
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
|
2011-11-21 16:34:41 +00:00
|
|
|
return false;
|
|
|
|
}
|
2012-11-20 10:06:20 +00:00
|
|
|
var active_ids_context = {
|
2011-11-21 14:09:08 +00:00
|
|
|
active_id: ids[0],
|
|
|
|
active_ids: ids,
|
2013-07-22 11:23:38 +00:00
|
|
|
active_model: self.getParent().dataset.model,
|
2013-07-25 13:50:01 +00:00
|
|
|
};
|
2013-07-22 11:23:38 +00:00
|
|
|
|
2013-07-25 15:28:46 +00:00
|
|
|
$.when(domain).done(function (domain) {
|
|
|
|
if (domain !== undefined) {
|
|
|
|
active_ids_context.active_domain = domain;
|
2013-07-25 13:50:01 +00:00
|
|
|
}
|
2013-07-22 11:23:38 +00:00
|
|
|
var c = instance.web.pyeval.eval('context',
|
2012-11-29 09:10:38 +00:00
|
|
|
new instance.web.CompoundContext(
|
|
|
|
sidebar_eval_context, active_ids_context));
|
2013-07-22 11:23:38 +00:00
|
|
|
|
|
|
|
self.rpc("/web/action/load", {
|
|
|
|
action_id: item.action.id,
|
2015-02-20 14:57:51 +00:00
|
|
|
context: new instance.web.CompoundContext(
|
[FIX] web: action translation (menuitems & more menu)
This rev. reverts partially 1d76586a1b0fff766581871cd277332bbfec030a
This rev. is related to #3462
Regarding addons/web/controllers/main.py
---
name of model ir.actions.actions is not translated
it's the name of server actions, client actions and window actions
that are translated.
Meaning the name of the ir.actions.actions will always be in English,
even when passing the user language within the context.
Regarding addons/web/static/src/js/views.js
---
There is no reason to pass the field values within the context
of the /web/action/load call: The read methods of actions are
not overidden to use the field values. Besides, it pollutes
the context of the action, leading to unwanted behavior, such
as the translation of the action name within the lang available in the
fields of the form view (e.g. the partner form).
Initially, the field values added in the context has been added
within the rev. 542928adde6f74269f51d24e18896b2e8bb44fc2
Indeed, sidebar_context (or sidebar_eval_context nowadays), contains
the field values, and the additional_context passed to /web/action/load
is an extension of this sidebar_context.
We are not sure the reasons why the sidebar_context was passed to the
/web/action/load, but we believe it was to pass the session/user context
containing the lang, timezone, and so on, not to pass the fields values.
2015-02-20 14:24:18 +00:00
|
|
|
self.dataset.get_context(), active_ids_context).eval()
|
2013-07-22 11:23:38 +00:00
|
|
|
}).done(function(result) {
|
|
|
|
result.context = new instance.web.CompoundContext(
|
|
|
|
result.context || {}, active_ids_context)
|
|
|
|
.set_eval_context(c);
|
|
|
|
result.flags = result.flags || {};
|
|
|
|
result.flags.new_window = true;
|
|
|
|
self.do_action(result, {
|
|
|
|
on_close: function() {
|
|
|
|
// reload view
|
|
|
|
self.getParent().reload();
|
|
|
|
},
|
|
|
|
});
|
2011-12-21 14:19:53 +00:00
|
|
|
});
|
2011-11-21 14:09:08 +00:00
|
|
|
});
|
2011-11-14 11:02:39 +00:00
|
|
|
});
|
|
|
|
},
|
2012-12-07 09:11:34 +00:00
|
|
|
do_attachement_update: function(dataset, model_id, args) {
|
2012-11-27 10:02:10 +00:00
|
|
|
var self = this;
|
2012-06-13 14:10:16 +00:00
|
|
|
this.dataset = dataset;
|
|
|
|
this.model_id = model_id;
|
2012-12-07 09:11:34 +00:00
|
|
|
if (args && args[0].error) {
|
2014-01-31 10:28:36 +00:00
|
|
|
this.do_warn(_t('Uploading Error'), args[0].error);
|
2012-11-27 10:02:10 +00:00
|
|
|
}
|
2012-04-17 01:19:33 +00:00
|
|
|
if (!model_id) {
|
|
|
|
this.on_attachments_loaded([]);
|
|
|
|
} else {
|
|
|
|
var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ];
|
2012-04-17 12:15:59 +00:00
|
|
|
var ds = new instance.web.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom);
|
2012-11-30 11:41:23 +00:00
|
|
|
ds.read_slice(['name', 'url', 'type', 'create_uid', 'create_date', 'write_uid', 'write_date'], {}).done(this.on_attachments_loaded);
|
2012-04-17 01:19:33 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
on_attachments_loaded: function(attachments) {
|
|
|
|
var self = this;
|
|
|
|
var items = [];
|
2012-11-14 17:41:50 +00:00
|
|
|
var prefix = this.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'name'});
|
2012-04-17 01:19:33 +00:00
|
|
|
_.each(attachments,function(a) {
|
|
|
|
a.label = a.name;
|
|
|
|
if(a.type === "binary") {
|
2012-11-14 17:41:50 +00:00
|
|
|
a.url = prefix + '&id=' + a.id + '&t=' + (new Date().getTime());
|
2012-04-17 01:19:33 +00:00
|
|
|
}
|
|
|
|
});
|
2012-04-17 01:43:23 +00:00
|
|
|
self.items['files'] = attachments;
|
|
|
|
self.redraw();
|
2012-06-27 14:12:49 +00:00
|
|
|
this.$('.oe_sidebar_add_attachment .oe_form_binary_file').change(this.on_attachment_changed);
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('.oe_sidebar_delete_item').click(this.on_attachment_delete);
|
2012-04-17 01:19:33 +00:00
|
|
|
},
|
|
|
|
on_attachment_changed: function(e) {
|
|
|
|
var $e = $(e.target);
|
2012-06-13 14:10:16 +00:00
|
|
|
if ($e.val() !== '') {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.find('form.oe_form_binary_form').submit();
|
2012-04-17 01:19:33 +00:00
|
|
|
$e.parent().find('input[type=file]').prop('disabled', true);
|
|
|
|
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
|
2012-06-13 14:10:16 +00:00
|
|
|
this.$('.oe_sidebar_add_attachment span').text(_t('Uploading...'));
|
2012-07-24 14:12:20 +00:00
|
|
|
instance.web.blockUI();
|
2012-04-17 01:19:33 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
on_attachment_delete: function(e) {
|
2012-06-13 16:17:12 +00:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
var self = this;
|
|
|
|
var $e = $(e.currentTarget);
|
|
|
|
if (confirm(_t("Do you really want to delete this attachment ?"))) {
|
2012-10-30 14:06:30 +00:00
|
|
|
(new instance.web.DataSet(this, 'ir.attachment')).unlink([parseInt($e.attr('data-id'), 10)]).done(function() {
|
2012-06-13 16:17:12 +00:00
|
|
|
self.do_attachement_update(self.dataset, self.model_id);
|
2012-04-17 01:19:33 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2011-04-01 12:10:42 +00:00
|
|
|
});
|
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.View = instance.web.Widget.extend({
|
2011-12-16 11:20:54 +00:00
|
|
|
// name displayed in view switchers
|
|
|
|
display_name: '',
|
2012-04-05 16:03:15 +00:00
|
|
|
/**
|
|
|
|
* Define a view type for each view to allow automatic call to fields_view_get.
|
|
|
|
*/
|
|
|
|
view_type: undefined,
|
2012-01-18 13:06:52 +00:00
|
|
|
init: function(parent, dataset, view_id, options) {
|
|
|
|
this._super(parent);
|
2013-04-17 13:34:38 +00:00
|
|
|
this.ViewManager = parent;
|
2012-01-18 13:06:52 +00:00
|
|
|
this.dataset = dataset;
|
|
|
|
this.view_id = view_id;
|
|
|
|
this.set_default_options(options);
|
|
|
|
},
|
2012-07-26 14:31:41 +00:00
|
|
|
start: function () {
|
2012-04-05 16:03:15 +00:00
|
|
|
return this.load_view();
|
|
|
|
},
|
2012-10-23 08:52:20 +00:00
|
|
|
load_view: function(context) {
|
2012-10-18 11:49:50 +00:00
|
|
|
var self = this;
|
2012-12-13 14:09:14 +00:00
|
|
|
var view_loaded_def;
|
2012-04-05 16:03:15 +00:00
|
|
|
if (this.embedded_view) {
|
2012-12-13 14:09:14 +00:00
|
|
|
view_loaded_def = $.Deferred();
|
2012-10-30 14:06:30 +00:00
|
|
|
$.async_when().done(function() {
|
2012-12-13 14:09:14 +00:00
|
|
|
view_loaded_def.resolve(self.embedded_view);
|
2012-10-18 11:49:50 +00:00
|
|
|
});
|
2012-04-05 16:03:15 +00:00
|
|
|
} else {
|
|
|
|
if (! this.view_type)
|
2012-04-05 16:21:31 +00:00
|
|
|
console.warn("view_type is not defined", this);
|
2012-12-13 14:09:14 +00:00
|
|
|
view_loaded_def = instance.web.fields_view_get({
|
2012-12-04 16:39:48 +00:00
|
|
|
"model": this.dataset._model,
|
2012-04-05 16:03:15 +00:00
|
|
|
"view_id": this.view_id,
|
|
|
|
"view_type": this.view_type,
|
2012-12-04 16:39:48 +00:00
|
|
|
"toolbar": !!this.options.$sidebar,
|
2013-03-18 17:45:03 +00:00
|
|
|
"context": this.dataset.get_context(),
|
2012-10-18 11:49:50 +00:00
|
|
|
});
|
2012-04-05 16:03:15 +00:00
|
|
|
}
|
2014-02-17 10:49:20 +00:00
|
|
|
return this.alive(view_loaded_def).then(function(r) {
|
2012-12-10 15:36:46 +00:00
|
|
|
self.fields_view = r;
|
2012-10-23 09:55:59 +00:00
|
|
|
// add css classes that reflect the (absence of) access rights
|
|
|
|
self.$el.addClass('oe_view')
|
|
|
|
.toggleClass('oe_cannot_create', !self.is_action_enabled('create'))
|
|
|
|
.toggleClass('oe_cannot_edit', !self.is_action_enabled('edit'))
|
|
|
|
.toggleClass('oe_cannot_delete', !self.is_action_enabled('delete'));
|
2012-12-13 14:09:14 +00:00
|
|
|
return $.when(self.view_loading(r)).then(function() {
|
|
|
|
self.trigger('view_loaded', r);
|
|
|
|
});
|
2012-10-23 08:52:20 +00:00
|
|
|
});
|
2012-04-05 16:03:15 +00:00
|
|
|
},
|
2012-12-13 14:09:14 +00:00
|
|
|
view_loading: function(r) {
|
|
|
|
},
|
2011-07-26 21:00:05 +00:00
|
|
|
set_default_options: function(options) {
|
|
|
|
this.options = options || {};
|
|
|
|
_.defaults(this.options, {
|
|
|
|
// All possible views options should be defaulted here
|
2012-04-09 16:10:00 +00:00
|
|
|
$sidebar: null,
|
2011-07-26 21:00:05 +00:00
|
|
|
sidebar_id: null,
|
2011-08-08 17:32:30 +00:00
|
|
|
action: null,
|
|
|
|
action_views_ids: {}
|
2011-07-26 21:00:05 +00:00
|
|
|
});
|
|
|
|
},
|
2011-05-11 14:49:10 +00:00
|
|
|
/**
|
|
|
|
* Fetches and executes the action identified by ``action_data``.
|
|
|
|
*
|
|
|
|
* @param {Object} action_data the action descriptor data
|
|
|
|
* @param {String} action_data.name the action name, used to uniquely identify the action to find and execute it
|
|
|
|
* @param {String} [action_data.special=null] special action handlers (currently: only ``'cancel'``)
|
|
|
|
* @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'``
|
|
|
|
* @param {Object} [action_data.context=null] additional action context, to add to the current context
|
2012-04-17 12:15:59 +00:00
|
|
|
* @param {instance.web.DataSet} dataset a dataset object used to communicate with the server
|
2011-05-27 07:44:42 +00:00
|
|
|
* @param {Object} [record_id] the identifier of the object on which the action is to be applied
|
2011-07-13 14:25:02 +00:00
|
|
|
* @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action)
|
2011-05-11 14:49:10 +00:00
|
|
|
*/
|
2011-09-14 12:13:05 +00:00
|
|
|
do_execute_action: function (action_data, dataset, record_id, on_closed) {
|
2011-06-28 12:43:55 +00:00
|
|
|
var self = this;
|
2011-09-14 12:13:05 +00:00
|
|
|
var result_handler = function () {
|
|
|
|
if (on_closed) { on_closed.apply(null, arguments); }
|
2012-02-21 15:30:23 +00:00
|
|
|
if (self.getParent() && self.getParent().on_action_executed) {
|
|
|
|
return self.getParent().on_action_executed.apply(null, arguments);
|
2011-10-10 09:57:54 +00:00
|
|
|
}
|
2011-09-14 12:13:05 +00:00
|
|
|
};
|
2013-12-11 16:18:10 +00:00
|
|
|
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
|
2013-11-08 16:06:27 +00:00
|
|
|
|
2013-12-11 16:18:10 +00:00
|
|
|
// response handler
|
2012-11-27 15:41:21 +00:00
|
|
|
var handler = function (action) {
|
2011-06-21 14:38:04 +00:00
|
|
|
if (action && action.constructor == Object) {
|
2013-12-11 16:18:10 +00:00
|
|
|
// filter out context keys that are specific to the current action.
|
|
|
|
// Wrong default_* and search_default_* values will no give the expected result
|
|
|
|
// Wrong group_by values will simply fail and forbid rendering of the destination view
|
|
|
|
var ncontext = new instance.web.CompoundContext(
|
2013-12-18 11:24:16 +00:00
|
|
|
_.object(_.reject(_.pairs(dataset.get_context().eval()), function(pair) {
|
2014-03-20 13:10:14 +00:00
|
|
|
return pair[0].match('^(?:(?:default_|search_default_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
|
2013-12-11 16:18:10 +00:00
|
|
|
}))
|
|
|
|
);
|
2013-12-18 11:24:16 +00:00
|
|
|
ncontext.add(action_data.context || {});
|
2013-12-11 16:36:09 +00:00
|
|
|
ncontext.add({active_model: dataset.model});
|
2011-10-18 15:57:33 +00:00
|
|
|
if (record_id) {
|
|
|
|
ncontext.add({
|
|
|
|
active_id: record_id,
|
|
|
|
active_ids: [record_id],
|
|
|
|
});
|
|
|
|
}
|
2011-10-19 09:56:12 +00:00
|
|
|
ncontext.add(action.context || {});
|
2012-11-27 15:41:21 +00:00
|
|
|
action.context = ncontext;
|
|
|
|
return self.do_action(action, {
|
|
|
|
on_close: result_handler,
|
|
|
|
});
|
2011-09-14 12:13:05 +00:00
|
|
|
} else {
|
2012-11-15 13:42:07 +00:00
|
|
|
self.do_action({"type":"ir.actions.act_window_close"});
|
2011-09-30 16:15:06 +00:00
|
|
|
return result_handler();
|
2011-05-11 14:49:10 +00:00
|
|
|
}
|
|
|
|
};
|
2011-03-30 14:00:48 +00:00
|
|
|
|
2012-11-14 23:37:43 +00:00
|
|
|
if (action_data.special === 'cancel') {
|
2012-10-05 11:30:46 +00:00
|
|
|
return handler({"type":"ir.actions.act_window_close"});
|
2011-08-10 01:34:48 +00:00
|
|
|
} else if (action_data.type=="object") {
|
2011-12-06 17:31:42 +00:00
|
|
|
var args = [[record_id]], additional_args = [];
|
|
|
|
if (action_data.args) {
|
|
|
|
try {
|
|
|
|
// Warning: quotes and double quotes problem due to json and xml clash
|
|
|
|
// Maybe we should force escaping in xml or do a better parse of the args array
|
|
|
|
additional_args = JSON.parse(action_data.args.replace(/'/g, '"'));
|
|
|
|
args = args.concat(additional_args);
|
|
|
|
} catch(e) {
|
|
|
|
console.error("Could not JSON.parse arguments", action_data.args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
args.push(context);
|
2013-04-17 13:34:38 +00:00
|
|
|
return dataset.call_button(action_data.name, args).then(handler).then(function () {
|
2013-05-13 11:22:02 +00:00
|
|
|
if (instance.webclient) {
|
|
|
|
instance.webclient.menu.do_reload_needaction();
|
2013-04-17 13:34:38 +00:00
|
|
|
}
|
|
|
|
});
|
2011-08-10 01:34:48 +00:00
|
|
|
} else if (action_data.type=="action") {
|
2012-11-26 10:54:50 +00:00
|
|
|
return this.rpc('/web/action/load', {
|
|
|
|
action_id: action_data.name,
|
2013-02-04 15:03:04 +00:00
|
|
|
context: _.extend({'active_model': dataset.model, 'active_ids': dataset.ids, 'active_id': record_id}, instance.web.pyeval.eval('context', context)),
|
2012-11-26 10:54:50 +00:00
|
|
|
do_not_eval: true
|
2012-11-27 15:41:21 +00:00
|
|
|
}).then(handler);
|
2011-08-10 01:34:48 +00:00
|
|
|
} else {
|
2012-11-27 15:41:21 +00:00
|
|
|
return dataset.exec_workflow(record_id, action_data.name).then(handler);
|
2011-09-06 13:05:25 +00:00
|
|
|
}
|
2011-06-06 07:52:43 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Directly set a view to use instead of calling fields_view_get. This method must
|
|
|
|
* be called before start(). When an embedded view is set, underlying implementations
|
2012-04-17 12:15:59 +00:00
|
|
|
* of instance.web.View must use the provided view instead of any other one.
|
2011-09-06 13:05:25 +00:00
|
|
|
*
|
2011-06-06 07:52:43 +00:00
|
|
|
* @param embedded_view A view.
|
|
|
|
*/
|
|
|
|
set_embedded_view: function(embedded_view) {
|
|
|
|
this.embedded_view = embedded_view;
|
2011-07-28 15:15:45 +00:00
|
|
|
},
|
2011-12-14 17:18:11 +00:00
|
|
|
do_show: function () {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.show();
|
2011-12-14 17:18:11 +00:00
|
|
|
},
|
|
|
|
do_hide: function () {
|
2012-08-24 18:27:07 +00:00
|
|
|
this.$el.hide();
|
2011-12-14 17:18:11 +00:00
|
|
|
},
|
2012-11-13 15:36:10 +00:00
|
|
|
is_active: function () {
|
|
|
|
var manager = this.getParent();
|
|
|
|
return !manager || !manager.active_view
|
|
|
|
|| manager.views[manager.active_view].controller === this;
|
|
|
|
}, /**
|
|
|
|
* Wraps fn to only call it if the current view is the active one. If the
|
|
|
|
* current view is not active, doesn't call fn.
|
|
|
|
*
|
|
|
|
* fn can not return anything, as a non-call to fn can't return anything
|
|
|
|
* either
|
|
|
|
*
|
|
|
|
* @param {Function} fn function to wrap in the active guard
|
|
|
|
*/
|
|
|
|
guard_active: function (fn) {
|
|
|
|
var self = this;
|
|
|
|
return function () {
|
|
|
|
if (self.is_active()) {
|
|
|
|
fn.apply(self, arguments);
|
|
|
|
}
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
2012-11-13 15:36:10 +00:00
|
|
|
},
|
2011-12-14 18:04:42 +00:00
|
|
|
do_push_state: function(state) {
|
2012-02-21 15:30:23 +00:00
|
|
|
if (this.getParent() && this.getParent().do_push_state) {
|
|
|
|
this.getParent().do_push_state(state);
|
2011-12-14 18:04:42 +00:00
|
|
|
}
|
|
|
|
},
|
2012-02-01 15:44:42 +00:00
|
|
|
do_load_state: function(state, warm) {
|
2011-12-14 18:04:42 +00:00
|
|
|
},
|
2011-12-07 10:45:03 +00:00
|
|
|
/**
|
|
|
|
* Switches to a specific view type
|
|
|
|
*/
|
2012-10-25 09:28:14 +00:00
|
|
|
do_switch_view: function() {
|
|
|
|
this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments)));
|
2011-12-14 17:18:11 +00:00
|
|
|
},
|
2011-12-07 10:45:03 +00:00
|
|
|
/**
|
|
|
|
* Cancels the switch to the current view, switches to the previous one
|
2012-01-24 09:30:33 +00:00
|
|
|
*
|
|
|
|
* @param {Object} [options]
|
|
|
|
* @param {Boolean} [options.created=false] resource was created
|
|
|
|
* @param {String} [options.default=null] view to switch to if no previous view
|
2011-12-07 10:45:03 +00:00
|
|
|
*/
|
2012-10-11 13:00:11 +00:00
|
|
|
|
2011-09-28 10:44:58 +00:00
|
|
|
do_search: function(view) {
|
|
|
|
},
|
2011-07-28 16:18:53 +00:00
|
|
|
on_sidebar_export: function() {
|
2012-08-14 14:30:03 +00:00
|
|
|
new instance.web.DataExport(this, this.dataset).open();
|
2011-07-28 16:18:53 +00:00
|
|
|
},
|
2012-11-20 10:06:20 +00:00
|
|
|
sidebar_eval_context: function () {
|
2012-11-22 09:43:47 +00:00
|
|
|
return $.when({});
|
2011-12-21 14:19:53 +00:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Asks the view to reload itself, if the reloading is asynchronous should
|
|
|
|
* return a {$.Deferred} indicating when the reloading is done.
|
|
|
|
*/
|
|
|
|
reload: function () {
|
|
|
|
return $.when();
|
2012-08-14 13:32:42 +00:00
|
|
|
},
|
|
|
|
/**
|
2012-08-31 15:37:58 +00:00
|
|
|
* Return whether the user can perform the action ('create', 'edit', 'delete') in this view.
|
|
|
|
* An action is disabled by setting the corresponding attribute in the view's main element,
|
|
|
|
* like: <form string="" create="false" edit="false" delete="false">
|
2012-08-14 13:32:42 +00:00
|
|
|
*/
|
2012-09-04 14:11:55 +00:00
|
|
|
is_action_enabled: function(action) {
|
2012-09-04 14:50:17 +00:00
|
|
|
var attrs = this.fields_view.arch.attrs;
|
|
|
|
return (action in attrs) ? JSON.parse(attrs[action]) : true;
|
2013-01-23 10:31:02 +00:00
|
|
|
},
|
2011-03-30 14:00:48 +00:00
|
|
|
});
|
|
|
|
|
2012-12-04 16:39:48 +00:00
|
|
|
/**
|
|
|
|
* Performs a fields_view_get and apply postprocessing.
|
|
|
|
* return a {$.Deferred} resolved with the fvg
|
|
|
|
*
|
2013-03-05 09:19:51 +00:00
|
|
|
* @param {Object} args
|
2012-12-04 16:39:48 +00:00
|
|
|
* @param {String|Object} args.model instance.web.Model instance or string repr of the model
|
2013-03-05 09:19:51 +00:00
|
|
|
* @param {Object} [args.context] context if args.model is a string
|
|
|
|
* @param {Number} [args.view_id] id of the view to be loaded, default view if null
|
|
|
|
* @param {String} [args.view_type] type of view to be loaded if view_id is null
|
2012-12-04 16:39:48 +00:00
|
|
|
* @param {Boolean} [args.toolbar=false] get the toolbar definition
|
|
|
|
*/
|
|
|
|
instance.web.fields_view_get = function(args) {
|
|
|
|
function postprocess(fvg) {
|
2012-12-05 16:05:36 +00:00
|
|
|
var doc = $.parseXML(fvg.arch).documentElement;
|
|
|
|
fvg.arch = instance.web.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
|
2012-12-04 16:39:48 +00:00
|
|
|
if ('id' in fvg.fields) {
|
|
|
|
// Special case for id's
|
|
|
|
var id_field = fvg.fields['id'];
|
|
|
|
id_field.original_type = id_field.type;
|
|
|
|
id_field.type = 'id';
|
|
|
|
}
|
|
|
|
_.each(fvg.fields, function(field) {
|
|
|
|
_.each(field.views || {}, function(view) {
|
|
|
|
postprocess(view);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return fvg;
|
|
|
|
}
|
|
|
|
args = _.defaults(args, {
|
|
|
|
toolbar: false,
|
|
|
|
});
|
|
|
|
var model = args.model;
|
2012-12-06 16:17:49 +00:00
|
|
|
if (typeof model === 'string') {
|
2012-12-04 16:39:48 +00:00
|
|
|
model = new instance.web.Model(args.model, args.context);
|
|
|
|
}
|
2013-03-18 17:45:03 +00:00
|
|
|
return args.model.call('fields_view_get', [args.view_id, args.view_type, args.context, args.toolbar]).then(function(fvg) {
|
2012-12-04 16:39:48 +00:00
|
|
|
return postprocess(fvg);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2012-12-04 17:02:30 +00:00
|
|
|
instance.web.xml_to_json = function(node, strip_whitespace) {
|
2012-03-14 17:09:49 +00:00
|
|
|
switch (node.nodeType) {
|
2012-12-04 16:39:48 +00:00
|
|
|
case 9:
|
2012-12-04 17:02:30 +00:00
|
|
|
return instance.web.xml_to_json(node.documentElement, strip_whitespace);
|
2012-03-14 17:09:49 +00:00
|
|
|
case 3:
|
|
|
|
case 4:
|
2012-12-04 17:02:30 +00:00
|
|
|
return (strip_whitespace && node.data.trim() === '') ? undefined : node.data;
|
2012-03-14 17:09:49 +00:00
|
|
|
case 1:
|
2012-03-19 14:53:32 +00:00
|
|
|
var attrs = $(node).getAttributes();
|
|
|
|
_.each(['domain', 'filter_domain', 'context', 'default_get'], function(key) {
|
|
|
|
if (attrs[key]) {
|
|
|
|
try {
|
|
|
|
attrs[key] = JSON.parse(attrs[key]);
|
|
|
|
} catch(e) { }
|
|
|
|
}
|
|
|
|
});
|
2012-03-14 17:09:49 +00:00
|
|
|
return {
|
|
|
|
tag: node.tagName.toLowerCase(),
|
2012-03-19 14:53:32 +00:00
|
|
|
attrs: attrs,
|
2012-12-04 17:02:30 +00:00
|
|
|
children: _.compact(_.map(node.childNodes, function(node) {
|
|
|
|
return instance.web.xml_to_json(node, strip_whitespace);
|
|
|
|
})),
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
2012-03-14 17:09:49 +00:00
|
|
|
}
|
2013-07-25 10:33:01 +00:00
|
|
|
};
|
|
|
|
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.json_node_to_xml = function(node, human_readable, indent) {
|
2011-06-22 11:11:29 +00:00
|
|
|
// For debugging purpose, this function will convert a json node back to xml
|
2011-10-25 16:35:31 +00:00
|
|
|
indent = indent || 0;
|
|
|
|
var sindent = (human_readable ? (new Array(indent + 1).join('\t')) : ''),
|
|
|
|
r = sindent + '<' + node.tag,
|
|
|
|
cr = human_readable ? '\n' : '';
|
2011-07-29 09:04:30 +00:00
|
|
|
|
|
|
|
if (typeof(node) === 'string') {
|
2011-10-25 16:35:31 +00:00
|
|
|
return sindent + node;
|
|
|
|
} else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
|
2012-07-04 09:56:26 +00:00
|
|
|
throw new Error(
|
2012-11-29 00:22:00 +00:00
|
|
|
_.str.sprintf(_t("Node [%s] is not a JSONified XML node"),
|
2012-07-04 09:56:26 +00:00
|
|
|
JSON.stringify(node)));
|
2011-06-22 11:11:29 +00:00
|
|
|
}
|
|
|
|
for (var attr in node.attrs) {
|
|
|
|
var vattr = node.attrs[attr];
|
|
|
|
if (typeof(vattr) !== 'string') {
|
|
|
|
// domains, ...
|
|
|
|
vattr = JSON.stringify(vattr);
|
|
|
|
}
|
|
|
|
vattr = vattr.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
2011-10-25 16:35:31 +00:00
|
|
|
if (human_readable) {
|
2011-06-22 11:11:29 +00:00
|
|
|
vattr = vattr.replace(/"/g, "'");
|
|
|
|
}
|
|
|
|
r += ' ' + attr + '="' + vattr + '"';
|
|
|
|
}
|
2011-09-05 14:15:45 +00:00
|
|
|
if (node.children && node.children.length) {
|
2011-10-25 16:35:31 +00:00
|
|
|
r += '>' + cr;
|
2011-06-22 11:11:29 +00:00
|
|
|
var childs = [];
|
|
|
|
for (var i = 0, ii = node.children.length; i < ii; i++) {
|
2012-04-17 12:15:59 +00:00
|
|
|
childs.push(instance.web.json_node_to_xml(node.children[i], human_readable, indent + 1));
|
2011-06-22 11:11:29 +00:00
|
|
|
}
|
2011-10-25 16:35:31 +00:00
|
|
|
r += childs.join(cr);
|
|
|
|
r += cr + sindent + '</' + node.tag + '>';
|
2011-06-22 11:11:29 +00:00
|
|
|
return r;
|
|
|
|
} else {
|
|
|
|
return r + '/>';
|
|
|
|
}
|
2012-11-14 16:44:13 +00:00
|
|
|
};
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.xml_to_str = function(node) {
|
2013-02-21 17:23:27 +00:00
|
|
|
var str = "";
|
2012-11-14 16:44:13 +00:00
|
|
|
if (window.XMLSerializer) {
|
2013-02-21 17:23:27 +00:00
|
|
|
str = (new XMLSerializer()).serializeToString(node);
|
2012-11-14 16:44:13 +00:00
|
|
|
} else if (window.ActiveXObject) {
|
2013-02-21 17:23:27 +00:00
|
|
|
str = node.xml;
|
2012-03-12 15:34:52 +00:00
|
|
|
} else {
|
2012-11-29 00:22:00 +00:00
|
|
|
throw new Error(_t("Could not serialize XML"));
|
2012-03-12 15:34:52 +00:00
|
|
|
}
|
2013-03-06 14:40:24 +00:00
|
|
|
// Browsers won't deal with self closing tags except void elements:
|
|
|
|
// http://www.w3.org/TR/html-markup/syntax.html
|
|
|
|
var void_elements = 'area base br col command embed hr img input keygen link meta param source track wbr'.split(' ');
|
|
|
|
|
2013-02-21 17:23:27 +00:00
|
|
|
// The following regex is a bit naive but it's ok for the xmlserializer output
|
2013-02-21 18:38:39 +00:00
|
|
|
str = str.replace(/<([a-z]+)([^<>]*)\s*\/\s*>/g, function(match, tag, attrs) {
|
2013-03-06 14:40:24 +00:00
|
|
|
if (void_elements.indexOf(tag) < 0) {
|
|
|
|
return "<" + tag + attrs + "></" + tag + ">";
|
|
|
|
} else {
|
|
|
|
return match;
|
|
|
|
}
|
2013-02-21 17:23:27 +00:00
|
|
|
});
|
|
|
|
return str;
|
2012-11-14 16:44:13 +00:00
|
|
|
};
|
2011-06-22 11:11:29 +00:00
|
|
|
|
2012-04-09 10:37:08 +00:00
|
|
|
/**
|
|
|
|
* Registry for all the main views
|
|
|
|
*/
|
2012-04-17 12:15:59 +00:00
|
|
|
instance.web.views = new instance.web.Registry();
|
2012-04-09 10:37:08 +00:00
|
|
|
|
2013-08-06 12:50:22 +00:00
|
|
|
})();
|
2011-03-30 14:00:48 +00:00
|
|
|
|
|
|
|
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
|