triggers.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. AJAX.registerTeardown('database/triggers.js', function () {
  2. $(document).off('click', 'a.ajax.add_anchor, a.ajax.edit_anchor');
  3. $(document).off('click', 'a.ajax.export_anchor');
  4. $(document).off('click', '#bulkActionExportButton');
  5. $(document).off('click', 'a.ajax.drop_anchor');
  6. $(document).off('click', '#bulkActionDropButton');
  7. });
  8. const DatabaseTriggers = {
  9. /**
  10. * @var $ajaxDialog Query object containing the reference to the
  11. * dialog that contains the editor
  12. */
  13. $ajaxDialog: null,
  14. /**
  15. * @var syntaxHiglighter Reference to the codemirror editor
  16. */
  17. syntaxHiglighter: null,
  18. /**
  19. * Validate editor form fields.
  20. *
  21. * @return {bool}
  22. */
  23. validate: function () {
  24. /**
  25. * @var $elm a jQuery object containing the reference
  26. * to an element that is being validated
  27. */
  28. var $elm = null;
  29. // Common validation. At the very least the name
  30. // and the definition must be provided for an item
  31. $elm = $('table.rte_table').last().find('input[name=item_name]');
  32. if ($elm.val() === '') {
  33. $elm.trigger('focus');
  34. alert(Messages.strFormEmpty);
  35. return false;
  36. }
  37. $elm = $('table.rte_table').find('textarea[name=item_definition]');
  38. if ($elm.val() === '') {
  39. if (this.syntaxHiglighter !== null) {
  40. this.syntaxHiglighter.focus();
  41. } else {
  42. $('textarea[name=item_definition]').last().trigger('focus');
  43. }
  44. alert(Messages.strFormEmpty);
  45. return false;
  46. }
  47. // The validation has so far passed, so now
  48. // we can validate item-specific fields.
  49. return this.validateCustom();
  50. },
  51. // end validate()
  52. /**
  53. * Validate custom editor form fields.
  54. * This function can be overridden by
  55. * other files in this folder
  56. *
  57. * @return {bool}
  58. */
  59. validateCustom: function () {
  60. return true;
  61. },
  62. // end validateCustom()
  63. exportDialog: function ($this) {
  64. var $msg = Functions.ajaxShowMessage();
  65. if ($this.attr('id') === 'bulkActionExportButton') {
  66. var combined = {
  67. success: true,
  68. title: Messages.strExport,
  69. message: '',
  70. error: ''
  71. };
  72. // export anchors of all selected rows
  73. var exportAnchors = $('input.checkall:checked').parents('tr').find('.export_anchor');
  74. var count = exportAnchors.length;
  75. var returnCount = 0;
  76. var p = $.when();
  77. exportAnchors.each(function () {
  78. var h = $(this).attr('href');
  79. p = p.then(function () {
  80. return $.get(h, {
  81. 'ajax_request': true
  82. }, function (data) {
  83. returnCount++;
  84. if (data.success === true) {
  85. combined.message += '\n' + data.message + '\n';
  86. if (returnCount === count) {
  87. showExport(combined);
  88. }
  89. } else {
  90. // complain even if one export is failing
  91. combined.success = false;
  92. combined.error += '\n' + data.error + '\n';
  93. if (returnCount === count) {
  94. showExport(combined);
  95. }
  96. }
  97. });
  98. });
  99. });
  100. } else {
  101. $.get($this.attr('href'), {
  102. 'ajax_request': true
  103. }, showExport);
  104. }
  105. Functions.ajaxRemoveMessage($msg);
  106. function showExport(data) {
  107. if (data.success === true) {
  108. Functions.ajaxRemoveMessage($msg);
  109. /**
  110. * @var buttonOptions Object containing options
  111. * for jQueryUI dialog buttons
  112. */
  113. var buttonOptions = {
  114. [Messages.strClose]: {
  115. text: Messages.strClose,
  116. class: 'btn btn-primary'
  117. }
  118. };
  119. buttonOptions[Messages.strClose].click = function () {
  120. $(this).dialog('close').remove();
  121. };
  122. /**
  123. * Display the dialog to the user
  124. */
  125. data.message = '<textarea cols="40" rows="15" class="w-100">' + data.message + '</textarea>';
  126. var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({
  127. classes: {
  128. 'ui-dialog-titlebar-close': 'btn-close'
  129. },
  130. width: 500,
  131. buttons: buttonOptions,
  132. title: data.title
  133. });
  134. // Attach syntax highlighted editor to export dialog
  135. /**
  136. * @var $elm jQuery object containing the reference
  137. * to the Export textarea.
  138. */
  139. var $elm = $ajaxDialog.find('textarea');
  140. Functions.getSqlEditor($elm);
  141. } else {
  142. Functions.ajaxShowMessage(data.error, false);
  143. }
  144. } // end showExport()
  145. },
  146. // end exportDialog()
  147. editorDialog: function (isNew, $this) {
  148. var that = this;
  149. /**
  150. * @var $edit_row jQuery object containing the reference to
  151. * the row of the the item being edited
  152. * from the list of items
  153. */
  154. var $editRow = null;
  155. if ($this.hasClass('edit_anchor')) {
  156. // Remember the row of the item being edited for later,
  157. // so that if the edit is successful, we can replace the
  158. // row with info about the modified item.
  159. $editRow = $this.parents('tr');
  160. }
  161. /**
  162. * @var $msg jQuery object containing the reference to
  163. * the AJAX message shown to the user
  164. */
  165. var $msg = Functions.ajaxShowMessage();
  166. $.get($this.attr('href'), {
  167. 'ajax_request': true
  168. }, function (data) {
  169. if (data.success === true) {
  170. var buttonOptions = {
  171. [Messages.strGo]: {
  172. text: Messages.strGo,
  173. class: 'btn btn-primary'
  174. },
  175. [Messages.strClose]: {
  176. text: Messages.strClose,
  177. class: 'btn btn-secondary'
  178. }
  179. };
  180. // We have successfully fetched the editor form
  181. Functions.ajaxRemoveMessage($msg);
  182. // Now define the function that is called when
  183. // the user presses the "Go" button
  184. buttonOptions[Messages.strGo].click = function () {
  185. // Move the data from the codemirror editor back to the
  186. // textarea, where it can be used in the form submission.
  187. if (typeof CodeMirror !== 'undefined') {
  188. that.syntaxHiglighter.save();
  189. }
  190. // Validate editor and submit request, if passed.
  191. if (that.validate()) {
  192. /**
  193. * @var data Form data to be sent in the AJAX request
  194. */
  195. var data = $('form.rte_form').last().serialize();
  196. $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  197. var url = $('form.rte_form').last().attr('action');
  198. $.post(url, data, function (data) {
  199. if (data.success === true) {
  200. // Item created successfully
  201. Functions.ajaxRemoveMessage($msg);
  202. Functions.slidingMessage(data.message);
  203. that.$ajaxDialog.dialog('close');
  204. // If we are in 'edit' mode, we must
  205. // remove the reference to the old row.
  206. if (mode === 'edit' && $editRow !== null) {
  207. $editRow.remove();
  208. }
  209. // Sometimes, like when moving a trigger from
  210. // a table to another one, the new row should
  211. // not be inserted into the list. In this case
  212. // "data.insert" will be set to false.
  213. if (data.insert) {
  214. // Insert the new row at the correct
  215. // location in the list of items
  216. /**
  217. * @var text Contains the name of an item from
  218. * the list that is used in comparisons
  219. * to find the correct location where
  220. * to insert a new row.
  221. */
  222. var text = '';
  223. /**
  224. * @var inserted Whether a new item has been
  225. * inserted in the list or not
  226. */
  227. var inserted = false;
  228. $('table.data').find('tr').each(function () {
  229. text = $(this).children('td').eq(0).find('strong').text().toUpperCase().trim();
  230. if (text !== '' && text > data.name) {
  231. $(this).before(data.new_row);
  232. inserted = true;
  233. return false;
  234. }
  235. });
  236. if (!inserted) {
  237. // If we didn't manage to insert the row yet,
  238. // it must belong at the end of the list,
  239. // so we insert it there.
  240. $('table.data').append(data.new_row);
  241. }
  242. // Fade-in the new row
  243. $('tr.ajaxInsert').show('slow').removeClass('ajaxInsert');
  244. } else if ($('table.data').find('tr').has('td').length === 0) {
  245. // If we are not supposed to insert the new row,
  246. // we will now check if the table is empty and
  247. // needs to be hidden. This will be the case if
  248. // we were editing the only item in the list,
  249. // which we removed and will not be inserting
  250. // something else in its place.
  251. $('table.data').hide('slow', function () {
  252. $('#nothing2display').show('slow');
  253. });
  254. }
  255. // Now we have inserted the row at the correct
  256. // position, but surely at least some row classes
  257. // are wrong now. So we will iterate through
  258. // all rows and assign correct classes to them
  259. /**
  260. * @var ct Count of processed rows
  261. */
  262. var ct = 0;
  263. /**
  264. * @var rowclass Class to be attached to the row
  265. * that is being processed
  266. */
  267. var rowclass = '';
  268. $('table.data').find('tr').has('td').each(function () {
  269. rowclass = ct % 2 === 0 ? 'odd' : 'even';
  270. $(this).removeClass().addClass(rowclass);
  271. ct++;
  272. });
  273. // If this is the first item being added, remove
  274. // the "No items" message and show the list.
  275. if ($('table.data').find('tr').has('td').length > 0 && $('#nothing2display').is(':visible')) {
  276. $('#nothing2display').hide('slow', function () {
  277. $('table.data').show('slow');
  278. });
  279. }
  280. Navigation.reload();
  281. } else {
  282. Functions.ajaxShowMessage(data.error, false);
  283. }
  284. }); // end $.post()
  285. } // end "if (that.validate())"
  286. }; // end of function that handles the submission of the Editor
  287. buttonOptions[Messages.strClose].click = function () {
  288. $(this).dialog('close');
  289. };
  290. /**
  291. * Display the dialog to the user
  292. */
  293. that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({
  294. classes: {
  295. 'ui-dialog-titlebar-close': 'btn-close'
  296. },
  297. width: '70%',
  298. minWidth: 500,
  299. buttons: buttonOptions,
  300. // Issue #15810 - use button titles for modals (eg: new procedure)
  301. // Respect the order: title on href tag, href content, title sent in response
  302. title: $this.attr('title') || $this.text() || $(data.title).text(),
  303. modal: true,
  304. open: function () {
  305. $('#rteDialog').dialog('option', 'max-height', $(window).height());
  306. if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) {
  307. $('#rteDialog').dialog('option', 'height', $(window).height());
  308. }
  309. $(this).find('input[name=item_name]').trigger('focus');
  310. $(this).find('input.datefield').each(function () {
  311. Functions.addDatepicker($(this).css('width', '95%'), 'date');
  312. });
  313. $(this).find('input.datetimefield').each(function () {
  314. Functions.addDatepicker($(this).css('width', '95%'), 'datetime');
  315. });
  316. $.datepicker.initialized = false;
  317. },
  318. close: function () {
  319. $(this).remove();
  320. }
  321. });
  322. /**
  323. * @var mode Used to remember whether the editor is in
  324. * "Edit" or "Add" mode
  325. */
  326. var mode = 'add';
  327. if ($('input[name=editor_process_edit]').length > 0) {
  328. mode = 'edit';
  329. }
  330. // Attach syntax highlighted editor to the definition
  331. /**
  332. * @var elm jQuery object containing the reference to
  333. * the Definition textarea.
  334. */
  335. var $elm = $('textarea[name=item_definition]').last();
  336. var linterOptions = {
  337. editorType: 'trigger'
  338. };
  339. that.syntaxHiglighter = Functions.getSqlEditor($elm, {}, 'both', linterOptions);
  340. } else {
  341. Functions.ajaxShowMessage(data.error, false);
  342. }
  343. }); // end $.get()
  344. },
  345. dropDialog: function ($this) {
  346. /**
  347. * @var $curr_row Object containing reference to the current row
  348. */
  349. var $currRow = $this.parents('tr');
  350. /**
  351. * @var question String containing the question to be asked for confirmation
  352. */
  353. var question = $('<div></div>').text($currRow.children('td').children('.drop_sql').html());
  354. // We ask for confirmation first here, before submitting the ajax request
  355. $this.confirm(question, $this.attr('href'), function (url) {
  356. /**
  357. * @var msg jQuery object containing the reference to
  358. * the AJAX message shown to the user
  359. */
  360. var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  361. var params = Functions.getJsConfirmCommonParam(this, $this.getPostData());
  362. $.post(url, params, function (data) {
  363. if (data.success === true) {
  364. /**
  365. * @var $table Object containing reference
  366. * to the main list of elements
  367. */
  368. var $table = $currRow.parent();
  369. // Check how many rows will be left after we remove
  370. // the one that the user has requested us to remove
  371. if ($table.find('tr').length === 3) {
  372. // If there are two rows left, it means that they are
  373. // the header of the table and the rows that we are
  374. // about to remove, so after the removal there will be
  375. // nothing to show in the table, so we hide it.
  376. $table.hide('slow', function () {
  377. $(this).find('tr.even, tr.odd').remove();
  378. $('.withSelected').remove();
  379. $('#nothing2display').show('slow');
  380. });
  381. } else {
  382. $currRow.hide('slow', function () {
  383. $(this).remove();
  384. // Now we have removed the row from the list, but maybe
  385. // some row classes are wrong now. So we will iterate
  386. // through all rows and assign correct classes to them.
  387. /**
  388. * @var ct Count of processed rows
  389. */
  390. var ct = 0;
  391. /**
  392. * @var rowclass Class to be attached to the row
  393. * that is being processed
  394. */
  395. var rowclass = '';
  396. $table.find('tr').has('td').each(function () {
  397. rowclass = ct % 2 === 1 ? 'odd' : 'even';
  398. $(this).removeClass().addClass(rowclass);
  399. ct++;
  400. });
  401. });
  402. }
  403. // Get rid of the "Loading" message
  404. Functions.ajaxRemoveMessage($msg);
  405. // Show the query that we just executed
  406. Functions.slidingMessage(data.sql_query);
  407. Navigation.reload();
  408. } else {
  409. Functions.ajaxShowMessage(data.error, false);
  410. }
  411. }); // end $.post()
  412. });
  413. },
  414. dropMultipleDialog: function ($this) {
  415. // We ask for confirmation here
  416. $this.confirm(Messages.strDropRTEitems, '', function () {
  417. /**
  418. * @var msg jQuery object containing the reference to
  419. * the AJAX message shown to the user
  420. */
  421. var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  422. // drop anchors of all selected rows
  423. var dropAnchors = $('input.checkall:checked').parents('tr').find('.drop_anchor');
  424. var success = true;
  425. var count = dropAnchors.length;
  426. var returnCount = 0;
  427. dropAnchors.each(function () {
  428. var $anchor = $(this);
  429. /**
  430. * @var $curr_row Object containing reference to the current row
  431. */
  432. var $currRow = $anchor.parents('tr');
  433. var params = Functions.getJsConfirmCommonParam(this, $anchor.getPostData());
  434. $.post($anchor.attr('href'), params, function (data) {
  435. returnCount++;
  436. if (data.success === true) {
  437. /**
  438. * @var $table Object containing reference
  439. * to the main list of elements
  440. */
  441. var $table = $currRow.parent();
  442. // Check how many rows will be left after we remove
  443. // the one that the user has requested us to remove
  444. if ($table.find('tr').length === 3) {
  445. // If there are two rows left, it means that they are
  446. // the header of the table and the rows that we are
  447. // about to remove, so after the removal there will be
  448. // nothing to show in the table, so we hide it.
  449. $table.hide('slow', function () {
  450. $(this).find('tr.even, tr.odd').remove();
  451. $('.withSelected').remove();
  452. $('#nothing2display').show('slow');
  453. });
  454. } else {
  455. $currRow.hide('fast', function () {
  456. // we will iterate
  457. // through all rows and assign correct classes to them.
  458. /**
  459. * @var ct Count of processed rows
  460. */
  461. var ct = 0;
  462. /**
  463. * @var rowclass Class to be attached to the row
  464. * that is being processed
  465. */
  466. var rowclass = '';
  467. $table.find('tr').has('td').each(function () {
  468. rowclass = ct % 2 === 1 ? 'odd' : 'even';
  469. $(this).removeClass().addClass(rowclass);
  470. ct++;
  471. });
  472. });
  473. $currRow.remove();
  474. }
  475. if (returnCount === count) {
  476. if (success) {
  477. // Get rid of the "Loading" message
  478. Functions.ajaxRemoveMessage($msg);
  479. $('#rteListForm_checkall').prop({
  480. checked: false,
  481. indeterminate: false
  482. });
  483. }
  484. Navigation.reload();
  485. }
  486. } else {
  487. Functions.ajaxShowMessage(data.error, false);
  488. success = false;
  489. if (returnCount === count) {
  490. Navigation.reload();
  491. }
  492. }
  493. }); // end $.post()
  494. }); // end drop_anchors.each()
  495. });
  496. }
  497. };
  498. AJAX.registerOnload('database/triggers.js', function () {
  499. /**
  500. * Attach Ajax event handlers for the Add/Edit functionality.
  501. */
  502. $(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) {
  503. event.preventDefault();
  504. if ($(this).hasClass('add_anchor')) {
  505. $.datepicker.initialized = false;
  506. }
  507. DatabaseTriggers.editorDialog($(this).hasClass('add_anchor'), $(this));
  508. });
  509. /**
  510. * Attach Ajax event handlers for Export
  511. */
  512. $(document).on('click', 'a.ajax.export_anchor', function (event) {
  513. event.preventDefault();
  514. DatabaseTriggers.exportDialog($(this));
  515. });
  516. $(document).on('click', '#bulkActionExportButton', function (event) {
  517. event.preventDefault();
  518. DatabaseTriggers.exportDialog($(this));
  519. });
  520. /**
  521. * Attach Ajax event handlers for Drop functionality
  522. */
  523. $(document).on('click', 'a.ajax.drop_anchor', function (event) {
  524. event.preventDefault();
  525. DatabaseTriggers.dropDialog($(this));
  526. });
  527. $(document).on('click', '#bulkActionDropButton', function (event) {
  528. event.preventDefault();
  529. DatabaseTriggers.dropMultipleDialog($(this));
  530. });
  531. });