[FIX] fix a crash when editing dashboard title (web)
[odoo/odoo.git] / addons / board / static / src / js / dashboard.js
1 openerp.board = function(instance) {
2 var QWeb = instance.web.qweb,
3     _t = instance.web._t;
4
5 if (!instance.board) {
6     /** @namespace */
7     instance.board = {};
8 }
9
10 instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
11     events: {
12         'click .oe_dashboard_link_change_layout': 'on_change_layout',
13         'click h2.oe_header span.oe_header_txt': function (ev) {
14             if(ev.target === ev.currentTarget)
15                 this.on_header_string($(ev.target).parent());
16         },
17         'click .oe_dashboard_column .oe_fold': 'on_fold_action',
18         'click .oe_dashboard_column .oe_close': 'on_close_action',
19     },
20     init: function(view, node) {
21         this._super(view, node);
22         this.form_template = 'DashBoard';
23         this.actions_attrs = {};
24         this.action_managers = [];
25     },
26     start: function() {
27         var self = this;
28         this._super.apply(this, arguments);
29
30         this.$('.oe_dashboard_column').sortable({
31             connectWith: '.oe_dashboard_column',
32             handle: '.oe_header',
33             scroll: false
34         }).bind('sortstop', self.do_save_dashboard);
35
36         var old_title = this.__parentedParent.get('title');
37         this.__parentedParent.on('load_record', self, function(){
38             self.__parentedParent.set({ 'title': old_title});
39         });
40
41         // Init actions
42         _.each(this.node.children, function(column, column_index) {
43             _.each(column.children, function(action, action_index) {
44                 delete(action.attrs.width);
45                 delete(action.attrs.height);
46                 delete(action.attrs.colspan);
47                 var action_id = _.str.toNumber(action.attrs.name);
48                 if (!_.isNaN(action_id)) {
49                     self.rpc('/web/action/load', {action_id: action_id}).done(function(result) {
50                         self.on_load_action(result, column_index + '_' + action_index, action.attrs);
51                     });
52                 }
53             });
54         });
55     },
56     on_header_string:function(h2){
57         var self = this;
58         var span = h2.find('span:first').hide();
59         var input = h2.find('.oe_header_text').css('visibility','visible');
60         var attr = h2.closest(".oe_action").data('action_attrs');
61         var change_string = function(new_name){
62                 attr['string'] = new_name;
63                 span.text(new_name).show();
64                 input.css('visibility','hidden');
65                 self.do_save_dashboard();
66         }
67         input.unbind()
68         .val(span.text())
69         .change(function(event){
70             change_string($(this).val());
71         })
72         .keyup(function(event){
73             if(event.keyCode == 27){
74                 //esc key to cancel changes
75                 input.css('visibility','hidden');
76                 span.show();
77             }
78         });
79     },
80     on_change_layout: function() {
81         var self = this;
82         var qdict = {
83             current_layout : this.$el.find('.oe_dashboard').attr('data-layout')
84         };
85         var $dialog = new instance.web.Dialog(this, {
86                             title: _t("Edit Layout"),
87                         }, QWeb.render('DashBoard.layouts', qdict)).open();
88         $dialog.$el.find('li').click(function() {
89             var layout = $(this).attr('data-layout');
90             self.do_change_layout(layout);
91             $dialog.$dialog_box.modal('hide'); 
92         });
93     },
94     do_change_layout: function(new_layout) {
95         var $dashboard = this.$el.find('.oe_dashboard');
96         var current_layout = $dashboard.attr('data-layout');
97         if (current_layout != new_layout) {
98             var clayout = current_layout.split('-').length,
99                 nlayout = new_layout.split('-').length,
100                 column_diff = clayout - nlayout;
101             if (column_diff > 0) {
102                 var $last_column = $();
103                 $dashboard.find('.oe_dashboard_column').each(function(k, v) {
104                     if (k >= nlayout) {
105                         $(v).find('.oe_action').appendTo($last_column);
106                     } else {
107                         $last_column = $(v);
108                     }
109                 });
110             }
111             $dashboard.toggleClass('oe_dashboard_layout_' + current_layout + ' oe_dashboard_layout_' + new_layout);
112             $dashboard.attr('data-layout', new_layout);
113             this.do_save_dashboard();
114         }
115     },
116     on_fold_action: function(e) {
117         var $e = $(e.currentTarget),
118             $action = $e.parents('.oe_action:first'),
119             id = parseInt($action.attr('data-id'), 10);
120         if ($e.is('.oe_minimize')) {
121             $action.data('action_attrs').fold = '1';
122         } else {
123             delete($action.data('action_attrs').fold);
124         }
125         $e.toggleClass('oe_minimize oe_maximize');
126         $action.find('.oe_content').toggle();
127         this.do_save_dashboard();
128     },
129     on_close_action: function(e) {
130         if (confirm(_t("Are you sure you want to remove this item ?"))) {
131             $(e.currentTarget).parents('.oe_action:first').remove();
132             this.do_save_dashboard();
133         }
134     },
135     do_save_dashboard: function() {
136         var self = this;
137         var board = {
138                 form_title : this.view.fields_view.arch.attrs.string,
139                 style : this.$el.find('.oe_dashboard').attr('data-layout'),
140                 columns : []
141             };
142         this.$el.find('.oe_dashboard_column').each(function() {
143             var actions = [];
144             $(this).find('.oe_action').each(function() {
145                 var action_id = $(this).attr('data-id'),
146                     new_attrs = _.clone($(this).data('action_attrs'));
147                 if (new_attrs.domain) {
148                     new_attrs.domain = new_attrs.domain_string;
149                     delete(new_attrs.domain_string);
150                 }
151                 if (new_attrs.context) {
152                     new_attrs.context = new_attrs.context_string;
153                     delete(new_attrs.context_string);
154                 }
155                 actions.push(new_attrs);
156             });
157             board.columns.push(actions);
158         });
159         var arch = QWeb.render('DashBoard.xml', board);
160         this.rpc('/web/view/add_custom', {
161             view_id: this.view.fields_view.view_id,
162             arch: arch
163         });
164     },
165     on_load_action: function(result, index, action_attrs) {
166         var self = this,
167             action = result,
168             view_mode = action_attrs.view_mode;
169
170         // evaluate action_attrs context and domain
171         action_attrs.context_string = action_attrs.context;
172         action_attrs.context = instance.web.pyeval.eval(
173             'context', action_attrs.context || {});
174         action_attrs.domain_string = action_attrs.domain;
175         action_attrs.domain = instance.web.pyeval.eval(
176             'domain', action_attrs.domain || [], action_attrs.context);
177         if (action_attrs.context['dashboard_merge_domains_contexts'] === false) {
178             // TODO: replace this 6.1 workaround by attribute on <action/>
179             action.context = action_attrs.context || {};
180             action.domain = action_attrs.domain || [];
181         } else {
182             action.context = instance.web.pyeval.eval(
183                 'contexts', [action.context || {}, action_attrs.context]);
184             action.domain = instance.web.pyeval.eval(
185                 'domains', [action_attrs.domain, action.domain || []],
186                 action.context)
187         }
188
189         var action_orig = _.extend({ flags : {} }, action);
190
191         if (view_mode && view_mode != action.view_mode) {
192             action.views = _.map(view_mode.split(','), function(mode) {
193                 mode = mode === 'tree' ? 'list' : mode;
194                 return _(action.views).find(function(view) { return view[1] == mode; })
195                     || [false, mode];
196             });
197         }
198
199         action.flags = {
200             search_view : false,
201             sidebar : false,
202             views_switcher : false,
203             action_buttons : false,
204             pager: false,
205             headless: true,
206             low_profile: true,
207             display_title: false,
208             list: {
209                 selectable: false
210             }
211         };
212         var am = new instance.web.ActionManager(this),
213             // FIXME: ideally the dashboard view shall be refactored like kanban.
214             $action = $('#' + this.view.element_id + '_action_' + index);
215         $action.parent().data('action_attrs', action_attrs);
216         this.action_managers.push(am);
217         am.appendTo($action);
218         am.do_action(action);
219         am.do_action = function (action) {
220             self.do_action(action);
221         };
222         if (am.inner_widget) {
223             var new_form_action = function(id, editable) {
224                 var new_views = [];
225                 _.each(action_orig.views, function(view) {
226                     new_views[view[1] === 'form' ? 'unshift' : 'push'](view);
227                 });
228                 if (!new_views.length || new_views[0][1] !== 'form') {
229                     new_views.unshift([false, 'form']);
230                 }
231                 action_orig.views = new_views;
232                 action_orig.res_id = id;
233                 action_orig.flags = {
234                     form: {
235                         "initial_mode": editable ? "edit" : "view",
236                     }
237                 };
238                 self.do_action(action_orig);
239             };
240             var list = am.inner_widget.views.list;
241             if (list) {
242                 list.created.done(function() {
243                     $(list.controller.groups).off('row_link').on('row_link', function(e, id) {
244                         new_form_action(id);
245                     });
246                 });
247             }
248             var kanban = am.inner_widget.views.kanban;
249             if (kanban) {
250                 kanban.created.done(function() {
251                     kanban.controller.open_record = function(id, editable) {
252                         new_form_action(id, editable);
253                     };
254                 });
255             }
256         }
257     },
258     renderElement: function() {
259         this._super();
260
261         var check = _.detect(this.node.children, function(column, column_index) {
262             return _.detect(column.children,function(element){
263                 return element.tag === "action"? element: false;
264             });
265         });
266         if (!check) {
267             return this.no_result();
268         }
269         // We should start with three columns available
270         for (var i = this.node.children.length; i < 3; i++) {
271             this.node.children.push({
272                 tag: 'column',
273                 attrs: {},
274                 children: []
275             });
276         }
277         var rendered = QWeb.render(this.form_template, this);
278         this.$el.html(rendered);
279     },
280     no_result: function() {
281         if (this.view.options.action.help) {
282             this.$el.append(
283                 $('<div class="oe_view_nocontent">')
284                     .append($('<div>').html(this.view.options.action.help || " "))
285             );
286         }
287     },
288     do_reload: function() {
289         var view_manager = this.view.getParent(),
290             action_manager = view_manager.getParent();
291         this.view.destroy();
292         action_manager.do_action(view_manager.action);
293     }
294 });
295 instance.web.form.DashBoardLegacy = instance.web.form.DashBoard.extend({
296     renderElement: function() {
297         if (this.node.tag == 'hpaned') {
298             this.node.attrs.layout = '2-1';
299         } else if (this.node.tag == 'vpaned') {
300             this.node.attrs.layout = '1';
301         }
302         this.node.tag = 'board';
303         _.each(this.node.children, function(child) {
304             if (child.tag.indexOf('child') == 0) {
305                 child.tag = 'column';
306                 var actions = [], first_child = child.children[0];
307                 if (first_child && first_child.tag == 'vpaned') {
308                     _.each(first_child.children, function(subchild) {
309                         actions.push.apply(actions, subchild.children);
310                     });
311                     child.children = actions;
312                 }
313             }
314         });
315         this._super(this);
316     }
317 });
318
319 instance.web.form.tags.add('hpaned', 'instance.web.form.DashBoardLegacy');
320 instance.web.form.tags.add('vpaned', 'instance.web.form.DashBoardLegacy');
321 instance.web.form.tags.add('board', 'instance.web.form.DashBoard');
322
323
324 instance.web.search.FavoriteMenu.include({
325     prepare_dropdown_menu: function (filters) {
326         var self = this;
327         this._super(filters);
328         this.$('.favorites-menu').append(QWeb.render('SearchView.addtodashboard'));
329         var $add_to_dashboard = this.$('.add-to-dashboard');
330         this.$add_dashboard_btn = $add_to_dashboard.eq(2).find('button');
331         this.$add_dashboard_input = $add_to_dashboard.eq(1).find('input');
332         this.$add_dashboard_link = $add_to_dashboard.first().find('a');
333         var title = this.searchview.getParent().title;
334         this.$add_dashboard_input.val(title);
335         this.$add_dashboard_link.click(function () {
336             self.toggle_dashboard_menu();
337         });
338         this.$add_dashboard_btn.click(this.proxy('add_dashboard'));
339     },
340     toggle_dashboard_menu: function (is_open) {
341         this.$add_dashboard_link
342             .toggleClass('closed-menu', !is_open)
343             .toggleClass('open-menu', is_open);
344         this.$add_dashboard_btn.toggle(is_open);
345         this.$add_dashboard_input.toggle(is_open);
346         if (this.$add_dashboard_link.hasClass('open-menu')) {
347             this.$add_dashboard_input.focus();
348         }
349     },
350     close_menus: function () {
351         this.toggle_dashboard_menu(false);
352         this._super();
353     },
354     add_dashboard: function () {
355         var self = this,
356             view_manager = this.findAncestor(function (a) {
357                 return a instanceof instance.web.ViewManager
358             });
359         if (!view_manager.action) {
360             this.do_warn(_t("Can't find dashboard action"));
361             return;
362         }
363         var searchview = view_manager.searchview,
364             data = searchview.build_search_data(),
365             context = new instance.web.CompoundContext(searchview.dataset.get_context() || []),
366             domain = new instance.web.CompoundDomain(searchview.dataset.get_domain() || []);
367         _.each(data.contexts, context.add, context);
368         _.each(data.domains, domain.add, domain);
369
370         context.add({
371             group_by: instance.web.pyeval.eval('groupbys', data.groupbys || [])
372         });
373         var c = instance.web.pyeval.eval('context', context);
374         for(var k in c) {
375             if (c.hasOwnProperty(k) && /^search_default_/.test(k)) {
376                 delete c[k];
377             }
378         }
379         this.toggle_dashboard_menu(false);
380         c.dashboard_merge_domains_contexts = false;
381         var d = instance.web.pyeval.eval('domain', domain),
382             board = new instance.web.Model('board.board'),
383             name = self.$add_dashboard_input.val();
384         
385         board.call('list', [board.context()])
386             .then(function (board_list) {
387                 return self.rpc('/board/add_to_dashboard', {
388                     menu_id: board_list[0].id,                    
389                     action_id: view_manager.action.id,
390                     context_to_save: c,
391                     domain: d,
392                     view_mode: view_manager.active_view.type,
393                     name: name,
394                 });
395             }).then(function (r) {
396                 if (r) {
397                     self.do_notify(_.str.sprintf(_t("'%s' added to dashboard"), name), '');
398                 } else {
399                     self.do_warn(_t("Could not add filter to dashboard"));
400                 }
401             });
402     },
403 });
404
405 };