PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/addons/web_view_editor/static/src/js/view_editor.js

https://gitlab.com/thanhchatvn/cloud-odoo
JavaScript | 997 lines | 980 code | 9 blank | 8 comment | 161 complexity | 03de73cd96a8bb8bc7b832a93fcdb957 MD5 | raw file
  1. odoo.define('web_view_editor.view_editor', function (require) {
  2. "use strict";
  3. var ActionManager = require('web.ActionManager');
  4. var core = require('web.core');
  5. var data = require('web.data');
  6. var DebugManager = require('web.DebugManager');
  7. var Dialog = require('web.Dialog');
  8. var formats = require('web.formats');
  9. var Registry = require('web.Registry');
  10. var session = require('web.session');
  11. var utils = require('web.utils');
  12. var Widget = require('web.Widget');
  13. var QWeb = core.qweb;
  14. var _t = core._t;
  15. if (core.debug) {
  16. DebugManager.include({
  17. manage_views: function() {
  18. if (this.view.fields_view && this.view.fields_view.arch) {
  19. var view_editor = new ViewEditor(this.view, this.view.$el, this.dataset, this.view.fields_view.arch);
  20. view_editor.start();
  21. } else {
  22. this.do_warn(_t("Manage Views"), _t("Could not find current view declaration"));
  23. }
  24. }
  25. });
  26. }
  27. var ViewEditor = Widget.extend({
  28. init: function(parent, element_id, dataset) {
  29. this._super(parent);
  30. this.parent = parent;
  31. this.dataset = new data.DataSetSearch(this, 'ir.ui.view', null, null);
  32. this.model = dataset.model;
  33. this.xml_element_id = 0;
  34. this.property = ViewEditor.property_widget;
  35. this.one_object = false;
  36. },
  37. start: function() {
  38. this.init_view_editor();
  39. },
  40. init_view_editor: function() {
  41. var self = this,
  42. action_title = _.str.sprintf(_t("Manage Views (%s)"), this.model);
  43. var action = {
  44. name: action_title,
  45. context: session.user_context,
  46. domain: [["model", "=", this.model]],
  47. res_model: 'ir.ui.view',
  48. views: [[false, 'list']],
  49. type: 'ir.actions.act_window',
  50. target: "current",
  51. limit: this.dataset.limit || 80,
  52. auto_search : true,
  53. flags: {
  54. sidebar: false,
  55. deletable: false,
  56. views_switcher: false,
  57. action_buttons: false,
  58. search_view: true,
  59. headless: true,
  60. pager: false,
  61. radio: true,
  62. select_view_id: self.parent.fields_view.view_id
  63. }
  64. };
  65. this.view_edit_dialog = new Dialog(this, {
  66. title: action_title,
  67. buttons: [
  68. {text: _t("Create"), click: function() { self.on_create_view(); }},
  69. {text: _t("Edit"), click: function() { self.xml_element_id = 0; self.get_arch(); }},
  70. {text: _t("Remove"), click: function() { self.do_delete_view(); }},
  71. {text: _t("Close"), click: function() { window.location.reload(); }, close: true}
  72. ]
  73. }).open();
  74. this.view_edit_dialog.on("closed", this, function(){window.location.reload();});
  75. this.main_view_id = this.parent.fields_view.view_id;
  76. this.action_manager = new ActionManager(this);
  77. this.action_manager.appendTo(this.view_edit_dialog.$el);
  78. $.when(this.action_manager.do_action(action)).done(function() {
  79. var viewmanager = self.action_manager.inner_widget;
  80. var controller = viewmanager.active_view.controller;
  81. $(controller.groups).bind({
  82. 'selected': function (e, ids) {
  83. self.main_view_id = ids[0];
  84. }
  85. });
  86. });
  87. },
  88. on_create_view: function() {
  89. var self = this;
  90. this.create_view_dialog = new Dialog(this, {
  91. title: _.str.sprintf(_t("Create a view (%s)"), self.model),
  92. buttons: [
  93. {text: _t("Save"), click: function () {
  94. var view_values = {};
  95. var warn = false;
  96. _.each(self.create_view_widget, function(widget) {
  97. if (widget.is_invalid) {
  98. warn = true;
  99. return false;
  100. }
  101. if (widget.dirty && !widget.is_invalid) {
  102. view_values[widget.name] = widget.get_value();
  103. }
  104. });
  105. if (warn) {
  106. self.on_valid_create_view(self.create_view_widget);
  107. } else {
  108. $.when(self.do_save_view(view_values)).done(function() {
  109. self.create_view_dialog.close();
  110. var controller = self.action_manager.inner_widget.views[self.action_manager.inner_widget.active_view].controller;
  111. controller.reload_content();
  112. });
  113. }
  114. }},
  115. {text: _t("Cancel"), click: function () { self.create_view_dialog.close(); }}
  116. ]
  117. }).open();
  118. var view_widget = [{'name': 'view_name', 'string':'View Name', 'type': 'char', 'required': true, 'value' : this.model + '.custom_' + Math.round(Math.random() * 1000)},
  119. {'name': 'view_type', 'string': 'View Type', 'type': 'selection', 'required': true, 'value': 'Form', 'selection': [['',''],['tree', 'Tree'],['form', 'Form'],['graph', 'Graph'],['calendar', 'Calender']]},
  120. {'name': 'proirity', 'string': 'Priority', 'type': 'float', 'required': true, 'value':'16'}];
  121. this.create_view_dialog.$el.append('<table id="create_view" style="width:400px" class="oe_form"></table>');
  122. this.create_view_widget = [];
  123. _.each(view_widget, function(widget) {
  124. var type_widget = new (self.property.get_any([widget.type])) (self.create_view_dialog, widget);
  125. self.create_view_dialog.$el.find('table[id=create_view]').append('<tr><td width="100px" align="right">' + widget.string + ':</td>' + type_widget.render()+'</tr>');
  126. var value = null;
  127. if (widget.value) {
  128. value = widget.value;
  129. type_widget.dirty = true;
  130. }
  131. type_widget.start();
  132. type_widget.set_value(value);
  133. self.create_view_widget.push(type_widget);
  134. });
  135. },
  136. do_save_view: function(values) {
  137. var def = $.Deferred();
  138. var field_dataset = new data.DataSetSearch(this, this.model, null, null);
  139. var model_dataset = new data.DataSetSearch(this, 'ir.model', null, null);
  140. var view_string = "", field_name = false, self = this;
  141. field_dataset.call( 'fields_get', []).done(function(fields) {
  142. _.each(['name', 'x_name'], function(value) {
  143. if (_.include(_.keys(fields), value)) {
  144. field_name = value;
  145. return false;
  146. }
  147. });
  148. if (field_name) {
  149. model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}).done(function(records) {
  150. if (records) {view_string = records[0].name;}
  151. var arch = _.str.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type);
  152. var vals = {'model': self.model, 'name': values.view_name, 'priority': values.priority, 'type': values.view_type, 'arch': arch};
  153. def = self.dataset.create(vals);
  154. });
  155. }
  156. });
  157. return def.promise();
  158. },
  159. on_valid_create_view: function(widgets) {
  160. var msg = "<ul>";
  161. _.each(widgets, function(widget) {
  162. if (widget.is_invalid) {
  163. msg += "<li>" + widget.name + "</li>";
  164. }
  165. });
  166. msg += "</ul>";
  167. this.do_warn(_t("The following fields are invalid :"), msg);
  168. },
  169. add_node_name : function(node) {
  170. if(node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "field"){
  171. return (node.getAttribute('name'))?
  172. _.str.sprintf( "<%s name='%s'>",node.tagName.toLowerCase(), node.getAttribute('name')):
  173. _.str.sprintf( "<%s>",node.tagName.toLowerCase());
  174. }else if(node.tagName.toLowerCase() == "group"){
  175. return (node.getAttribute('string'))?
  176. _.str.sprintf( "<%s>",node.getAttribute('string')):
  177. _.str.sprintf( "<%s>",node.tagName.toLowerCase());
  178. }else{
  179. return (node.getAttribute('string'))?
  180. _.str.sprintf( "<%s string='%s'>",node.tagName.toLowerCase(), node.getAttribute('string')):
  181. _.str.sprintf( "<%s>",node.tagName.toLowerCase());
  182. }
  183. },
  184. do_delete_view: function() {
  185. var self = this;
  186. if (confirm(_t("Do you really want to remove this view?"))) {
  187. var controller = this.action_manager.inner_widget.views[this.action_manager.inner_widget.active_view].controller;
  188. this.dataset.unlink([this.main_view_id]).done(function() {
  189. controller.reload_content();
  190. self.main_view_id = self.parent.fields_view.view_id;
  191. });
  192. }
  193. },
  194. create_View_Node: function(node){
  195. var ViewNode = {
  196. 'level': ($(node).parents()).length + 1,
  197. 'id': this.xml_element_id += 1,
  198. 'att_list': [],
  199. 'name': this.add_node_name(node),
  200. 'child_id': []
  201. };
  202. ViewNode.att_list.push(node.tagName.toLowerCase());
  203. _.each(node.attributes, function(att) {
  204. ViewNode.att_list.push([att.nodeName, att.nodeValue]);
  205. });
  206. return ViewNode;
  207. },
  208. append_child_object: function(main_object, parent_id, child_obj_list) {
  209. var self = this;
  210. if (main_object.id == parent_id) {
  211. main_object.child_id = child_obj_list;
  212. return main_object;
  213. } else {
  214. _.each(main_object.child_id, function(child_object) {
  215. self.append_child_object(child_object, parent_id, child_obj_list);
  216. });
  217. }
  218. },
  219. convert_arch_to_obj: function(xml_Node, main_object, parent_id) {
  220. var self = this;
  221. var child_obj_list = [];
  222. _.each(xml_Node, function(element) {
  223. child_obj_list.push(self.create_View_Node(element));
  224. });
  225. this.append_child_object(main_object, parent_id, child_obj_list);
  226. var obj_xml_list = _.zip(xml_Node, child_obj_list);
  227. _.each(obj_xml_list, function(node) {
  228. var children = _.filter(node[0].childNodes, function(child) {
  229. return child.nodeType == 1;
  230. });
  231. if (children) {
  232. self.convert_arch_to_obj(children, main_object, node[1].id);
  233. }
  234. });
  235. return main_object;
  236. },
  237. parse_xml: function(arch, view_id) {
  238. //First element of att_list must be element tagname.
  239. var main_object = {
  240. 'level': 0,
  241. 'id': this.xml_element_id +=1,
  242. 'att_list': ["view"],
  243. 'name': _.str.sprintf("<view view_id = %s>", view_id),
  244. 'child_id': []
  245. };
  246. var xml_arch = QWeb.load_xml(arch);
  247. return [this.convert_arch_to_obj(xml_arch.childNodes, main_object, this.xml_element_id)];
  248. },
  249. get_arch: function() {
  250. var self = this;
  251. var view_arch_list = [];
  252. this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type','priority']).done(function(arch) {
  253. if (arch.length) {
  254. var arch_object = self.parse_xml(arch[0].arch, self.main_view_id);
  255. self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type;
  256. view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch,"priority":arch[0].priority});
  257. self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}).done(function(result) {
  258. _.each(result, function(res) {
  259. view_arch_list.push({"view_id": res.id, "arch": res.arch,"priority":res.priority});
  260. self.inherit_view(arch_object, res);
  261. });
  262. return self.edit_view({"main_object": arch_object,
  263. "parent_child_id": self.parent_child_list(arch_object, []),
  264. "arch": view_arch_list});
  265. });
  266. } else {
  267. self.do_warn(_t("Please select view in list :"));
  268. }
  269. });
  270. },
  271. parent_child_list : function(one_object, parent_list) {
  272. var self = this;
  273. _.each(one_object , function(element) {
  274. if (element.child_id.length !== 0) {
  275. parent_list.push({"key": element.id, "value": _.pluck(element.child_id, 'id')});
  276. self.parent_child_list(element.child_id, parent_list);
  277. }
  278. });
  279. return parent_list;
  280. },
  281. inherit_view : function(arch_object, result) {
  282. var self = this, xml_list = [], xml_arch = QWeb.load_xml(result.arch);
  283. if (xml_arch.childNodes[0].tagName == "data") {
  284. xml_list = _.filter(xml_arch.childNodes[0].childNodes, function(child) {
  285. return child.nodeType == 1;
  286. });
  287. } else {
  288. xml_list.push( xml_arch.childNodes[0]);
  289. }
  290. _.each(xml_list, function(xml) {
  291. var expr_to_list = [], xpath_arch_object = self.parse_xml(QWeb.tools.xml_node_to_string(xml), result.id);
  292. if (xml.tagName == "xpath") {
  293. var part_expr = _.without(xml.getAttribute('expr').split("/"), "");
  294. _.each(part_expr, function(part) {
  295. expr_to_list.push(_.without($.trim(part.replace(/[^a-zA-Z 0-9 _]+/g,'!')).split("!"), ""));
  296. });
  297. } else {
  298. var temp = _.reject(xpath_arch_object[0].child_id[0].att_list, function(list) {
  299. return list instanceof Array? _.include(list, "position"): false;
  300. });
  301. expr_to_list = [_.flatten(temp)];
  302. }
  303. self.inherit_apply(expr_to_list, arch_object ,xpath_arch_object);
  304. });
  305. },
  306. inherit_apply: function(expr_list ,arch_object ,xpath_arch_object) {
  307. var self = this;
  308. var temp_list;
  309. if (xpath_arch_object.length) {
  310. var check = expr_list[0], obj = false;
  311. switch (check.length) {
  312. case 2:
  313. if (parseInt(check[1])) {
  314. //for field[3]
  315. temp_list = _.select(arch_object, function(element) {
  316. return _.include(_.flatten(element.att_list), check[0]);
  317. });
  318. obj = arch_object[_.indexOf(arch_object, temp_list[parseInt(check[1]) - 1])];
  319. } else {
  320. //for notebook[last()]
  321. obj = _.detect(arch_object, function(element) {
  322. return _.include(_.flatten(element.att_list), check[0]);
  323. });
  324. }
  325. break;
  326. case 3:
  327. //for field[@name='type']
  328. obj = _.detect(arch_object, function(element){
  329. if ((_.intersection(_.flatten(element.att_list), _.uniq(check))).length == _.uniq(check).length) {
  330. return element;
  331. }
  332. });
  333. break;
  334. case 1:
  335. //for /form/notebook
  336. temp_list = _.select(arch_object, function(element) {
  337. return _.include(_.flatten(element.att_list), check[0]);
  338. });
  339. if (temp_list.length !== 0) {
  340. expr_list.length === 1 ? obj = temp_list[0] : expr_list.shift();
  341. }
  342. break;
  343. }
  344. if (obj) {
  345. expr_list.shift();
  346. if (expr_list.length) {
  347. self.inherit_apply(expr_list, obj.child_id, xpath_arch_object);
  348. } else {
  349. self.increase_level(xpath_arch_object[0], obj.level + 1);
  350. obj.child_id.push(xpath_arch_object[0]);
  351. xpath_arch_object.pop();
  352. }
  353. } else {
  354. _.each(arch_object, function(element) {
  355. self.inherit_apply(expr_list, element.child_id, xpath_arch_object);
  356. });
  357. }
  358. }
  359. },
  360. increase_level: function(val, level) {
  361. var self = this;
  362. val.level = level;
  363. _.each(val.child_id, function(val) {
  364. self.increase_level(val, level + 1);
  365. });
  366. },
  367. do_select_row: function(row_id) {
  368. this.edit_xml_dialog.$el.find("tr[id^='viewedit-']").removeClass('ui-selected');
  369. this.edit_xml_dialog.$el.find("tr[id=viewedit-" + row_id + "]").addClass('ui-selected');
  370. },
  371. do_parent_img_hide_show: function(img) {
  372. if (_.str.include($(img).attr('src'), '/web/static/src/img/collapse.gif')) {
  373. $(img).attr('src', '/web/static/src/img/expand.gif');
  374. this.on_expand(img);
  375. } else {
  376. $(img).attr('src', '/web/static/src/img/collapse.gif');
  377. this.on_collapse(img);
  378. }
  379. },
  380. edit_view: function(one_object) {
  381. var self = this;
  382. this.one_object = one_object;
  383. this.edit_xml_dialog = new Dialog(this, {
  384. title: _.str.sprintf(_t("View Editor %d - %s"), self.main_view_id, self.model),
  385. buttons: [
  386. {text: _t("Inherited View"), click: function(){
  387. var selected_row = self.edit_xml_dialog.$el.find('.ui-selected');
  388. if (selected_row.length) {
  389. if(selected_row.find('a').text().search("field") != -1){
  390. if (confirm(_t("Do you really wants to create an inherited view here?"))) {
  391. self.inherited_view(selected_row);
  392. }
  393. }else{
  394. alert(_t("Can't Update View"));
  395. }
  396. }else{
  397. alert(_t("Select an element"));
  398. }
  399. }},
  400. {text: _t("Preview"), click: function() {
  401. var action = {
  402. context: self.session.user_context,
  403. res_model: self.model,
  404. views: [[self.main_view_id, self.main_view_type]],
  405. type: 'ir.actions.act_window',
  406. target: "new",
  407. auto_search: true,
  408. flags: {
  409. sidebar: false,
  410. views_switcher: false,
  411. action_buttons: false
  412. }
  413. };
  414. var action_manager = new ActionManager(self);
  415. action_manager.do_action(action);
  416. }},
  417. {text: _t("Close"), click: function(){
  418. self.action_manager.inner_widget.active_view.controller.reload_content();
  419. self.edit_xml_dialog.close();
  420. }}
  421. ]
  422. }).open();
  423. var no_property_att = [];
  424. _.each(_PROPERTIES, function(val, key) {
  425. if (! val.length) no_property_att.push(key);
  426. });
  427. this.edit_xml_dialog.$el.html(QWeb.render('view_editor', {'data': one_object.main_object, 'no_properties': no_property_att}));
  428. this.edit_xml_dialog.$el.find("tr[id^='viewedit-']").click(function() {
  429. self.do_select_row(this.id.split('-')[1]);
  430. });
  431. this.edit_xml_dialog.$el.find("img[id^='parentimg-']").click(function() {
  432. self.do_parent_img_hide_show(this);
  433. });
  434. this.edit_xml_dialog.$el.find("img[id^='side-']").click(function() {
  435. self.on_select_img(this);
  436. });
  437. },
  438. inherited_view: function(selected_row){
  439. var self = this;
  440. var row_id = parseInt((selected_row.attr('id')).split('-')[1]);
  441. var obj = self.get_object_by_id(row_id,self.one_object.main_object, [])[0];
  442. var view_name = this.model + '.inherit_' + Math.round(Math.random() * 1000);
  443. var view_find = selected_row;
  444. var view_id;
  445. var min_level = parseInt(selected_row.attr('level'));
  446. while (1) {
  447. view_find = view_find.prev();
  448. if (view_find.length === 0 ||
  449. self.edit_xml_dialog.$el.find(view_find).find('a').text().search("view_id") != -1 &&
  450. parseInt(view_find.attr('level')) < min_level ) {
  451. view_id = parseInt($(view_find).find('a').text().replace(/[^0-9]+/g, ''));
  452. break;
  453. }
  454. if (view_find.attr('level') < min_level) {
  455. min_level = parseInt(view_find.attr('level'));
  456. }
  457. }
  458. var val = _.detect(obj.att_list, function(val) {return val[0] == "name";});
  459. var priority = _.detect(self.one_object.arch, function(val) {return val.view_id === view_id;});
  460. var arch = _.str.sprintf("<?xml version='1.0'?>\n\t <field name='%s' position='after'> </field>", val[1]);
  461. var vals = {'model': self.model, 'name': view_name, 'priority': priority.priority + 1, 'type': "form", 'arch': arch,'inherit_id':self.main_view_id};
  462. this.dataset.create(vals).done(function(id) {
  463. var arch_to_obj = self.parse_xml(arch,id);
  464. obj.child_id.push(arch_to_obj[0]);
  465. self.one_object.parent_child_id = self.parent_child_list(self.one_object.main_object,[]);
  466. self.one_object.arch.push({'view_id':id,"arch":arch,'priority': priority.priority + 1});
  467. self.increase_level(arch_to_obj[0],obj.level+1);
  468. self.render_inherited_view(selected_row,arch_to_obj[0]);
  469. });
  470. },
  471. render_inherited_view: function(selected_row,obj){
  472. var self = this,row_id = parseInt((selected_row.attr('id')).split('-')[1]);
  473. var clone = this.create_clone(selected_row.clone(),obj);
  474. if (selected_row.find("img[id^='parentimg-']").length === 0) {
  475. ($(selected_row.find('a').parent()).siblings('td'))
  476. .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif').
  477. attr('id','parentimg-'+ row_id).click(function(){
  478. self.do_parent_img_hide_show(this);
  479. }));
  480. }
  481. self.edit_xml_dialog.$el.
  482. find("tr[id='viewedit-"+row_id+"']").after(clone.removeClass('ui-selected'));
  483. _.each(obj.child_id,function(obj){self.render_inherited_view(clone,obj);});
  484. },
  485. on_select_img: function(element_img) {
  486. var self = this;
  487. var side = $(element_img).closest("tr[id^='viewedit-']");
  488. this.one_object.clicked_tr_id = parseInt((side.attr('id')).split('-')[1]);
  489. this.one_object.clicked_tr_level = parseInt(side.attr('level'));
  490. var img = side.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']").attr('src');
  491. var view_id = 0, view_xml_id = 0, view_find = side;
  492. //for view id finding
  493. var min_level = this.one_object.clicked_tr_id;
  494. if (($(side).find('a').text()).search("view_id") != -1) {
  495. view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, ''));
  496. view_xml_id = (view_find.attr('id')).split('-')[1];
  497. this.one_object.clicked_tr_id += 1;
  498. this.one_object.clicked_tr_level += 1;
  499. }else{
  500. while (1) {
  501. view_find = view_find.prev();
  502. if (view_find.length === 0 ||
  503. (self.edit_xml_dialog.$el.find(view_find).find('a').text()).search("view_id") !== -1
  504. && parseInt(view_find.attr('level')) < min_level ) {
  505. view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, ''));
  506. view_xml_id = parseInt((view_find.attr('id')).split('-')[1]);
  507. break;
  508. }
  509. if (view_find.attr('level') < min_level) {
  510. min_level = parseInt(view_find.attr('level'));
  511. }
  512. }
  513. }
  514. this.one_object.clicked_tr_view = [view_id, view_xml_id];
  515. switch (element_img.id) {
  516. case "side-add":
  517. self.do_node_add(side);
  518. break;
  519. case "side-remove":
  520. if (confirm(_t("Do you really want to remove this node?"))) {
  521. self.do_save_update_arch("remove_node");
  522. }
  523. break;
  524. case "side-edit":
  525. self.do_node_edit(side);
  526. break;
  527. case "side-up":
  528. self.do_node_up(side, img);
  529. break;
  530. case "side-down":
  531. self.do_node_down(side, img);
  532. break;
  533. }
  534. },
  535. do_node_add: function(side){
  536. var self = this;
  537. var property_to_check = [];
  538. var tr = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object.main_object, [])[0].att_list[0];
  539. var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0];
  540. var field_dataset = new data.DataSetSearch(this, this.model, null, null);
  541. if(_.isUndefined(parent_tr))
  542. return;
  543. parent_tr = self.get_object_by_id(parseInt($(parent_tr).attr('id').replace(/[^0-9]+/g, '')), this.one_object.main_object, [])[0].att_list[0];
  544. _.each([tr, parent_tr],function(element) {
  545. var value = _.has(_CHILDREN, element) ? element : _.str.include(html_tag, element)?"html_tag":false;
  546. property_to_check.push(value);
  547. });
  548. field_dataset.call( 'fields_get', []).done(function(result) {
  549. var fields = _.keys(result);
  550. fields.push(" ");
  551. fields.sort();
  552. self.on_add_node(property_to_check, fields, self.inject_position(parent_tr,tr));
  553. });
  554. },
  555. inject_position : function(parent_tag,current_tag){
  556. if(parent_tag == "view")
  557. return ['Inside'];
  558. if(current_tag == "field")
  559. return ['After','Before'];
  560. return ['After','Before','Inside'];
  561. },
  562. do_node_edit: function() {
  563. var self = this;
  564. var result = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object.main_object, []);
  565. if (result.length && result[0] && result[0].att_list) {
  566. var properties = _PROPERTIES[result[0].att_list[0]];
  567. self.on_edit_node(properties);
  568. }
  569. },
  570. do_node_down: function(cur_tr, img) {
  571. var self = this;
  572. var next_tr, last_tr, tr_to_move = [];
  573. tr_to_move.push(cur_tr);
  574. if (img) {
  575. while (1) {
  576. next_tr = cur_tr.next();
  577. if ( parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length === 0) {
  578. last_tr = next_tr;
  579. break;
  580. } else {
  581. tr_to_move.push(next_tr);
  582. cur_tr = next_tr;
  583. }
  584. }
  585. } else {
  586. last_tr = cur_tr.next();
  587. }
  588. if ((self.edit_xml_dialog.$el.find(last_tr).find('a').text()).search("view_id") != -1) {
  589. return false;
  590. }
  591. if (last_tr.length !== 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level) {
  592. var last_tr_id = (last_tr.attr('id')).split('-')[1];
  593. img = last_tr.find("img[id='parentimg-" + last_tr_id + "']").attr('src');
  594. if (img) {
  595. self.edit_xml_dialog.$el.find("img[id='parentimg-" + last_tr_id + "']").
  596. attr('src', '/web/static/src/img/expand.gif');
  597. while (1) {
  598. next_tr = last_tr.next();
  599. if (next_tr.attr('level') <= this.one_object.clicked_tr_level || next_tr.length === 0) break;
  600. next_tr.hide();
  601. last_tr = next_tr;
  602. }
  603. }
  604. tr_to_move.reverse();
  605. _.each(tr_to_move, function(rec) {
  606. $(last_tr).after(rec);
  607. });
  608. self.do_save_update_arch("down");
  609. }
  610. },
  611. do_node_up: function(cur_tr, img) {
  612. var self = this;
  613. var side = cur_tr;
  614. var tr_to_move = [];
  615. var last_tr;
  616. var next_tr;
  617. tr_to_move.push(side);
  618. while (1) {
  619. var prev_tr = cur_tr.prev();
  620. if (this.one_object.clicked_tr_level >= parseInt(prev_tr.attr('level')) || prev_tr.length === 0) {
  621. last_tr = prev_tr;
  622. break;
  623. }
  624. cur_tr = prev_tr;
  625. }
  626. if (img) {
  627. self.edit_xml_dialog.$el.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']").
  628. attr('src', '/web/static/src/img/expand.gif');
  629. while (1) {
  630. next_tr = side.next();
  631. if (parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length === 0) {
  632. break;
  633. } else {
  634. next_tr.hide();
  635. tr_to_move.push(next_tr);
  636. side = next_tr;
  637. }
  638. }
  639. }
  640. if (last_tr.length !== 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level &&
  641. (self.edit_xml_dialog.$el.find(last_tr).find('a').text()).search("view_id") === -1) {
  642. _.each(tr_to_move, function(rec) {
  643. $(last_tr).before(rec);
  644. });
  645. self.do_save_update_arch("up");
  646. }
  647. },
  648. do_save_update_arch: function(move_direct, update_values) {
  649. var self = this;
  650. var arch = _.detect(self.one_object.arch, function(element)
  651. {return element.view_id === self.one_object.clicked_tr_view[0];});
  652. var obj = self.get_object_by_id(this.one_object.clicked_tr_view[1],this.one_object.main_object, []);
  653. //for finding xpath tag from inherit view
  654. var xml_arch = QWeb.load_xml(arch.arch);
  655. if (xml_arch.childNodes[0].tagName == "data") {
  656. var check_list = _.flatten(obj[0].child_id[0].att_list);
  657. var children = _.filter(xml_arch.childNodes[0].childNodes, function (child) {
  658. return child.nodeType == 1;
  659. });
  660. var inherited_view = _.detect(children, function(xml_child) {
  661. var temp_obj = self.create_View_Node(xml_child),
  662. insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list));
  663. if (insert.length == _.uniq(check_list).length ) {return xml_child;}
  664. });
  665. xml_arch = QWeb.load_xml(utils.xml_to_str(inherited_view));
  666. }
  667. return self.do_save_xml(xml_arch.documentElement, obj[0].child_id[0],obj[0].child_id, move_direct, update_values,arch);
  668. },
  669. get_object_by_id: function(id, one_object, result) {
  670. var self = this;
  671. if (result.length === 0 ) {
  672. var check = _.detect(one_object , function(obj) {
  673. return id === obj.id;
  674. });
  675. if (check) {result.push(check);}
  676. _.each(one_object, function(obj) {
  677. self.get_object_by_id(id,obj.child_id, result);
  678. });
  679. }
  680. return result;
  681. },
  682. create_clone: function(clone, new_node_obj){
  683. var self = this;
  684. clone.find('a').text(new_node_obj.name);
  685. ($(clone.find('a').parent()).siblings('td')).css( "padding-left", 20 * new_node_obj.level);
  686. clone.attr("id", "viewedit-" + new_node_obj.id);
  687. clone.attr("level", new_node_obj.level);
  688. clone.find("img[id^='parentimg-']").remove();
  689. clone.bind("click",function(){
  690. self.do_select_row(this.id.split('-')[1]);
  691. });
  692. clone.find("img[id^='side-']").click(function() {
  693. self.on_select_img(this);
  694. });
  695. return clone;
  696. },
  697. do_save_xml: function(arch1, obj, child_list, move_direct, update_values, arch){
  698. var self = this; // blame this line if something terribly wrong happens
  699. var children_list = $(arch1).children();
  700. var list_obj_xml;
  701. var re_insert_obj;
  702. try{list_obj_xml = _.zip(children_list, obj.child_id);}catch(err){return;}
  703. if (this.one_object.clicked_tr_id) {
  704. if (obj.id == this.one_object.clicked_tr_id) {
  705. var parent = false,
  706. index = _.indexOf(child_list, obj);
  707. if (move_direct == "down") {
  708. var next = $(arch1).next();
  709. $(next).after(arch1);
  710. re_insert_obj = child_list.splice(index, 1);
  711. child_list.splice(index+1, 0, re_insert_obj[0]);
  712. parent = $(arch1).parents();
  713. } else if (move_direct == "up") {
  714. var prev = $(arch1).prev();
  715. $(prev).before(arch1);
  716. re_insert_obj = child_list.splice(index, 1);
  717. child_list.splice(index-1, 0, re_insert_obj[0]);
  718. parent = $(arch1).parents();
  719. } else if (move_direct == "update_node") {
  720. _.each(update_values, function(val){
  721. if (val[1]) $(arch1)[0].setAttribute(val[0], val[1]);
  722. else $(arch1)[0].removeAttribute(val[0]);
  723. });
  724. var new_obj = self.create_View_Node(arch1);
  725. new_obj.id = obj.id,new_obj.child_id = obj.child_id;
  726. self.edit_xml_dialog.$el.
  727. find("tr[id='viewedit-"+this.one_object.clicked_tr_id+"']").
  728. find('a').text(new_obj.name);
  729. child_list.splice(index, 1, new_obj);
  730. parent = $(arch1).parents();
  731. } else if(move_direct == "add_node") {
  732. var tr_click = self.edit_xml_dialog.$el.find("tr[id='viewedit-"+self.one_object.clicked_tr_id+"']"),
  733. temp_xml = QWeb.load_xml(update_values[0]),
  734. object_xml = self.create_View_Node(temp_xml.childNodes[0]);
  735. (update_values[1] == "Inside")? object_xml.level = obj.level + 1:object_xml.level = obj.level;
  736. var clone = self.create_clone(tr_click.clone(),object_xml),
  737. after_append = _.detect(self.one_object.parent_child_id,function(ele){
  738. return self.one_object.clicked_tr_id == ele.key;
  739. });
  740. after_append = (after_append)?_.last(after_append.value):self.one_object.clicked_tr_id;
  741. switch (update_values[1]) {
  742. case "After":
  743. self.edit_xml_dialog.$el.
  744. find("tr[id='viewedit-"+after_append+"']").after(clone);
  745. $(arch1).after($(update_values[0]));
  746. child_list.splice(index + 1, 0, object_xml);
  747. break;
  748. case "Before":
  749. tr_click.before(clone);
  750. $(arch1).before($(update_values[0]));
  751. child_list.splice(index - 1, 0, object_xml);
  752. break;
  753. case "Inside":
  754. if (tr_click.find("img[id^='parentimg-']").length === 0) {
  755. ($(tr_click.find('a').parent()).siblings('td'))
  756. .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif').
  757. attr('id','parentimg-'+ self.one_object.clicked_tr_id).click(function(){
  758. self.do_parent_img_hide_show(this);
  759. }));
  760. }
  761. $(arch1).append($(update_values[0]));
  762. self.edit_xml_dialog.$el.
  763. find("tr[id='viewedit-"+after_append+"']").after(clone);
  764. obj.child_id.push(object_xml);
  765. break;
  766. }
  767. self.edit_xml_dialog.$el.
  768. find("tr[id='viewedit-" + object_xml.id + "']").removeClass('ui-selected');
  769. parent = $(arch1).parents();
  770. } else if (move_direct == "remove_node") {
  771. parent = $(arch1).parents();
  772. if (parent.length === 0 || (parent[0].tagName.toLowerCase() == "data")) {
  773. self.one_object.clicked_tr_id = self.one_object.clicked_tr_id -1;
  774. self.one_object.clicked_tr_level = self.one_object.clicked_tr_level - 1;
  775. (parent.length === 0)?parent.push("remove_view"):false;
  776. }
  777. $(arch1).remove();
  778. child_list.splice(index,1);
  779. var cur_tr = self.edit_xml_dialog.$el.
  780. find("tr[id='viewedit-" + self.one_object.clicked_tr_id + "']");
  781. _.each(self.get_list_tr(cur_tr,self.one_object.clicked_tr_level), function(tr_element){
  782. tr_element.remove();
  783. });
  784. cur_tr.remove();
  785. var parent_img = _.detect(self.one_object.parent_child_id,function(element){
  786. return _.include(element.value, self.one_object.clicked_tr_id);
  787. });
  788. if(parent_img.value.length == 1){
  789. self.edit_xml_dialog.$el.
  790. find("tr[id='viewedit-"+parent_img.key+"']").
  791. find("img[id^='parentimg-']").remove();
  792. }
  793. self.one_object.parent_child_id = self.parent_child_list(self.one_object.main_object,[]);
  794. }
  795. var convert_to_utf = (parent.length !== 0)? parent[parent.length-1]: arch1;
  796. if (convert_to_utf != "remove_view") {
  797. convert_to_utf = QWeb.tools.xml_node_to_string(convert_to_utf);
  798. convert_to_utf = convert_to_utf.replace('xmlns="http://www.w3.org/1999/xhtml"', "");
  799. convert_to_utf = '<?xml version="1.0"?>' + convert_to_utf;
  800. arch.arch = convert_to_utf;
  801. this.dataset.write(this.one_object.clicked_tr_view[0] ,{"arch":convert_to_utf});
  802. } else {
  803. this.dataset.unlink([this.one_object.clicked_tr_view[0]]);
  804. }
  805. if(move_direct === "add_node"){
  806. self.add_node_dialog.close();
  807. self.on_select_img(clone.find("img[id='side-edit']")[0]);
  808. self.one_object.parent_child_id = self.parent_child_list(self.one_object.main_object,[]);
  809. }
  810. }
  811. if (obj.level <= this.one_object.clicked_tr_level) {
  812. _.each(list_obj_xml, function(child_node) {
  813. self.do_save_xml(child_node[0], child_node[1], obj.child_id, move_direct, update_values, arch);
  814. });
  815. }
  816. }
  817. },
  818. on_expand: function(expand_img){
  819. var level = parseInt($(expand_img).closest("tr[id^='viewedit-']").attr('level'));
  820. var cur_tr = $(expand_img).closest("tr[id^='viewedit-']");
  821. _.each(this.get_list_tr(cur_tr,level), function(tr_element){
  822. tr_element.hide();
  823. });
  824. },
  825. get_list_tr: function(cur_tr,level){
  826. var tr_list = [];
  827. while (1) {
  828. var nxt_tr = cur_tr.next();
  829. if (parseInt(nxt_tr.attr('level')) > level) {
  830. cur_tr = nxt_tr;
  831. tr_list.push(nxt_tr);
  832. } else return tr_list;
  833. }
  834. },
  835. on_collapse: function(collapse_img) {
  836. var self = this, id = collapse_img.id.split('-')[1];
  837. var datas = _.detect(self.one_object.parent_child_id , function(res) {
  838. return res.key == id;
  839. });
  840. _.each(datas.value, function (rec) {
  841. var tr = self.edit_xml_dialog.$el.find("tr[id='viewedit-" + rec + "']");
  842. tr.find("img[id='parentimg-" + rec + "']").attr('src', '/web/static/src/img/expand.gif');
  843. tr.show();
  844. });
  845. },
  846. on_edit_node: function(properties){
  847. var self = this;
  848. this.edit_node_dialog = new Dialog(this,{
  849. title: _t("Properties"),
  850. size: 'medium',
  851. buttons: [
  852. {text: _t("Update"), click: function () {
  853. var warn = false, update_values = [];
  854. _.each(self.edit_widget, function(widget) {
  855. if (widget.is_invalid) {
  856. warn = true;
  857. return false;
  858. }
  859. if (widget.dirty && !widget.is_invalid) {
  860. update_values.push([widget.name, widget.get_value()]);
  861. }
  862. });
  863. if (warn) {
  864. self.on_valid_create_view(self.edit_widget);
  865. } else {
  866. self.do_save_update_arch("update_node", update_values);
  867. self.edit_node_dialog.close();
  868. }
  869. }},
  870. {text: _t("Cancel"), click: function () { self.edit_node_dialog.close(); }}
  871. ]
  872. }).open();
  873. var _PROPERTIES_ATTRIBUTES = {
  874. 'name' : {'name':'name', 'string': 'Name', 'type': 'char'},
  875. 'string' : {'name':'string', 'string': 'String', 'type': 'char'},
  876. 'required' : {'name':'required', 'string': 'Required', 'type': 'boolean'},
  877. 'readonly' : {'name':'readonly', 'string': 'Readonly', 'type': 'boolean'},
  878. 'invisible' : {'name':'invisible', 'string': 'Invisible', 'type': 'boolean'},
  879. 'domain' : {'name':'domain', 'string': 'Domain', 'type': 'char'},
  880. 'context' : {'name':'context', 'string': 'Context', 'type': 'char'},
  881. 'limit' : {'name':'limit', 'string': 'Limit', 'type': 'float'},
  882. 'min_rows' : {'name':'min_rows', 'string': 'Minimum rows', 'type': 'float'},
  883. 'date_start' : {'name':'date_start', 'string': 'Start date', 'type': 'char'},
  884. 'date_delay' : {'name':'date_delay', 'string': 'Delay date', 'type': 'char'},
  885. 'day_length' : {'name':'day_length', 'string': 'Day length', 'type': 'char'},
  886. 'mode' : {'name':'mode', 'string': 'Mode', 'type': 'char'},
  887. 'align' : {'name':'align', 'string': 'Alignment ', 'type': 'selection', 'selection': [['', ''], ['0.0', 'Left'], ['0.5', 'Center'], ['1.0', 'Right']]},
  888. 'icon' : {'name':'icon', 'string': 'Icon', 'type': 'selection', 'selection': _ICONS},
  889. 'type' : {'name':'type', 'string': 'Type', 'type': 'selection', 'selection': [['', ''], ['action', 'Action'], ['object', 'Object'], ['workflow', 'Workflow'], ['server_action', 'Server Action']]},
  890. 'special' : {'name':'special', 'string': 'Special', 'type': 'selection', 'selection': [['',''],['save', 'Save Button'], ['cancel', 'Cancel Button'], ['open', 'Open Button']]},
  891. 'target' : {'name':'target', 'string': 'Target', 'type': 'selection', 'selection': [['', ''], ['new', 'New Window']]},
  892. 'confirm' : {'name':'confirm', 'string': 'Confirm', 'type': 'char'},
  893. 'style' : {'name':'style', 'string': 'Style', 'type': 'selection', 'selection':[["",""],["1", "1"],["1-1", "1-1"],["1-2", "1-2"],["2-1", "2-1"],["1-1-1", "1-1-1"]]},
  894. 'filename' : {'name':'filename', 'string': 'File Name', 'type': 'char'},
  895. 'width' : {'name':'width', 'string': 'Width', 'type': 'float'},
  896. 'height' : {'name':'height', 'string': 'Height', 'type': 'float'},
  897. 'attrs' : {'name':'attrs', 'string': 'Attrs', 'type': 'char'},
  898. 'col' : {'name':'col', 'string': 'col', 'type': 'float'},
  899. 'link' : {'name':'link', 'string': 'Link', 'type': 'char'},
  900. 'position' : {'name':'position', 'string': 'Position', 'type': 'selection', 'selection': [['',''],['after', 'After'],['before', 'Before'],['inside', 'Inside'],['replace', 'Replace']]},
  901. 'states' : {'name':'states', 'string': 'states', 'type': 'char'},
  902. 'eval' : {'name':'eval', 'string': 'Eval', 'type': 'char'},
  903. 'ref' : {'name':'ref', 'string': 'Ref', 'type': 'char'},
  904. 'on_change' : {'name':'on_change', 'string': 'On change', 'type': 'char'},
  905. 'nolabel' : {'name':'nolabel', 'string': 'No label', 'type': 'boolean'},
  906. 'completion' : {'name':'completion', 'string': 'Completion', 'type': 'boolean'},
  907. 'colspan' : {'name':'colspan', 'string': 'Colspan', 'type': 'float'},
  908. 'widget' : {'name':'widget', 'string': 'widget', 'type': 'selection'},
  909. 'colors' : {'name':'colors', 'string': 'Colors', 'type': 'char'},
  910. 'editable' : {'name':'editable', 'string': 'Editable', 'type': 'selection', 'selection': [["",""],["top","Top"],["bottom", "Bottom"]]},
  911. 'groups' : {'name':'groups', 'string': 'Groups', 'type': 'selection_multi'},
  912. 'fonts' : {'name':'fonts', 'string': 'fonts', 'type': 'char'},
  913. };
  914. var arch_val = self.get_object_by_id(this.one_object.clicked_tr_id,this.one_object.main_object, []);
  915. this.edit_node_dialog.$el.append('<table id="rec_table" style="width:400px" class="oe_form"></table>');
  916. this.edit_widget = [];
  917. self.ready = $.when(self.on_groups(properties)).done(function () {
  918. _PROPERTIES_ATTRIBUTES.groups.selection = self.groups;
  919. var values = _.keys(core.form_widget_registry.map);
  920. values.push('');
  921. values.sort();
  922. _PROPERTIES_ATTRIBUTES.widget.selection = values;
  923. var widgets = _.filter(_PROPERTIES_ATTRIBUTES, function (property) { return _.include(properties, property.name);});
  924. _.each(widgets, function(widget) {
  925. var type_widget = new (self.property.get_any([widget.type])) (self.edit_node_dialog, widget);
  926. var value = _.detect(arch_val[0].att_list,function(res) {
  927. return res instanceof Array? _.include(res, widget.name): false;
  928. });
  929. value = value instanceof Array ? value[1] : value;
  930. self.edit_node_dialog.$el.find('table[id=rec_table]').append('<tr><td align="right">' + widget.string + ':</td>' + type_widget.render() + '</tr>');
  931. type_widget.start();
  932. type_widget.set_value(value);
  933. self.edit_widget.push(type_widget);
  934. });
  935. });
  936. },
  937. //for getting groups
  938. on_groups: function(properties){
  939. var self = this,
  940. def = $.Deferred();
  941. if (!_.include(properties, 'groups')) {
  942. self.groups = false;
  943. def.resolve();
  944. }
  945. var group_ids = [], group_names = {}, groups = [];
  946. var res_groups = new data.DataSetSearch(this,'res.groups', null, null),
  947. model_data = new data.DataSetSearch(self,'ir.model.data', null, null);
  948. res_groups.read_slice([], {}).done(function (res_grp) {
  949. _.each(res_grp, function (res) {
  950. var key = res.id;
  951. group_names[key]=res.full_name;
  952. group_ids.push(res.id);
  953. });
  954. model_data.read_slice([], {domain: [
  955. ['res_id', 'in', group_ids],
  956. ['model', '=', 'res.groups']
  957. ]}).done(function (model_grp) {
  958. _.each(model_grp, function (res_group) {
  959. groups.push([res_group.module + "." + res_group.name, group_names[res_group.res_id]]);
  960. });
  961. self.groups = groups;
  962. def.resolve();
  963. });
  964. });
  965. return def.promise();
  966. },
  967. on_add_node: function(properties, fields, position){
  968. var self = this;
  969. var render_list = [{'name': 'node_type','selection': _.keys(_CHILDREN).sort(), 'value': 'field', 'string': 'Node Type','type': 'selection'},
  970. {'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'},
  971. {'name': 'position','selection': position, 'value': false, 'string': 'Position','type': 'selection'}];
  972. this.add_widget = [];
  973. this.add_node_dialog = new Dialog(this,{
  974. title: _t("Properties"),
  975. size: 'medium',
  976. buttons: [
  977. {text: _t("Update"), click: function() {
  978. var check_add_node = true, values = {};
  979. _.each(self.add_widget, function(widget) {
  980. values[widget.name] = widget.get_value() || false;
  981. });
  982. (values.position == "Inside")?
  983. check_add_node =(_.include(_CHILDREN[properties[0]],values.node_type))?true:false:
  984. check_add_node =(_.include(_CHILDREN[properties[1]],values.node_type))?true:false;
  985. if(values.node_type == "field" && check_add_node )
  986. {check_add_node = (values.field_value != " ")?true:false;
  987. }
  988. if(check_add_node){