[IMP] get translated JS strings outside of QWeb attributes as the corresponding extra...
[odoo/odoo.git] / addons / web_dashboard / static / src / js / dashboard.js
1 openerp.web_dashboard = function(openerp) {
2 var QWeb = openerp.web.qweb,
3     _t = openerp.web._t;
4
5 if (!openerp.web_dashboard) {
6     /** @namespace */
7     openerp.web_dashboard = {};
8 }
9
10 openerp.web.form.DashBoard = openerp.web.form.Widget.extend({
11     init: function(view, node) {
12         this._super(view, node);
13         this.template = 'DashBoard';
14         this.actions_attrs = {};
15         this.action_managers = [];
16     },
17     start: function() {
18         var self = this;
19         this._super.apply(this, arguments);
20
21         this.$element.find('.oe-dashboard-column').sortable({
22             connectWith: '.oe-dashboard-column',
23             handle: '.oe-dashboard-action-header',
24             scroll: false
25         }).disableSelection().bind('sortstop', self.do_save_dashboard);
26
27         // Events
28         this.$element.find('.oe-dashboard-link-reset').click(this.on_reset);
29         this.$element.find('.oe-dashboard-link-change_layout').click(this.on_change_layout);
30
31         this.$element.delegate('.oe-dashboard-column .oe-dashboard-fold', 'click', this.on_fold_action);
32         this.$element.delegate('.oe-dashboard-column .ui-icon-closethick', 'click', this.on_close_action);
33
34         this.actions_attrs = {};
35         // Init actions
36         _.each(this.node.children, function(column, column_index) {
37             _.each(column.children, function(action, action_index) {
38                 delete(action.attrs.width);
39                 delete(action.attrs.height);
40                 delete(action.attrs.colspan);
41                 self.actions_attrs[action.attrs.name] = action.attrs;
42                 self.rpc('/web/action/load', {
43                     action_id: parseInt(action.attrs.name, 10)
44                 }, function(result) {
45                     self.on_load_action(result, column_index + '_' + action_index);
46                 });
47             });
48         });
49     },
50     on_reset: function() {
51         this.rpc('/web/view/undo_custom', {
52             view_id: this.view.fields_view.view_id,
53             reset: true
54         }, this.do_reload);
55     },
56     on_change_layout: function() {
57         var self = this;
58         var qdict = {
59             current_layout : this.$element.find('.oe-dashboard').attr('data-layout')
60         };
61         var $dialog = $('<div>').dialog({
62                             modal: true,
63                             title: _t("Edit Layout"),
64                             width: 'auto',
65                             height: 'auto'
66                         }).html(QWeb.render('DashBoard.layouts', qdict));
67         $dialog.find('li').click(function() {
68             var layout = $(this).attr('data-layout');
69             $dialog.dialog('destroy');
70             self.do_change_layout(layout);
71         });
72     },
73     do_change_layout: function(new_layout) {
74         var $dashboard = this.$element.find('.oe-dashboard');
75         var current_layout = $dashboard.attr('data-layout');
76         if (current_layout != new_layout) {
77             var clayout = current_layout.split('-').length,
78                 nlayout = new_layout.split('-').length,
79                 column_diff = clayout - nlayout;
80             if (column_diff > 0) {
81                 var $last_column = $();
82                 $dashboard.find('.oe-dashboard-column').each(function(k, v) {
83                     if (k >= nlayout) {
84                         $(v).find('.oe-dashboard-action').appendTo($last_column);
85                     } else {
86                         $last_column = $(v);
87                     }
88                 });
89             }
90             $dashboard.toggleClass('oe-dashboard-layout_' + current_layout + ' oe-dashboard-layout_' + new_layout);
91             $dashboard.attr('data-layout', new_layout);
92             this.do_save_dashboard();
93         }
94     },
95     on_fold_action: function(e) {
96         var $e = $(e.currentTarget),
97             $action = $e.parents('.oe-dashboard-action:first'),
98             id = parseInt($action.attr('data-id'), 10);
99         if ($e.is('.ui-icon-minusthick')) {
100             this.actions_attrs[id].fold = '1';
101         } else {
102             delete(this.actions_attrs[id].fold);
103         }
104         $e.toggleClass('ui-icon-minusthick ui-icon-plusthick');
105         $action.find('.oe-dashboard-action-content').toggle();
106         this.do_save_dashboard();
107     },
108     on_close_action: function(e) {
109         if (confirm(_t("Are you sure you want to remove this item ?"))) {
110             $(e.currentTarget).parents('.oe-dashboard-action:first').remove();
111             this.do_save_dashboard();
112         }
113     },
114     do_save_dashboard: function() {
115         var self = this;
116         var board = {
117                 form_title : this.view.fields_view.arch.attrs.string,
118                 style : this.$element.find('.oe-dashboard').attr('data-layout'),
119                 columns : []
120             };
121         this.$element.find('.oe-dashboard-column').each(function() {
122             var actions = [];
123             $(this).find('.oe-dashboard-action').each(function() {
124                 var action_id = $(this).attr('data-id'),
125                     new_attrs = _.clone(self.actions_attrs[action_id]);
126                 if (new_attrs.domain) {
127                     new_attrs.domain = new_attrs.domain_string;
128                     delete(new_attrs.domain_string);
129                 }
130                 if (new_attrs.context) {
131                     new_attrs.context = new_attrs.context_string;
132                     delete(new_attrs.context_string);
133                 }
134                 actions.push(new_attrs);
135             });
136             board.columns.push(actions);
137         });
138         var arch = QWeb.render('DashBoard.xml', board);
139         this.rpc('/web/view/add_custom', {
140             view_id: this.view.fields_view.view_id,
141             arch: arch
142         }, function() {
143             self.$element.find('.oe-dashboard-link-reset').show();
144         });
145     },
146     on_load_action: function(result, index) {
147         var self = this,
148             action = result.result,
149             action_attrs = this.actions_attrs[action.id],
150             view_mode = action_attrs.view_mode;
151
152         if (action_attrs.context) {
153             action.context = _.extend((action.context || {}), action_attrs.context);
154         }
155         if (action_attrs.domain) {
156             action.domain = action.domain || [];
157             action.domain.unshift.apply(action.domain, action_attrs.domain);
158         }
159         var action_orig = _.extend({ flags : {} }, action);
160
161         if (view_mode && view_mode != action.view_mode) {
162             var action_view_mode = action.view_mode.split(',');
163             action.views = _.map(view_mode.split(','), function(mode) {
164                 if (_.indexOf(action_view_mode, mode) < 0) {
165                     return [false, mode == 'tree' ? 'list': mode];
166                 } else {
167                     mode = mode === 'tree' ? 'list' : mode;
168                     return _.find(action.views, function(view) {
169                         return view[1] == mode;
170                     });
171                 }
172             });
173         }
174
175         action.flags = {
176             search_view : false,
177             sidebar : false,
178             views_switcher : false,
179             action_buttons : false,
180             pager: false,
181             low_profile: true,
182             display_title: false,
183             list: {
184                 selectable: false
185             }
186         };
187         var am = new openerp.web.ActionManager(this),
188             // FIXME: ideally the dashboard view shall be refactored like kanban.
189             $action = $('#' + this.view.element_id + '_action_' + index);
190         this.action_managers.push(am);
191         am.appendTo($action);
192         am.do_action(action);
193         am.do_action = function (action) {
194             self.do_action(action);
195         };
196         if (action_attrs.creatable && action_attrs.creatable !== 'false') {
197             var action_id = parseInt(action_attrs.creatable, 10);
198             $action.parent().find('button.oe_dashboard_button_create').click(function() {
199                 if (isNaN(action_id)) {
200                     action_orig.flags.default_view = 'form';
201                     self.do_action(action_orig);
202                 } else {
203                     self.rpc('/web/action/load', {
204                         action_id: action_id
205                     }, function(result) {
206                         result.result.flags = result.result.flags || {};
207                         result.result.flags.default_view = 'form';
208                         self.do_action(result.result);
209                     });
210                 }
211             });
212         }
213         if (am.inner_viewmanager) {
214             am.inner_viewmanager.on_mode_switch.add(function(mode) {
215                 var new_views = [];
216                 _.each(action_orig.views, function(view) {
217                     new_views[view[1] === mode ? 'unshift' : 'push'](view);
218                 });
219                 if (!new_views.length || new_views[0][1] !== mode) {
220                     new_views.unshift([false, mode]);
221                 }
222                 action_orig.views = new_views;
223                 action_orig.res_id = am.inner_viewmanager.dataset.ids[am.inner_viewmanager.dataset.index];
224                 self.do_action(action_orig);
225             });
226         }
227     },
228     render: function() {
229         // We should start with three columns available
230         for (var i = this.node.children.length; i < 3; i++) {
231             this.node.children.push({
232                 tag: 'column',
233                 attrs: {},
234                 children: []
235             });
236         }
237         return QWeb.render(this.template, this);
238     },
239     do_reload: function() {
240         var view_manager = this.view.widget_parent,
241             action_manager = view_manager.widget_parent;
242         this.view.stop();
243         action_manager.do_action(view_manager.action);
244     }
245 });
246 openerp.web.form.DashBoardLegacy = openerp.web.form.DashBoard.extend({
247     render: function() {
248         if (this.node.tag == 'hpaned') {
249             this.node.attrs.style = '2-1';
250         } else if (this.node.tag == 'vpaned') {
251             this.node.attrs.style = '1';
252         }
253         this.node.tag = 'board';
254         _.each(this.node.children, function(child) {
255             if (child.tag.indexOf('child') == 0) {
256                 child.tag = 'column';
257                 var actions = [], first_child = child.children[0];
258                 if (first_child && first_child.tag == 'vpaned') {
259                     _.each(first_child.children, function(subchild) {
260                         actions.push.apply(actions, subchild.children);
261                     });
262                     child.children = actions;
263                 }
264             }
265         });
266         return this._super(this, arguments);
267     }
268 });
269
270 openerp.web.form.widgets.add('hpaned', 'openerp.web.form.DashBoardLegacy');
271 openerp.web.form.widgets.add('vpaned', 'openerp.web.form.DashBoardLegacy');
272 openerp.web.form.widgets.add('board', 'openerp.web.form.DashBoard');
273
274 /*
275  * ConfigOverview
276  * This client action designed to be used as a dashboard widget display
277  * ir.actions.todo in a fancy way
278  */
279 openerp.web.client_actions.add( 'board.config.overview', 'openerp.web_dashboard.ConfigOverview');
280 openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({
281     template: 'ConfigOverview',
282     init: function (parent) {
283         this._super(parent);
284         this.user = _.extend(new openerp.web.DataSet(this, 'res.users'), {
285             index: 0,
286             ids: [this.session.uid]
287         });
288         this.dataset = new openerp.web.DataSetSearch(this, 'ir.actions.todo');
289     },
290     start: function () {
291         this._super();
292         var self = this;
293         return this.user.read_index(['groups_id']).pipe(function(record) {
294             var todos_filter = [
295                 ['type', '!=', 'automatic'],
296                 '|', ['groups_id', '=', false],
297                      ['groups_id', 'in', record['groups_id']]];
298             return $.when(
299                 self.dataset.read_slice(
300                     ['state', 'action_id', 'category_id'],
301                     { domain: todos_filter }
302                 ),
303                 self.dataset.call('progress').pipe(
304                         function (arg) { return arg; }, null))
305         }, null).then(this.on_records_loaded);
306
307     },
308     on_records_loaded: function (records, progress) {
309         var grouped_todos = _(records).chain()
310             .map(function (record) {
311                 return {
312                     id: record.id,
313                     name: record.action_id[1],
314                     done: record.state !== 'open',
315                     to_do: record.state === 'open',
316                     category: record['category_id'][1] || _t("Uncategorized")
317                 }
318             })
319             .groupBy(function (record) {return record.category})
320             .value();
321         this.$element.html(QWeb.render('ConfigOverview.content', {
322             completion: 100 * progress.done / progress.total,
323             groups: grouped_todos,
324             task_title: _t("Execute task \"%s\""),
325             checkbox_title: _t("Mark this task as done"),
326             _: _
327         }));
328         var $progress = this.$element.find('div.oe-config-progress-bar');
329         $progress.progressbar({value: $progress.data('completion')});
330
331         var self = this;
332         this.$element.find('dl')
333             .delegate('input', 'click', function (e) {
334                 // switch todo status
335                 e.stopImmediatePropagation();
336                 var new_state = this.checked ? 'done' : 'open',
337                       todo_id = parseInt($(this).val(), 10);
338                 self.dataset.write(todo_id, {state: new_state}, {}, function () {
339                     self.start();
340                 });
341             })
342             .delegate('li:not(.oe-done)', 'click', function () {
343                 self.widget_parent.widget_parent.widget_parent.do_execute_action({
344                         type: 'object',
345                         name: 'action_launch'
346                     }, self.dataset,
347                     $(this).data('id'), function () {
348                         // after action popup closed, refresh configuration
349                         // thingie
350                         self.start();
351                     });
352             });
353     }
354 });
355
356 /*
357  * Widgets
358  * This client action designed to be used as a dashboard widget display
359  * the html content of a res_widget given as argument
360  */
361 openerp.web.client_actions.add( 'board.home.widgets', 'openerp.web_dashboard.Widget');
362 openerp.web_dashboard.Widget = openerp.web.View.extend(/** @lends openerp.web_dashboard.Widgets# */{
363     template: 'HomeWidget',
364     /**
365      * Initializes a "HomeWidget" client widget: handles the display of a given
366      * res.widget objects in an OpenERP view (mainly a dashboard).
367      *
368      * @constructs openerp.web_dashboard.Widget
369      * @extends openerp.web.View
370      *
371      * @param {Object} parent
372      * @param {Object} options
373      * @param {Number} options.widget_id
374      */
375     init: function (parent, options) {
376         this._super(parent);
377         this.widget_id = options.widget_id;
378     },
379     start: function () {
380         this._super();
381         var ds = new openerp.web.DataSet(this, 'res.widget');
382         return ds.read_ids([this.widget_id], ['title']).then(this.on_widget_loaded);
383     },
384     on_widget_loaded: function (widgets) {
385         var widget = widgets[0];
386         var url = _.str.sprintf(
387             '/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
388             this.session.session_id, widget.id);
389         this.$element.html(QWeb.render('HomeWidget.content', {
390             widget: widget,
391             url: url
392         }));
393     }
394 });
395
396 /*
397  * HomeTiles this client action display either the list of application to
398  * install (if none is installed yet) or a list of root menu items
399  */
400 openerp.web.client_actions.add('default_home', 'session.web_dashboard.ApplicationTiles');
401 openerp.web_dashboard.ApplicationTiles = openerp.web.OldWidget.extend({
402     template: 'web_dashboard.ApplicationTiles',
403     init: function(parent) {
404         this._super(parent);
405     },
406     start: function() {
407         var self = this;
408         openerp.webclient.menu.do_hide_secondary();
409         var domain = [['application','=',true], ['state','=','installed'], ['name', '!=', 'base']];
410         var ds = new openerp.web.DataSetSearch(this, 'ir.module.module',{},domain);
411         ds.read_slice(['id']).then(function(result) {
412             if(result.length) {
413                 self.on_installed_database();
414             } else {
415                 self.on_uninstalled_database();
416             }
417         });
418     },
419     on_uninstalled_database: function() {
420         installer = new openerp.web_dashboard.ApplicationInstaller(this);
421         installer.appendTo(this.$element);
422     },
423     on_installed_database: function() {
424         var self = this;
425         self.rpc('/web/menu/get_user_roots', {}).then(function (menu_ids) {
426             var menuds = new openerp.web.DataSet(this, 'ir.ui.menu',{})
427                 .read_ids(menu_ids, ['name', 'web_icon_data', 'web_icon_hover_data', 'module']).then(function (applications) {
428                     var tiles = QWeb.render('ApplicationTiles.content', {applications: applications});
429                     $(tiles).appendTo(self.$element).find('.oe_install-module-link').click(function () {
430                         openerp.webclient.menu.on_menu_click(null, $(this).data('menu'))
431                     });
432                 });
433         });
434     }
435 });
436
437 /**
438  * ApplicationInstaller
439  * This client action  display a list of applications to install.
440  */
441 openerp.web.client_actions.add( 'board.application.installer', 'openerp.web_dashboard.ApplicationInstaller');
442 openerp.web_dashboard.ApplicationInstaller = openerp.web.OldWidget.extend({
443     template: 'web_dashboard.ApplicationInstaller',
444     start: function () {
445         // TODO menu hide
446         var r = this._super();
447         this.action_manager = new openerp.web.ActionManager(this);
448         this.action_manager.appendTo(this.$element.find('.oe_installer'));
449         this.action_manager.do_action({
450             type: 'ir.actions.act_window',
451             res_model: 'ir.module.module',
452             domain: [['application','=',true]],
453             views: [[false, 'kanban']],
454             flags: {
455                 display_title:false,
456                 search_view: false,
457                 views_switcher: false,
458                 action_buttons: false,
459                 sidebar: false,
460                 pager: false
461             }
462         });
463         return r;
464     },
465     stop: function() {
466         this.action_manager.stop();
467         return this._super();
468     }
469 });
470
471
472 };