glass_views_ShareView.js.html 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: glass/views/ShareView.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: glass/views/ShareView.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>/**
  20. * Licensed Materials - Property of IBM
  21. * IBM Cognos Products: Collaboration
  22. * (C) Copyright IBM Corp. 2017, 2020
  23. *
  24. * US Government Users Restricted Rights - Use, duplication or disclosure
  25. * restricted by GSA ADP Schedule Contract with IBM Corp.
  26. */
  27. define([
  28. 'underscore',
  29. 'jquery',
  30. 'react',
  31. 'react-dom',
  32. 'ca-ui-toolkit',
  33. '../../lib/@waca/core-client/js/core-client/ui/AccessibleView',
  34. '../../lib/@waca/core-client/js/core-client/utils/BrowserUtils',
  35. '../../lib/@waca/core-client/js/core-client/utils/DateTimeUtils',
  36. '../../nls/StringResources',
  37. '../../api/sharing/ShareController',
  38. '../utils/GlassUtil'
  39. ], function (_, $, React, ReactDOM, Toolkit, AccessibleView, BrowserUtils, DateTimeUtils, StringResources, ShareController, GlassUtil) {
  40. 'use strict';
  41. const SHARE_VIEW_CLASS = 'share-view ba-collab-fill-space ba-theme-default';
  42. var ShareView = AccessibleView.extend( /** @lends ShareView */ {
  43. /**
  44. * @desc Constructor for ShareView.
  45. * @constructs ShareView
  46. * @extends AccessibleView
  47. * @public
  48. * @param options {Object} Options
  49. * @param options.glassContext {Object} The glass context
  50. */
  51. init: function (options) {
  52. _.extend(options, {
  53. enableTabLooping: true,
  54. className: 'ba-collab-fill-space'
  55. });
  56. ShareView.inherited('init', this, arguments);
  57. this.glassContext = options.glassContext;
  58. this.shareController = new ShareController(_.extend({
  59. errorHandler: this.displayError.bind(this),
  60. slideout: this.slideout
  61. }, options));
  62. this._panel = null;
  63. this.link = options.slideout.content.link;
  64. this.embedVisible = this._isEmbedEnabled() &amp;&amp; options.slideout.content.embedVisible !== false;
  65. this.imageVisible = !!options.slideout.content.imageVisible;
  66. this._eventHandlers = this.slideout &amp;&amp; [
  67. this.slideout.on('show', this.onSlideShow.bind(this)),
  68. this.slideout.on('hide', this.onSlideHide.bind(this))
  69. ];
  70. this._logger = options.logger;
  71. try {
  72. this._instrumentationService = this.glassContext.getCoreSvc('.Instrumentation');
  73. } catch (e) {
  74. // just swallow it if the instrumentation service isn't available for some reason.
  75. void (e);
  76. }
  77. this.onSlideShow(); // event is not being called when slide is opened for the first time.
  78. this.reactContainer = null;
  79. this.shareStore = null;
  80. this.imageStore = null;
  81. },
  82. /**
  83. * It will be called after slideout is shown
  84. * @callback
  85. */
  86. setFocus: function () {
  87. if (this._panel) {
  88. this._panel.setFocus();
  89. }
  90. },
  91. _onIframeFocus: function (e) {
  92. e.stopPropagation();
  93. const activeElement = document.activeElement;
  94. if (activeElement instanceof HTMLIFrameElement
  95. &amp;&amp; !activeElement.classList.contains('cke_panel_frame')
  96. &amp;&amp; !this.$el.get(0).contains(activeElement)
  97. &amp;&amp; this.slideout.isOpen()) {
  98. this.close();
  99. }
  100. },
  101. onSlideShow: function () {
  102. var $iframe = $('iframe');
  103. if ($iframe.length > 0 &amp;&amp; this.slideout) {
  104. this._onIframeFocusFunc = this._onIframeFocus.bind(this);
  105. $(window).on('blur', this._onIframeFocusFunc);
  106. }
  107. if (this._panel) {
  108. const assetStrings = this.shareController.getAssetStrings();
  109. this.shareStore.setAssetType(assetStrings.assetType);
  110. this.shareStore.setAssetTitle(assetStrings.assetTitle);
  111. this.shareStore.setIsDirty(this._isDirty());
  112. return Promise.all([ this.refreshLink(), this.refreshIncludeImage() ])
  113. .then(results => {
  114. const link = results[0];
  115. // if cannot send, don't need to get connectors
  116. if (!this.canSend()) {
  117. this.shareStore.setConnectors([]);
  118. this.imageStore.setImageLoaded(false);
  119. return link;
  120. }
  121. // refresh connectors
  122. return this.getConnectors()
  123. .then((connectors) => {
  124. this.shareStore.setConnectors(connectors);
  125. this.imageStore.setImageLoaded(false);
  126. return link;
  127. });
  128. });
  129. }
  130. return Promise.resolve(null);
  131. },
  132. onSlideHide: function () {
  133. var $iframe = $('iframe');
  134. if ($iframe.length > 0 &amp;&amp; this.slideout &amp;&amp; this._onIframeFocusFunc) {
  135. this._onIframeFocusFunc = null;
  136. $(window).off('blur', this._onIframeFocusFunc);
  137. }
  138. return this.shareController.leaveShareState();
  139. },
  140. /**
  141. * Sets the current perspective's link URL.
  142. * @param link
  143. */
  144. setLink: function (link) {
  145. if (this.shareStore) {
  146. this.shareStore.setLink(link);
  147. }
  148. },
  149. /**
  150. * Refresh the current perspective's link URL.
  151. * @instance
  152. * @returns {Promise} resolved as an object that contains shareUrl and embedUrl
  153. */
  154. refreshLink: function () {
  155. return new Promise((resolve, reject) => {
  156. if (this.link) {
  157. this.setLink(this.link);
  158. resolve(this.link);
  159. } else {
  160. this.shareController.getLink(this.glassContext)
  161. .then((link) => {
  162. this.setLink(link);
  163. resolve(link);
  164. })
  165. .catch((error) => {
  166. this.setLink(null);
  167. reject(error);
  168. });
  169. }
  170. });
  171. },
  172. /**
  173. * Fetch user's configured product locale and Product version.
  174. * @instance
  175. * @returns {Promise} with the user's browser language.
  176. */
  177. getConfig: function () {
  178. // The passed in version number is of the form "11.1 RX", but the knowledge center
  179. // uses the form "11.1.X". Until glass provides consistent versioning with the knowlege
  180. // center, we will hard-code the version here.
  181. return this.glassContext.getSvc('.UserProfile')
  182. .then(userProfile => {
  183. const language = userProfile.preferences.productLocale ? userProfile.preferences.productLocale : 'en';
  184. return {
  185. language,
  186. version: '11.1.0'
  187. };
  188. });
  189. },
  190. /**
  191. * Capture the current perspective's screenshot image.
  192. * @instance
  193. * @returns {Promise} with an image string as URL.
  194. */
  195. refreshImage: function () {
  196. return new Promise((resolve, reject) => {
  197. if (this._panel &amp;&amp; this._panel.state.connector) {
  198. if (this._panel.state.connector.isImageSupported()) {
  199. this.shareController
  200. .getScreenshot(this.glassContext)
  201. .then((item) => {
  202. var img = new Image();
  203. img.onload = () => {
  204. this.imageStore.setImage(item.image, item.label, img.width, img.height);
  205. resolve(item);
  206. };
  207. img.src = item.image;
  208. })
  209. .catch((error) => {
  210. this.glassContext.showToast(StringResources.get('error_no_screenshot'), {
  211. type: 'error'
  212. });
  213. this.imageStore.setImage('', '', 0, 0);
  214. reject(error);
  215. });
  216. } else {
  217. var errorTxt = StringResources.get('error_screenshot_unsupported');
  218. this.glassContext.showToast(errorTxt, {
  219. type: 'warning'
  220. });
  221. reject(new Error(errorTxt));
  222. }
  223. } else {
  224. resolve(); // We're not trying to load a screenshot yet.
  225. }
  226. });
  227. },
  228. /**
  229. * Returns if the content supports export to pdf
  230. * @returns Promise
  231. */
  232. canExportToPDF: function () {
  233. return this.shareController.canExportToPDF(this.glassContext).then((result) => {
  234. // when link is set, we were called from My/Team Content navigation.
  235. return result &amp;&amp; !this.link &amp;&amp; this.features.includes(GlassUtil.feature.EXPORT);
  236. });
  237. },
  238. /**
  239. * Returns whether "Include image" option should be visible or hidden.
  240. *
  241. * @returns {Promise} that resolves to a boolean value.
  242. */
  243. includeImage: function () {
  244. if (!this.imageVisible) {
  245. return Promise.resolve(false);
  246. }
  247. // Authoring perspective supports image capture but requires an extra check
  248. // of the current report format. Image capture should be enabled for HTML reports;
  249. // disabled for PDF reports.
  250. return this.shareController.canCaptureImage(this.glassContext);
  251. },
  252. /**
  253. * Update "Include image" flags in the ImageStore.
  254. *
  255. * One use case where it's necessary:
  256. * 1. Run HTML report.
  257. * 2. Open Share / Email panel ==> "Include image" should be enabled
  258. * 3. Hide Share slideout.
  259. * 4. Run the same report as PDF in the report viewer.
  260. * 5. Open Share panel ==> "Include image" should be disabled.
  261. */
  262. refreshIncludeImage: function() {
  263. if (!this.imageStore) {
  264. return Promise.resolve();
  265. }
  266. return this.includeImage().then(result => {
  267. this.imageStore.setShowIncludeImage(result);
  268. return result;
  269. });
  270. },
  271. _checkLinkShareable: function (link) {
  272. var pathQuery = 'pathRef';
  273. if (!!this.type &amp;&amp; this.type.assetType === 'folder') {
  274. pathQuery = 'folder';
  275. }
  276. return !!(link &amp;&amp; link.shareUrl &amp;&amp; link.shareUrl.search(pathQuery + '=.my_folders') === -1);
  277. },
  278. isLinkShareable: function (link) {
  279. return this._isLinkEnabled() &amp;&amp; this._checkLinkShareable(link);
  280. },
  281. canSend: function () {
  282. return this._isSendEnabled() &amp;&amp; (!this.link ? true : this._checkLinkShareable(this.link));
  283. },
  284. /**
  285. * Render the view.
  286. * @instance
  287. */
  288. render: function () {
  289. // tell html2canvas to ignore our panel when capturing the image.
  290. this.$el.closest('.flyoutPane').attr('data-html2canvas-ignore', 'true');
  291. // 1. render a spinner
  292. ReactDOM.render(React.createElement(Toolkit.ProgressIndicator, {
  293. className: 'initialCollaborationSlideoutSpinner',
  294. id: 'initialCollaborationSlideoutSpinner',
  295. size: 'large',
  296. style: { left: '50%', top: '50%', position: 'absolute', transform: 'translate(-50%, -50%)' }
  297. }), this.$el.get(0));
  298. return new Promise(function (resolve, reject) {
  299. // 2. load modules
  300. require(['collaboration/canvaseditor/CanvasEditor', 'collaboration-ui/collaboration-ui.min', 'ckeditor'], function (CanvasEditor, CollaborationUI) {
  301. // 3. get language and version, connectors and more
  302. Promise.all([this.getConfig(), this.getConnectors(), this.canExportToPDF(), this.refreshLink(), this.includeImage()])
  303. .then(([config, connectors, canExport, link, showIncludeImage]) => {
  304. // 4. remove the spinner
  305. ReactDOM.unmountComponentAtNode(this.$el.get(0));
  306. // 5. create the stores
  307. this.createStores(
  308. CollaborationUI.ShareStore,
  309. CollaborationUI.ImageStore,
  310. CanvasEditor,
  311. connectors,
  312. config,
  313. canExport,
  314. link,
  315. showIncludeImage);
  316. // 6. render react
  317. this.renderReact(CollaborationUI.CollaborationPanel);
  318. // all done!
  319. resolve();
  320. })
  321. .catch(reject);
  322. }.bind(this));
  323. }.bind(this));
  324. },
  325. _isSendEnabled: function () {
  326. return this.features.includes(GlassUtil.feature.SEND);
  327. },
  328. _isLinkEnabled: function () {
  329. return this.features.includes(GlassUtil.feature.LINK);
  330. },
  331. _isEmbedEnabled: function () {
  332. return this.features.includes(GlassUtil.feature.EMBED);
  333. },
  334. _isEmailLinkEnabled: function () {
  335. return this.features.includes(GlassUtil.feature.EMAIL_LINK) &amp;&amp; this.glassContext.hasCapability('canIncludeLinkInEmail');
  336. },
  337. _isDirty: function() {
  338. return this.glassContext.currentAppView.isDirty();
  339. },
  340. createStores: function (ShareStore, ImageStore, CanvasEditor, connectors, config, canExport, link, showIncludeImage) {
  341. const assetStrings = this.shareController.getAssetStrings();
  342. const shareStore = ShareStore.create({
  343. isIE11: BrowserUtils.isIE11(),
  344. language: config.language,
  345. version: config.version,
  346. assetTitle: assetStrings.assetTitle,
  347. assetType: assetStrings.assetType,
  348. isDirty: this._isDirty(),
  349. objectType: this.objectType || null,
  350. objectId: this.objectId || null
  351. }, {
  352. CanvasEditor,
  353. glassContext: this.glassContext,
  354. $root: this.$el,
  355. isLinkShareable: this.isLinkShareable.bind(this),
  356. send: this.send.bind(this),
  357. generatePDF: this.generatePDF.bind(this),
  358. displayError: this.displayError.bind(this),
  359. instrument: this.instrument.bind(this),
  360. features: this.features,
  361. glassFeature: GlassUtil.feature,
  362. dateTimeUtils: DateTimeUtils
  363. });
  364. shareStore.enableSend(this._isSendEnabled());
  365. shareStore.enableLink(this._isLinkEnabled());
  366. shareStore.enableEmailLink(this._isEmailLinkEnabled());
  367. shareStore.enableEmbed(this._isEmbedEnabled());
  368. shareStore.enableExport(canExport);
  369. shareStore.setConnectors(connectors);
  370. shareStore.setLink(link);
  371. this.shareStore = shareStore;
  372. const imageStore = ImageStore.create({
  373. showIncludeImage: showIncludeImage,
  374. includeImage: showIncludeImage,
  375. image: '',
  376. imageLoaded: false,
  377. imageLabel: '',
  378. imageWidth: 0,
  379. imageHeight: 0
  380. }, {
  381. refreshImage: this.refreshImage.bind(this)
  382. });
  383. this.imageStore = imageStore;
  384. },
  385. createReactPanel: function (CollaborationPanel, options) {
  386. this._panel = ReactDOM.render(React.createElement(CollaborationPanel, options), this.reactContainer);
  387. },
  388. renderReact: function (CollaborationPanel) {
  389. const $container = $(`&lt;div class="${SHARE_VIEW_CLASS}" tabIndex="-1">&lt;/div>`).appendTo(this.$el);
  390. this.enableLooping($container);
  391. const options = {
  392. shareStore: this.shareStore,
  393. imageStore: this.imageStore,
  394. nls: StringResources.get,
  395. panel: 'main',
  396. cancel: this.close.bind(this),
  397. embedVisible: this.embedVisible,
  398. contentMenuShare: !!this.link
  399. };
  400. // render the view with options
  401. this.reactContainer = $container.get(0);
  402. this.createReactPanel(CollaborationPanel, options);
  403. this.$el.on('escapeaction', (event) => {
  404. event.preventDefault();
  405. event.stopPropagation();
  406. });
  407. // The slideout has 'transition' css in its root DOM element, and it relies on these events to calculate animation.
  408. // Stop propagation to avoid issues.
  409. var animEvents = 'transitionend webkitTransitionEnd oTransitionEnd';
  410. $(this.reactContainer).off(animEvents).on(animEvents, function (event) {
  411. event.stopPropagation();
  412. });
  413. },
  414. generatePDF: function (pageSize, includeFilter) {
  415. if (this.shareStore &amp;&amp; this.shareStore.canExport) {
  416. this.shareController.exportToPDF(this.glassContext, pageSize, includeFilter);
  417. }
  418. },
  419. instrument: function (action, shareType) {
  420. if (this._instrumentationService &amp;&amp; this._instrumentationService.enabled) {
  421. return this.shareController.getInstrumentation(this.glassContext)
  422. .then((event) => {
  423. event.action = action;
  424. event['custom.shareType'] = shareType;
  425. event.type = 'Shared Object';
  426. event.objectType = event.objectType || this.objectType;
  427. // If neither of those worked, try to get the type from glass.
  428. if (!event.objectType &amp;&amp; typeof this.glassContext.getCurrentContentView === 'function') {
  429. event.objectType = this.glassContext.getCurrentContentView().getType();
  430. }
  431. event.milestoneName = `${action}_${event.objectType}`;
  432. this._instrumentationService.track(event);
  433. });
  434. }
  435. return Promise.resolve();
  436. },
  437. getConnectors: function () {
  438. // don't need to load connectors if cannot send
  439. if (!this.canSend()) {
  440. return Promise.resolve([]);
  441. }
  442. return this.shareController.getConnectors()
  443. .catch(function (error) {
  444. if (this._logger) {
  445. this._logger.error('Error fetching connectors: ' + error);
  446. }
  447. this.glassContext.showToast(StringResources.get('error_retrieving_platforms'), {
  448. type: 'error'
  449. });
  450. throw error;
  451. }.bind(this));
  452. },
  453. /**
  454. * Sends the message.
  455. * @instance
  456. * @param {object} payload
  457. * @param {object} payload.connector
  458. * @param {object} payload.data
  459. * @returns {Promise}
  460. */
  461. send: function (payload) {
  462. return this.shareController.send(payload.connector, payload.data)
  463. .then((result) => {
  464. // success
  465. void (result);
  466. const message = (payload.type === 'email') ? StringResources.get('toast_success_email') : StringResources.get('toast_success', {
  467. connector: payload.connector.getLabel()
  468. });
  469. this.glassContext.showToast(message, { type: 'success' });
  470. }).then(() => this.close());
  471. },
  472. /**
  473. * Close the panel.
  474. * @instance
  475. * @returns {Promise}
  476. */
  477. close: function () {
  478. return this.shareController.close()
  479. .then(function () {
  480. return this.slideout.hide({
  481. force: true,
  482. hideOnly: this.slideout.hideOnly
  483. });
  484. }.bind(this))
  485. .then(function () {
  486. var launchPoint = this.getLaunchPoint();
  487. if (launchPoint) {
  488. $(launchPoint).focus();
  489. }
  490. }.bind(this));
  491. },
  492. /**
  493. * Display errors as toast message.
  494. * @instance
  495. * @param {object} error
  496. * @throws {error}
  497. */
  498. displayError: function (error) {
  499. var msg = error.message;
  500. if (error.connector) {
  501. const connectorType = error.connector.getType();
  502. var toastStringId = (connectorType === 'email') ? 'toast_failure_email' : 'toast_failure';
  503. var toastMessageData = {
  504. connector: error.connector.getLabel(),
  505. error: error.message
  506. };
  507. if (error.showContactAdmin) {
  508. toastStringId = (connectorType === 'email') ? 'toast_failure_detailed_email' : 'toast_failure_detailed';
  509. toastMessageData.contactAdmin = StringResources.get('message_contact_administrator');
  510. }
  511. msg = StringResources.get(toastStringId, toastMessageData);
  512. }
  513. this.glassContext.showToast(msg, {
  514. type: 'error'
  515. });
  516. throw error;
  517. },
  518. /**
  519. * Cleaning up events
  520. * @override
  521. */
  522. remove: function () {
  523. if (this._eventHandlers) {
  524. this._eventHandlers.forEach(function (handler) {
  525. if (handler) {
  526. handler.remove();
  527. }
  528. });
  529. }
  530. if (this.reactContainer) {
  531. ReactDOM.unmountComponentAtNode(this.reactContainer);
  532. }
  533. }
  534. });
  535. return ShareView;
  536. });
  537. </code></pre>
  538. </article>
  539. </section>
  540. </div>
  541. <nav>
  542. <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ConnectorBase.html">ConnectorBase</a></li><li><a href="Connectors.html">Connectors</a></li><li><a href="EmailClient.html">EmailClient</a></li><li><a href="EmailConnector.html">EmailConnector</a></li><li><a href="MSTeamsAuth.html">MSTeamsAuth</a></li><li><a href="MSTeamsClient.html">MSTeamsClient</a></li><li><a href="MSTeamsConnector.html">MSTeamsConnector</a></li><li><a href="ShareableItems.html">ShareableItems</a></li><li><a href="ShareController.html">ShareController</a></li><li><a href="ShareView.html">ShareView</a></li><li><a href="SlackAuth.html">SlackAuth</a></li><li><a href="SlackClient.html">SlackClient</a></li><li><a href="SlackConnector.html">SlackConnector</a></li></ul><h3>Interfaces</h3><ul><li><a href="ShareInterface.html">ShareInterface</a></li></ul>
  543. </nav>
  544. <br class="clear">
  545. <footer>
  546. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Wed May 25 2022 13:54:53 GMT+0000 (UTC)
  547. </footer>
  548. <script> prettyPrint(); </script>
  549. <script src="scripts/linenumber.js"> </script>
  550. </body>
  551. </html>