integration.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /**
  6. * jQuery plugin is added.
  7. *
  8. * @api
  9. */
  10. define([
  11. 'jquery',
  12. 'Magento_Ui/js/modal/alert',
  13. 'jquery/ui',
  14. 'mage/translate',
  15. 'Magento_Ui/js/modal/modal'
  16. ], function ($, alert) {
  17. 'use strict';
  18. $.widget('mage.integration', {
  19. /**
  20. * Options common to all instances of this widget.
  21. * @type {Object}
  22. */
  23. options: {
  24. /**
  25. * URL of the integration grid.
  26. * @type {String}
  27. */
  28. gridUrl: ''
  29. },
  30. /**
  31. * Bind event handler for the action when admin clicks "Save & Activate" button.
  32. * @private
  33. */
  34. _create: function () {
  35. if ($('#save-split-button-activate').length) {
  36. // We're on the "New integration" page - bind related handler
  37. this._form = $('#edit_form');
  38. this._form.on('saveAndActivate', $.proxy(this._saveAndActivate, this));
  39. }
  40. },
  41. /**
  42. * Save new integration, then kick off the activate dialog.
  43. * @private
  44. */
  45. _saveAndActivate: function () {
  46. if (this._form.validation && !this._form.validation('isValid')) {
  47. return false;
  48. }
  49. $.ajax({
  50. url: this._form.prop('action'),
  51. type: 'post',
  52. data: this._form.serialize(),
  53. dataType: 'json',
  54. context: this,
  55. /** @inheritdoc */
  56. beforeSend: function () {
  57. $('body').trigger('processStart');
  58. },
  59. /** @inheritdoc */
  60. success: function (data) {
  61. var integrationName, that;
  62. if (data._redirect) {
  63. window.location.href = data._redirect;
  64. } else if (data.integrationId) {
  65. integrationName = $('#integration_properties_name').val();
  66. window.integration.popup.show($('<span>').attr({
  67. 'data-row-dialog': 'permissions',
  68. 'data-row-id': data.integrationId,
  69. // We do escaping here instead of the place of actual output because _showPopup()
  70. // actually receives dialog window title from couple of places: from here and from the grid.
  71. // The issue is we always should escape values in the grid, so that value is already
  72. // escaped. To avoid double escaping we do it here instead of the output.
  73. 'data-row-name': $('<div>').text(integrationName).html(),
  74. 'data-row-is-reauthorize': '0',
  75. 'data-row-is-token-exchange': data.isTokenExchange
  76. }));
  77. that = this;
  78. $('#integration-popup-container').on('dialogclose', function () {
  79. $('body').trigger('processStart');
  80. window.location.href = that.options.gridUrl;
  81. return false;
  82. });
  83. }
  84. },
  85. /** @inheritdoc */
  86. error: function (jqXHR, status, error) {
  87. alert({
  88. content: $.mage.__('Sorry, something went wrong. Please try again later.')
  89. });
  90. window.console && console.log(status + ': ' + error + '\nResponse text:\n' + jqXHR.responseText);
  91. },
  92. /** @inheritdoc */
  93. complete: function () {
  94. $('body').trigger('processStop');
  95. }
  96. });
  97. return true;
  98. }
  99. });
  100. /**
  101. * @param {*} permissionsDialogUrl
  102. * @param {*} tokensDialogUrl
  103. * @param {*} tokensExchangeUrl
  104. * @param {*} gridUrl
  105. * @param {*} successCallbackUrl
  106. * @return {Object}
  107. * @constructor
  108. */
  109. window.Integration = function (
  110. permissionsDialogUrl,
  111. tokensDialogUrl,
  112. tokensExchangeUrl,
  113. gridUrl,
  114. successCallbackUrl
  115. ) {
  116. var url = {
  117. permissions: permissionsDialogUrl,
  118. tokens: tokensDialogUrl,
  119. tokensExchange: tokensExchangeUrl,
  120. grid: gridUrl
  121. },
  122. IdentityLogin = {
  123. win: null,
  124. strLocation: null,
  125. checker: null,
  126. isCalledBack: false,
  127. //Info popup dialog. Should be hidden when login window is closed
  128. jqInfoDialog: $('#integration-popup-container'),
  129. successCallbackUrl: successCallbackUrl,
  130. Constants: {
  131. /*
  132. This interval is set such that it adjusts to the child window closing timeout of 1000 ms. This will
  133. give the checker function enough time to detect if the successCallback has been invoked
  134. */
  135. CHECKER_INTERVAL: 500,
  136. //Login screen size plus some buffer
  137. WIDTH: 680,
  138. HEIGHT: 510,
  139. // subtract pixels(30) and width(680) from screen width to move popup from extreme left
  140. LEFT: screen.width - 680 - 30,
  141. // subtract pixels(300) and height(300) from screen height to move from top
  142. TOP: screen.height - 510 - 300
  143. },
  144. /**
  145. * @param {*} identityCallbackUrl
  146. * @param {*} consumerKey
  147. * @param {*} jqInfoDialog
  148. */
  149. invokePopup: function (identityCallbackUrl, consumerKey, jqInfoDialog) {
  150. var param;
  151. // Callback should be invoked only once. Reset callback flag on subsequent invocations.
  152. IdentityLogin.isCalledBack = false;
  153. IdentityLogin.jqInfoDialog = jqInfoDialog;
  154. param = $.param({
  155. 'oauth_consumer_key': consumerKey,
  156. 'success_call_back': IdentityLogin.successCallbackUrl
  157. });
  158. IdentityLogin.win = window.open(identityCallbackUrl + '?' + param, '',
  159. 'top=' + IdentityLogin.Constants.TOP +
  160. ', left=' + IdentityLogin.Constants.LEFT +
  161. ', width=' + IdentityLogin.Constants.WIDTH +
  162. ', height=' + IdentityLogin.Constants.HEIGHT + ',scrollbars=no');
  163. if (IdentityLogin.checker != null) {
  164. //Clear any previous check
  165. clearInterval(IdentityLogin.checker);
  166. }
  167. //Polling to detect url of the child window.
  168. IdentityLogin.checker = setInterval(
  169. IdentityLogin.fnCheckLocation, IdentityLogin.Constants.CHECKER_INTERVAL
  170. );
  171. },
  172. /**
  173. * Function to check the location of the child popup window.
  174. * Once detected if the callback is successful, parent window will be reloaded
  175. */
  176. fnCheckLocation: function () {
  177. if (IdentityLogin.win == null) {
  178. return;
  179. }
  180. // Check to see if the location has changed.
  181. try {
  182. //Is the success callback invoked
  183. if (IdentityLogin.win.closed ||
  184. IdentityLogin.win.location.href == IdentityLogin.successCallbackUrl //eslint-disable-line eqeqeq
  185. ) {
  186. //Stop the polling
  187. clearInterval(IdentityLogin.checker);
  188. $('body').trigger('processStart');
  189. //Check for window closed
  190. window.location.reload();
  191. IdentityLogin.jqInfoDialog.modal('closeModal');
  192. }
  193. } catch (e) {
  194. //squash. In case Window closed without success callback, clear polling
  195. if (IdentityLogin.win.closed) {
  196. IdentityLogin.jqInfoDialog.modal('closeModal');
  197. clearInterval(IdentityLogin.checker);
  198. }
  199. return;
  200. }
  201. }
  202. },
  203. /**
  204. * @param {Object} popupWindow
  205. * @return {Boolean}
  206. */
  207. isPopupBlocked = function (popupWindow) {
  208. try {
  209. popupWindow.focus();
  210. } catch (e) {
  211. alert({
  212. content: $.mage.__('Popup Blocker is enabled! Please add this site to your exception list.')
  213. });
  214. return true;
  215. }
  216. return false;
  217. },
  218. /**
  219. * @param {*} dialog
  220. * @param {*} title
  221. * @param {*} okButton
  222. * @param {*} ajaxUrl
  223. * @private
  224. */
  225. _showPopup = function (dialog, title, okButton, ajaxUrl) {
  226. $.ajax({
  227. url: ajaxUrl,
  228. cache: false,
  229. data: {
  230. 'form_key': window.FORM_KEY
  231. },
  232. method: 'GET',
  233. /** @inheritdoc */
  234. beforeSend: function () {
  235. // Show the spinner
  236. $('body').trigger('processStart');
  237. },
  238. /** @inheritdoc */
  239. success: function (result) {
  240. var redirect = result._redirect,
  241. identityLinkUrl, consumerKey, popupHtml, popup, resultObj, buttons, dialogProperties;
  242. if (redirect) {
  243. window.location.href = redirect;
  244. return;
  245. }
  246. identityLinkUrl = null;
  247. consumerKey = null;
  248. popupHtml = null;
  249. popup = $('#integration-popup-container');
  250. try {
  251. resultObj = typeof result === 'string' ?
  252. JSON.parse(result) :
  253. result;
  254. identityLinkUrl = resultObj['identity_link_url'];
  255. consumerKey = resultObj['oauth_consumer_key'];
  256. popupHtml = resultObj['popup_content'];
  257. } catch (e) {
  258. //This is expected if result is not json. Do nothing.
  259. }
  260. if (identityLinkUrl && consumerKey && popupHtml) {
  261. IdentityLogin.invokePopup(identityLinkUrl, consumerKey, popup);
  262. if (isPopupBlocked(IdentityLogin.win)) {
  263. return;
  264. }
  265. } else {
  266. popupHtml = result;
  267. }
  268. if (popup.length === 0) {
  269. popup = $('<div/>');
  270. }
  271. popup.html(popupHtml);
  272. buttons = [];
  273. dialogProperties = {
  274. title: title,
  275. type: 'slide',
  276. dialogClass: dialog == 'permissions' ? 'integration-dialog' : 'integration-dialog no-close' //eslint-disable-line
  277. };
  278. // Add confirmation button to the list of dialog buttons. okButton not set for tokenExchange dialog
  279. if (okButton) {
  280. buttons.push(okButton);
  281. }
  282. // Add button only if its not empty
  283. if (buttons.length > 0) {
  284. dialogProperties.buttons = buttons;
  285. }
  286. popup.modal(dialogProperties);
  287. popup.modal('openModal');
  288. },
  289. /** @inheritdoc */
  290. error: function (jqXHR, status, error) {
  291. alert({
  292. content: $.mage.__('Sorry, something went wrong. Please try again later.')
  293. });
  294. window.console && console.log(status + ': ' + error + '\nResponse text:\n' + jqXHR.responseText);
  295. },
  296. /** @inheritdoc */
  297. complete: function () {
  298. // Hide the spinner
  299. $('body').trigger('processStop');
  300. }
  301. });
  302. };
  303. return {
  304. popup: {
  305. /**
  306. * @param {*} ctx
  307. */
  308. show: function (ctx) {
  309. var dialog = $(ctx).attr('data-row-dialog'),
  310. isReauthorize = $(ctx).attr('data-row-is-reauthorize'),
  311. isTokenExchange = $(ctx).attr('data-row-is-token-exchange'),
  312. integrationId, ajaxUrl, integrationName, okButton;
  313. if (!url.hasOwnProperty(dialog)) {
  314. throw 'Invalid dialog type';
  315. }
  316. integrationId = $(ctx).attr('data-row-id');
  317. if (!integrationId) {
  318. throw 'Unable to find integration ID';
  319. }
  320. // Replace placeholders in URL
  321. ajaxUrl = url[dialog].replace(':id', integrationId).replace(':isReauthorize', isReauthorize);
  322. try {
  323. // Get integration name either from current element or from neighbor column
  324. integrationName = $(ctx).attr('data-row-name') ||
  325. $(ctx).parents('tr').find('.col-name').html().trim();
  326. if (integrationName.indexOf('<span') > -1) {
  327. // Remove unsecure URL warning from popup window title if it is present
  328. integrationName = integrationName.substring(0, integrationName.indexOf('<span'));
  329. }
  330. } catch (e) {
  331. throw 'Unable to find integration name';
  332. }
  333. okButton = {
  334. permissions: {
  335. text: isReauthorize == '1' ? $.mage.__('Reauthorize') : $.mage.__('Allow'), //eslint-disable-line
  336. 'class': 'action-primary',
  337. attr: {
  338. 'data-row-id': integrationId,
  339. 'data-row-name': integrationName,
  340. 'data-row-dialog': isTokenExchange == '1' ? 'tokensExchange' : 'tokens', //eslint-disable-line
  341. 'data-row-is-reauthorize': isReauthorize,
  342. 'data-row-is-token-exchange': isTokenExchange
  343. },
  344. /**
  345. * Click.
  346. */
  347. click: function () {
  348. // Find the 'Allow' button and clone - it has all necessary data, but is going to be
  349. // destroyed along with the current dialog
  350. var context = this.modal.find('button.action-primary').clone(true);
  351. this.closeModal();
  352. this.modal.remove();
  353. // Make popup out of data we saved from 'Allow' button
  354. window.integration.popup.show(context);
  355. }
  356. },
  357. tokens: {
  358. text: $.mage.__('Done'),
  359. 'class': 'action-primary',
  360. /**
  361. * Click.
  362. */
  363. click: function () {
  364. // Integration has been activated at the point of generating tokens
  365. window.location.href = url.grid;
  366. }
  367. }
  368. };
  369. _showPopup(dialog, integrationName, okButton[dialog], ajaxUrl);
  370. }
  371. }
  372. };
  373. };
  374. return $.mage.integration;
  375. });