@instructure/canvas-rce 7.2.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/es/enhance-user-content/doc_previews.js +1 -14
  3. package/es/index.d.ts +1 -0
  4. package/es/index.js +2 -1
  5. package/es/rce/AlertMessageArea.d.ts +2 -2
  6. package/es/rce/AlertMessageArea.js +4 -6
  7. package/es/rce/RCEGlobals.d.ts +2 -0
  8. package/es/rce/RCEGlobals.js +1 -0
  9. package/es/rce/RCEVariants.js +3 -3
  10. package/es/rce/RCEWrapper.d.ts +14 -12
  11. package/es/rce/RCEWrapper.js +206 -199
  12. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.d.ts +2 -0
  13. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.js +45 -0
  14. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.d.ts +1 -0
  15. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.js +43 -0
  16. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.d.ts +1 -1
  17. package/es/rce/plugins/instructure_rce_external_tools/components/ExternalToolSelectionDialog/ExternalToolSelectionDialog.js +25 -25
  18. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.d.ts +1 -1
  19. package/es/rce/plugins/instructure_record/AudioOptionsTray/TrayController.js +2 -1
  20. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +1 -1
  21. package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +2 -1
  22. package/es/rce/plugins/instructure_record/mediaTranslations.js +1 -1
  23. package/es/rce/plugins/instructure_studio_media_options/plugin.js +109 -14
  24. package/es/rce/plugins/instructure_studio_media_options/studioToolbarIcons.d.ts +5 -0
  25. package/es/rce/plugins/instructure_studio_media_options/studioToolbarIcons.js +23 -0
  26. package/es/rce/plugins/instructure_wordcount_header/plugin.d.ts +1 -0
  27. package/es/rce/plugins/instructure_wordcount_header/plugin.js +75 -0
  28. package/es/rce/plugins/shared/ContentSelection.d.ts +1 -2
  29. package/es/rce/plugins/shared/ContentSelection.js +1 -18
  30. package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +9 -1
  31. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +94 -1
  32. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -1
  33. package/es/rce/plugins/shared/Upload/UploadFileModal.js +37 -4
  34. package/es/rce/plugins/shared/Upload/VideoUrlPanel.d.ts +15 -0
  35. package/es/rce/plugins/shared/Upload/VideoUrlPanel.js +51 -0
  36. package/es/rce/plugins/shared/Upload/videoValidationUtils.d.ts +7 -0
  37. package/es/rce/plugins/shared/Upload/videoValidationUtils.js +58 -0
  38. package/es/rce/plugins/shared/iframeUtils.d.ts +1 -0
  39. package/es/rce/plugins/shared/iframeUtils.js +37 -0
  40. package/es/rce/tinyRCE.js +2 -0
  41. package/es/sidebar/actions/upload.d.ts +1 -0
  42. package/es/sidebar/actions/upload.js +56 -0
  43. package/es/sidebar/containers/sidebarHandlers.d.ts +1 -0
  44. package/es/sidebar/containers/sidebarHandlers.js +2 -1
  45. package/es/translations/locales/ar.js +15 -6
  46. package/es/translations/locales/ca.js +15 -6
  47. package/es/translations/locales/cy.js +15 -6
  48. package/es/translations/locales/da-x-k12.js +15 -6
  49. package/es/translations/locales/da.js +15 -6
  50. package/es/translations/locales/de.js +15 -6
  51. package/es/translations/locales/el.js +6 -0
  52. package/es/translations/locales/en-AU-x-unimelb.js +15 -6
  53. package/es/translations/locales/en-GB-x-ukhe.js +15 -6
  54. package/es/translations/locales/en.js +30 -6
  55. package/es/translations/locales/en_AU.js +15 -6
  56. package/es/translations/locales/en_CA.js +15 -6
  57. package/es/translations/locales/en_CY.js +15 -6
  58. package/es/translations/locales/en_GB.js +15 -6
  59. package/es/translations/locales/es.js +15 -6
  60. package/es/translations/locales/es_ES.js +15 -6
  61. package/es/translations/locales/fa_IR.js +6 -3
  62. package/es/translations/locales/fi.js +15 -6
  63. package/es/translations/locales/fr.js +15 -6
  64. package/es/translations/locales/fr_CA.js +19 -10
  65. package/es/translations/locales/ga.js +15 -6
  66. package/es/translations/locales/he.js +6 -0
  67. package/es/translations/locales/hi.js +15 -6
  68. package/es/translations/locales/ht.js +15 -6
  69. package/es/translations/locales/hu.js +6 -6
  70. package/es/translations/locales/hy.js +6 -0
  71. package/es/translations/locales/id.js +15 -6
  72. package/es/translations/locales/is.js +21 -6
  73. package/es/translations/locales/it.js +15 -6
  74. package/es/translations/locales/ja.js +15 -6
  75. package/es/translations/locales/ko.js +6 -0
  76. package/es/translations/locales/mi.js +15 -6
  77. package/es/translations/locales/ms.js +15 -6
  78. package/es/translations/locales/nb-x-k12.js +15 -6
  79. package/es/translations/locales/nb.js +15 -6
  80. package/es/translations/locales/nl.js +15 -6
  81. package/es/translations/locales/nn.js +6 -6
  82. package/es/translations/locales/pl.js +15 -6
  83. package/es/translations/locales/pt.js +15 -6
  84. package/es/translations/locales/pt_BR.js +15 -6
  85. package/es/translations/locales/ru.js +15 -6
  86. package/es/translations/locales/sl.js +15 -6
  87. package/es/translations/locales/sv-x-k12.js +15 -6
  88. package/es/translations/locales/sv.js +15 -6
  89. package/es/translations/locales/th.js +15 -6
  90. package/es/translations/locales/tr.js +6 -3
  91. package/es/translations/locales/uk_UA.js +6 -3
  92. package/es/translations/locales/vi.js +15 -6
  93. package/es/translations/locales/zh-Hans.js +15 -6
  94. package/es/translations/locales/zh-Hant.js +15 -6
  95. package/es/translations/locales/zh.js +15 -6
  96. package/es/translations/locales/zh_HK.js +15 -6
  97. package/package.json +53 -53
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import PropTypes, { bool, shape } from 'prop-types';
20
+ import { findMediaPlayerIframe } from './iframeUtils';
20
21
 
21
22
  /**
22
23
  * Interface for content item's 'custom' field, specifically for what is expected to come from Studio
@@ -71,4 +72,96 @@ export function handleBeforeObjectSelected(e) {
71
72
  if (targetElement.getAttribute('data-mce-p-data-studio-resizable') === 'false') {
72
73
  targetElement.setAttribute('data-mce-resize', 'false');
73
74
  }
74
- }
75
+ }
76
+ export function findStudioLtiIframeFromSelection(selectedNode) {
77
+ let outerIframe = null;
78
+
79
+ // First, find the outer iframe
80
+ if (selectedNode.nodeName === 'IFRAME') {
81
+ outerIframe = selectedNode;
82
+ } else if (selectedNode.nodeType === Node.ELEMENT_NODE) {
83
+ // Look for iframe inside the selected element (the span)
84
+ outerIframe = selectedNode.querySelector('iframe');
85
+ }
86
+ if (!outerIframe) {
87
+ console.error('No outer iframe found');
88
+ return null;
89
+ }
90
+
91
+ // Now try to access the content document of the outer iframe
92
+ try {
93
+ const outerIframeDoc = outerIframe.contentDocument || outerIframe.contentWindow?.document;
94
+ if (!outerIframeDoc) {
95
+ return outerIframe; // Return outer iframe as fallback
96
+ }
97
+
98
+ // Search for nested iframe with data-lti-launch attribute
99
+ const nestedIframe = outerIframeDoc.querySelector('iframe[data-lti-launch="true"]');
100
+ if (nestedIframe) {
101
+ return nestedIframe;
102
+ } else {
103
+ // Try to find any iframe inside
104
+ const anyNestedIframe = outerIframeDoc.querySelector('iframe');
105
+ if (anyNestedIframe) {
106
+ return anyNestedIframe;
107
+ }
108
+ }
109
+ } catch (error) {
110
+ console.error('>> Cannot access outer iframe content (cross-origin):', error);
111
+ // Return the outer iframe as fallback since we can't access its contents
112
+ return outerIframe;
113
+ }
114
+ return outerIframe;
115
+ }
116
+ export const notifyStudioEmbedTypeChange = (editor, embedType) => {
117
+ const studioIframe = findStudioLtiIframeFromSelection(editor.selection.getNode());
118
+ if (studioIframe && studioIframe.contentWindow) {
119
+ studioIframe.contentWindow.postMessage({
120
+ subject: 'studio.embedTypeChanged',
121
+ embedType: embedType,
122
+ timestamp: Date.now()
123
+ }, '*');
124
+ }
125
+ };
126
+ export const updateStudioIframeDimensions = (editor, width, height, embedType) => {
127
+ const selectedNode = editor.selection.getNode();
128
+ const videoContainer = findMediaPlayerIframe(selectedNode);
129
+ if (videoContainer?.tagName !== 'IFRAME') {
130
+ return;
131
+ }
132
+ const tinymceIframeShim = videoContainer.parentElement;
133
+ if (!tinymceIframeShim) {
134
+ return;
135
+ }
136
+ editor.dom.setStyles(tinymceIframeShim, {
137
+ width: `${width}px`,
138
+ height: `${height}px`
139
+ });
140
+ editor.dom.setStyles(videoContainer, {
141
+ width: `${width}px`,
142
+ height: `${height}px`
143
+ });
144
+ const href = editor.dom.getAttrib(tinymceIframeShim, 'data-mce-p-src');
145
+ if (href && embedType) {
146
+ if (embedType) {
147
+ // Replace thumbnail_embed, learn_embed, or collaboration_embed with the new embed type
148
+ const updatedHref = href.replace(/(thumbnail_embed|learn_embed|collaboration_embed)/g, embedType);
149
+
150
+ // updating only mce-p-src as in we only want to update the real src whenever we step out of the editor or save it
151
+ editor.dom.setAttrib(tinymceIframeShim, 'data-mce-p-src', updatedHref);
152
+ editor.nodeChanged();
153
+ }
154
+ }
155
+ editor.fire('ObjectResized', {
156
+ // @ts-expect-error - needed for aligning tooltip with new iframe size
157
+ target: videoContainer,
158
+ width,
159
+ height
160
+ });
161
+ };
162
+ export const isValidEmbedType = embedType => {
163
+ return typeof embedType === 'string' && ['thumbnail_embed', 'learn_embed', 'collaboration_embed'].includes(embedType);
164
+ };
165
+ export const isValidDimension = value => {
166
+ return typeof value === 'number' && !isNaN(value) && isFinite(value) && value > 0;
167
+ };
@@ -238,7 +238,7 @@ export default function ComputerPanel({
238
238
  });
239
239
  },
240
240
  renderIcon: IconTrashLine,
241
- screenReaderLabel: formatMessage('Clear selected file: {filename}', {
241
+ screenReaderLabel: formatMessage('Remove {filename}', {
242
242
  filename
243
243
  })
244
244
  })), /*#__PURE__*/React.createElement(Flex.Item, {
@@ -31,9 +31,11 @@ import ImageOptionsForm from '../ImageOptionsForm';
31
31
  import UsageRightsSelectBox from './UsageRightsSelectBox';
32
32
  import { View } from '@instructure/ui-view';
33
33
  import { UploadCanvasPanelIds, CanvasPanelTitles } from '../canvasContentUtils';
34
+ import { validateVideoUrl } from './videoValidationUtils';
34
35
  const CanvasContentPanel = /*#__PURE__*/React.lazy(() => import('./CanvasContentPanel'));
35
36
  const ComputerPanel = /*#__PURE__*/React.lazy(() => import('./ComputerPanel'));
36
37
  const UrlPanel = /*#__PURE__*/React.lazy(() => import('./UrlPanel'));
38
+ const VideoUrlPanel = /*#__PURE__*/React.lazy(() => import('./VideoUrlPanel'));
37
39
  function shouldBeDisabled({
38
40
  fileUrl,
39
41
  theFile,
@@ -46,6 +48,7 @@ function shouldBeDisabled({
46
48
  case 'COMPUTER':
47
49
  return !theFile || theFile.error;
48
50
  case 'URL':
51
+ case 'VIDEO_URL':
49
52
  return !fileUrl;
50
53
  default:
51
54
  if (UploadCanvasPanelIds.includes(selectedPanel)) return !fileUrl;
@@ -133,8 +136,17 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef(({
133
136
  if (submitDisabled || uploading) {
134
137
  return false;
135
138
  }
139
+ let finalFileUrl = fileUrl;
140
+ if (selectedPanel === 'VIDEO_URL' && finalFileUrl) {
141
+ const validation = validateVideoUrl(finalFileUrl);
142
+ if (!validation.isValid) {
143
+ setError('Invalid video URL');
144
+ return false;
145
+ }
146
+ finalFileUrl = validation.embedUrl;
147
+ }
136
148
  onSubmit(editor, accept, selectedPanel, {
137
- fileUrl,
149
+ fileUrl: finalFileUrl,
138
150
  theFile,
139
151
  imageOptions: {
140
152
  altText,
@@ -155,7 +167,7 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef(({
155
167
  }), /*#__PURE__*/React.createElement(Heading, null, label)), /*#__PURE__*/React.createElement(Modal.Body, {
156
168
  ref: ref
157
169
  }, /*#__PURE__*/React.createElement(Tabs, {
158
- onRequestTabChange: (event, {
170
+ onRequestTabChange: (_event, {
159
171
  index
160
172
  }) => handleRequestTabChange(index)
161
173
  }, panels.map(panel => {
@@ -197,7 +209,28 @@ const UploadFileModal = /*#__PURE__*/React.forwardRef(({
197
209
  })
198
210
  }, /*#__PURE__*/React.createElement(UrlPanel, {
199
211
  fileUrl: fileUrl,
200
- setFileUrl: setFileUrl
212
+ setFileUrl: setFileUrl,
213
+ urlHasError: !!error
214
+ })));
215
+ case 'VIDEO_URL':
216
+ return /*#__PURE__*/React.createElement(Tabs.Panel, {
217
+ key: panel,
218
+ renderTitle: function () {
219
+ return formatMessage('Video URL');
220
+ },
221
+ isSelected: selectedPanel === 'VIDEO_URL'
222
+ }, /*#__PURE__*/React.createElement(Suspense, {
223
+ fallback: /*#__PURE__*/React.createElement(Spinner, {
224
+ renderTitle: formatMessage('Loading'),
225
+ size: "large"
226
+ })
227
+ }, /*#__PURE__*/React.createElement(VideoUrlPanel, {
228
+ fileUrl: fileUrl,
229
+ setFileUrl: url => {
230
+ setError(null);
231
+ setFileUrl(url);
232
+ },
233
+ urlHasError: !!error
201
234
  })));
202
235
  default:
203
236
  if (UploadCanvasPanelIds.includes(panel)) {
@@ -274,7 +307,7 @@ UploadFileModal.propTypes = {
274
307
  canvasOrigin: string,
275
308
  onSubmit: func,
276
309
  onDismiss: func.isRequired,
277
- panels: arrayOf(oneOf(['COMPUTER', 'URL', ...UploadCanvasPanelIds])),
310
+ panels: arrayOf(oneOf(['COMPUTER', 'URL', 'VIDEO_URL', ...UploadCanvasPanelIds])),
278
311
  label: string.isRequired,
279
312
  accept: oneOfType([arrayOf(string), string]),
280
313
  modalBodyWidth: number,
@@ -0,0 +1,15 @@
1
+ declare function VideoUrlPanel({ fileUrl, setFileUrl, urlHasError }: {
2
+ fileUrl: any;
3
+ setFileUrl: any;
4
+ urlHasError: any;
5
+ }): React.JSX.Element;
6
+ declare namespace VideoUrlPanel {
7
+ namespace propTypes {
8
+ export let fileUrl: import("prop-types").Validator<string>;
9
+ export let setFileUrl: import("prop-types").Validator<(...args: any[]) => any>;
10
+ export { bool as urlHasError };
11
+ }
12
+ }
13
+ export default VideoUrlPanel;
14
+ import React from 'react';
15
+ import { bool } from 'prop-types';
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright (C) 2025 - present Instructure, Inc.
3
+ *
4
+ * This file is part of Canvas.
5
+ *
6
+ * Canvas is free software: you can redistribute it and/or modify it under
7
+ * the terms of the GNU Affero General Public License as published by the Free
8
+ * Software Foundation, version 3 of the License.
9
+ *
10
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ * details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License along
16
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ import React from 'react';
20
+ import { string, func, bool } from 'prop-types';
21
+ import { TextInput } from '@instructure/ui-text-input';
22
+ import formatMessage from '../../../../format-message';
23
+ export default function VideoUrlPanel({
24
+ fileUrl,
25
+ setFileUrl,
26
+ urlHasError
27
+ }) {
28
+ const handleChange = (_e, val) => {
29
+ setFileUrl(val);
30
+ };
31
+ const getErrorMessage = () => {
32
+ if (!urlHasError) return [];
33
+ return [{
34
+ text: formatMessage('Please enter a valid video URL from a supported platform.'),
35
+ type: 'newError'
36
+ }];
37
+ };
38
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextInput, {
39
+ name: "video-url",
40
+ renderLabel: formatMessage('YouTube embed URL'),
41
+ type: "text",
42
+ value: fileUrl,
43
+ onChange: handleChange,
44
+ messages: getErrorMessage()
45
+ }));
46
+ }
47
+ VideoUrlPanel.propTypes = {
48
+ fileUrl: string.isRequired,
49
+ setFileUrl: func.isRequired,
50
+ urlHasError: bool
51
+ };
@@ -0,0 +1,7 @@
1
+ export function validateVideoUrl(input: any): {
2
+ isValid: boolean;
3
+ embedUrl: null;
4
+ } | {
5
+ isValid: boolean;
6
+ embedUrl: string;
7
+ };
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright (C) 2020 - present Instructure, Inc.
3
+ *
4
+ * This file is part of Canvas.
5
+ *
6
+ * Canvas is free software: you can redistribute it and/or modify it under
7
+ * the terms of the GNU Affero General Public License as published by the Free
8
+ * Software Foundation, version 3 of the License.
9
+ *
10
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ * details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License along
16
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ function validateAndExtractYouTubeUrl(input) {
20
+ if (!input || typeof input !== 'string') {
21
+ return {
22
+ isValid: false,
23
+ embedUrl: null
24
+ };
25
+ }
26
+ const trimmedInput = input.trim();
27
+ const patterns = [/^(?:https?:\/\/)?(?:www\.|m\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]+)(?:[&?][^\s]*)?$/, /^(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([a-zA-Z0-9_-]+)(?:[?][^\s]*)?$/, /<iframe[^>]*src=["'](?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([a-zA-Z0-9_-]+)[^"']*["'][^>]*>/];
28
+ for (const pattern of patterns) {
29
+ const match = trimmedInput.match(pattern);
30
+ if (match && match[1]) {
31
+ const videoId = match[1];
32
+ return {
33
+ isValid: true,
34
+ embedUrl: `https://www.youtube.com/embed/${videoId}`
35
+ };
36
+ }
37
+ }
38
+ return {
39
+ isValid: false,
40
+ embedUrl: null
41
+ };
42
+ }
43
+ export function validateVideoUrl(input) {
44
+ if (!input || typeof input !== 'string') {
45
+ return {
46
+ isValid: false,
47
+ embedUrl: null
48
+ };
49
+ }
50
+ const youTubeRegexp = /(?:https?:\/\/)?(?:www\.|m\.)?(?:youtube\.com|youtu\.be)/i;
51
+ if (youTubeRegexp.test(input)) {
52
+ return validateAndExtractYouTubeUrl(input);
53
+ }
54
+ return {
55
+ isValid: false,
56
+ embedUrl: null
57
+ };
58
+ }
@@ -0,0 +1 @@
1
+ export declare function findMediaPlayerIframe(elem: Element | null): Element | null;
@@ -0,0 +1,37 @@
1
+ /*
2
+ * Copyright (C) 2025 - present Instructure, Inc.
3
+ *
4
+ * This file is part of Canvas.
5
+ *
6
+ * Canvas is free software: you can redistribute it and/or modify it under
7
+ * the terms of the GNU Affero General Public License as published by the Free
8
+ * Software Foundation, version 3 of the License.
9
+ *
10
+ * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
11
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
+ * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
+ * details.
14
+ *
15
+ * You should have received a copy of the GNU Affero General Public License along
16
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ export function findMediaPlayerIframe(elem) {
20
+ if (!elem) return null;
21
+ if (elem.tagName === 'IFRAME') {
22
+ // we have the iframe
23
+ return elem;
24
+ }
25
+ if (elem.firstElementChild?.tagName === 'IFRAME') {
26
+ // we have the shim tinymce puts around the iframe
27
+ return elem.firstElementChild;
28
+ }
29
+ if (elem.classList.contains('mce-shim')) {
30
+ // tinymce puts a <span class='mce-shin'> after the iframe (since v5, I think)
31
+ const prevSibling = elem.previousSibling;
32
+ if (prevSibling && 'tagName' in prevSibling && prevSibling.tagName === 'IFRAME') {
33
+ return prevSibling;
34
+ }
35
+ }
36
+ return null;
37
+ }
package/es/rce/tinyRCE.js CHANGED
@@ -55,6 +55,8 @@ import './plugins/instructure_html_view/plugin';
55
55
  import './plugins/instructure_media_embed/plugin';
56
56
  import './plugins/instructure_icon_maker/plugin';
57
57
  import './plugins/instructure_wordcount/plugin';
58
+ import './plugins/instructure_wordcount_header/plugin';
59
+ import './plugins/instructure_keyboard_shortcuts_header/plugin';
58
60
  import './plugins/instructure_paste/plugin';
59
61
  import './plugins/instructure_fullscreen/plugin';
60
62
  import './plugins/instructure_studio_media_options/plugin';
@@ -91,6 +91,7 @@ export function fetchFolders(bookmark: any): (dispatch: any, getState: any) => a
91
91
  export function mediaUploadComplete(error: any, uploadData: any): (dispatch: any, _getState: any) => void;
92
92
  export function createMediaServerSession(): (dispatch: any, getState: any) => any;
93
93
  export function uploadToIconMakerFolder(svg: any, uploadSettings?: {}): (_dispatch: any, getState: any) => any;
94
+ export function uploadToMediaFolderWithoutEditor(fileMetaProps: any): (_: any, getState: any) => any;
94
95
  export function uploadToMediaFolder(tabContext: any, fileMetaProps: any): (dispatch: any, getState: any) => any;
95
96
  export function setUsageRights(source: any, fileMetaProps: any, results: any): any;
96
97
  export function getFileUrlIfMissing(source: any, results: any): any;
@@ -330,6 +330,62 @@ export function uploadToIconMakerFolder(svg, uploadSettings = {}) {
330
330
  });
331
331
  };
332
332
  }
333
+ export function uploadToMediaFolderWithoutEditor(fileMetaProps) {
334
+ return (_, getState) => {
335
+ const {
336
+ source,
337
+ jwt,
338
+ host,
339
+ contextId,
340
+ contextType
341
+ } = getState();
342
+ return source.fetchMediaFolder({
343
+ jwt,
344
+ host,
345
+ contextId,
346
+ contextType
347
+ }).then(async ({
348
+ folders
349
+ }) => {
350
+ fileMetaProps.parentFolderId = folders[0].id;
351
+ if (fileMetaProps.domObject) {
352
+ delete fileMetaProps.domObject.preview; // don't need this anymore
353
+ }
354
+ const getCategory = async fileProps => {
355
+ const categoryObject = await CategoryProcessor.process(fileProps.domObject);
356
+ return categoryObject?.category;
357
+ };
358
+ const category = await getCategory(fileMetaProps);
359
+ return source.preflightUpload(fileMetaProps, {
360
+ jwt,
361
+ host,
362
+ contextId,
363
+ contextType,
364
+ category
365
+ }).then(results => {
366
+ return source.uploadFRD(fileMetaProps.domObject, results);
367
+ }).then(results => {
368
+ return setUsageRights(source, fileMetaProps, results);
369
+ }).then(results => {
370
+ return getFileUrlIfMissing(source, results);
371
+ }).then(results => {
372
+ return fixupFileUrl(contextType, contextId, results, source.canvasOrigin);
373
+ }).then(results => {
374
+ return setAltText(fileMetaProps.altText, results);
375
+ }).then(results => {
376
+ if (fileMetaProps.isDecorativeImage) {
377
+ results.isDecorativeImage = fileMetaProps.isDecorativeImage;
378
+ }
379
+ if (fileMetaProps.displayAs) {
380
+ results.displayAs = fileMetaProps.displayAs;
381
+ }
382
+ return results;
383
+ });
384
+ }).catch(e => {
385
+ console.error('Upload to the media folder failed.', e);
386
+ });
387
+ };
388
+ }
333
389
  export function uploadToMediaFolder(tabContext, fileMetaProps) {
334
390
  return (dispatch, getState) => {
335
391
  const editorComponent = bridge.activeEditor();
@@ -19,6 +19,7 @@ export default function propsFromDispatch(dispatch: any): {
19
19
  toggleNewPageForm: () => any;
20
20
  startIconMakerUpload: (fileMetaProps: any, uploadSettings: any) => any;
21
21
  startMediaUpload: (tabContext: any, fileMetaProps: any) => any;
22
+ startMediaUploadInStandaloneMode: (fileMetaProps: any) => any;
22
23
  createMediaServerSession: () => any;
23
24
  mediaUploadComplete: (error: any, uploadData: any) => any;
24
25
  fetchInitialDocs: () => any;
@@ -21,7 +21,7 @@ import { fetchInitialPage, fetchNextPage } from '../actions/data';
21
21
  import { fetchInitialImages, fetchNextImages } from '../actions/images';
22
22
  import { createMediaServerSession, fetchFolders, openOrCloseUploadForm,
23
23
  // saveMediaRecording,
24
- mediaUploadComplete, uploadPreflight, uploadToIconMakerFolder, uploadToMediaFolder } from '../actions/upload';
24
+ mediaUploadComplete, uploadPreflight, uploadToIconMakerFolder, uploadToMediaFolder, uploadToMediaFolderWithoutEditor } from '../actions/upload';
25
25
  import { searchFlickr, openOrCloseFlickrForm } from '../actions/flickr';
26
26
  import { toggle as toggleFolder } from '../actions/files';
27
27
  import { openOrCloseNewPageForm } from '../actions/links';
@@ -53,6 +53,7 @@ export default function propsFromDispatch(dispatch) {
53
53
  toggleNewPageForm: () => dispatch(openOrCloseNewPageForm()),
54
54
  startIconMakerUpload: (fileMetaProps, uploadSettings) => dispatch(uploadToIconMakerFolder(fileMetaProps, uploadSettings)),
55
55
  startMediaUpload: (tabContext, fileMetaProps) => dispatch(uploadToMediaFolder(tabContext, fileMetaProps)),
56
+ startMediaUploadInStandaloneMode: fileMetaProps => dispatch(uploadToMediaFolderWithoutEditor(fileMetaProps)),
56
57
  createMediaServerSession: () => dispatch(createMediaServerSession()),
57
58
  // saveMediaRecording: (file, editor, dismiss) => dispatch(saveMediaRecording(file, editor, dismiss)),
58
59
  mediaUploadComplete: (error, uploadData) => dispatch(mediaUploadComplete(error, uploadData)),
@@ -301,12 +301,6 @@ const locale = {
301
301
  "clear_image_3213fe62": {
302
302
  "message": "مسح الصورة"
303
303
  },
304
- "clear_selected_file_82388e50": {
305
- "message": "مسح الملف المحدد"
306
- },
307
- "clear_selected_file_filename_2fe8a58e": {
308
- "message": "مسح الملف المحدد: { filename }"
309
- },
310
304
  "click_or_shift_click_for_the_html_editor_25d70bb4": {
311
305
  "message": "انقر أو نقرة + shift للوصول إلى محرر html."
312
306
  },
@@ -1711,6 +1705,9 @@ const locale = {
1711
1705
  "please_enter_a_file_name_f159edc1": {
1712
1706
  "message": "يرجى إدخال اسم الملف"
1713
1707
  },
1708
+ "please_enter_a_valid_video_url_from_a_supported_pl_30dc0596": {
1709
+ "message": "يُرجى إدخال عنوان URL صالح للفيديو من منصة مدعومة."
1710
+ },
1714
1711
  "please_select_a_file_of_a_supported_type_1fc578f2": {
1715
1712
  "message": "يرجى تحديد ملف بنوع مدعوم"
1716
1713
  },
@@ -1828,6 +1825,12 @@ const locale = {
1828
1825
  "religion_icon_246e0be1": {
1829
1826
  "message": "أيقونة الدين"
1830
1827
  },
1828
+ "remove_f47dc62a": {
1829
+ "message": "إزالة"
1830
+ },
1831
+ "remove_filename_3ea029f6": {
1832
+ "message": "إزالة { filename }"
1833
+ },
1831
1834
  "remove_heading_style_5fdc8855": {
1832
1835
  "message": "إزالة نمط العنوان"
1833
1836
  },
@@ -2542,6 +2545,9 @@ const locale = {
2542
2545
  "video_player_for_title_ffd9fbc4": {
2543
2546
  "message": "مشغل الفيديو لـ { title }"
2544
2547
  },
2548
+ "video_url_889d3263": {
2549
+ "message": "عنوان URL الفيديو"
2550
+ },
2545
2551
  "view_all_e13bf0a6": {
2546
2552
  "message": "عرض الكل"
2547
2553
  },
@@ -2656,6 +2662,9 @@ const locale = {
2656
2662
  "your_webcam_may_already_be_in_use_6cd64c25": {
2657
2663
  "message": "قد تكون كاميرا الويب الخاصة بك قيد الاستخدام بالفعل."
2658
2664
  },
2665
+ "youtube_embed_url_5c1018d4": {
2666
+ "message": "عنوان URL المضمن لـ YouTube"
2667
+ },
2659
2668
  "zeta_5ef24f0e": {
2660
2669
  "message": "Zeta"
2661
2670
  },
@@ -301,12 +301,6 @@ const locale = {
301
301
  "clear_image_3213fe62": {
302
302
  "message": "Esborra la imatge"
303
303
  },
304
- "clear_selected_file_82388e50": {
305
- "message": "Esborra el fitxer seleccionat"
306
- },
307
- "clear_selected_file_filename_2fe8a58e": {
308
- "message": "Esborra el fitxer seleccionat: { filename }"
309
- },
310
304
  "click_or_shift_click_for_the_html_editor_25d70bb4": {
311
305
  "message": "Feu clic aquí o feu-hi clic mentre premeu la tecla Maj per obrir l’editor d’HTML."
312
306
  },
@@ -1711,6 +1705,9 @@ const locale = {
1711
1705
  "please_enter_a_file_name_f159edc1": {
1712
1706
  "message": "Introduïu un nom de fitxer"
1713
1707
  },
1708
+ "please_enter_a_valid_video_url_from_a_supported_pl_30dc0596": {
1709
+ "message": "Introduïu un URL de vídeo vàlid des d’una plataforma admesa."
1710
+ },
1714
1711
  "please_select_a_file_of_a_supported_type_1fc578f2": {
1715
1712
  "message": "Seleccioneu un fitxer d’un tipus admès"
1716
1713
  },
@@ -1828,6 +1825,12 @@ const locale = {
1828
1825
  "religion_icon_246e0be1": {
1829
1826
  "message": "Icona de religió"
1830
1827
  },
1828
+ "remove_f47dc62a": {
1829
+ "message": "Suprimeix-ho"
1830
+ },
1831
+ "remove_filename_3ea029f6": {
1832
+ "message": "Suprimiu { filename }"
1833
+ },
1831
1834
  "remove_heading_style_5fdc8855": {
1832
1835
  "message": "Suprimeix l''estil de la capçalera"
1833
1836
  },
@@ -2542,6 +2545,9 @@ const locale = {
2542
2545
  "video_player_for_title_ffd9fbc4": {
2543
2546
  "message": "Reproductor de vídeo per a { title }"
2544
2547
  },
2548
+ "video_url_889d3263": {
2549
+ "message": "URL de vídeo"
2550
+ },
2545
2551
  "view_all_e13bf0a6": {
2546
2552
  "message": "Mostra-ho tot"
2547
2553
  },
@@ -2656,6 +2662,9 @@ const locale = {
2656
2662
  "your_webcam_may_already_be_in_use_6cd64c25": {
2657
2663
  "message": "És possible que la càmera web ja s''estigui utilitzant."
2658
2664
  },
2665
+ "youtube_embed_url_5c1018d4": {
2666
+ "message": "URL integrat de YouTube"
2667
+ },
2659
2668
  "zeta_5ef24f0e": {
2660
2669
  "message": "Zeta"
2661
2670
  },
@@ -301,12 +301,6 @@ const locale = {
301
301
  "clear_image_3213fe62": {
302
302
  "message": "Clirio’r ddelwedd"
303
303
  },
304
- "clear_selected_file_82388e50": {
305
- "message": "Clirio''r ffeil dan sylw"
306
- },
307
- "clear_selected_file_filename_2fe8a58e": {
308
- "message": "Clirio''r ffeil dan sylw: { filename }"
309
- },
310
304
  "click_or_shift_click_for_the_html_editor_25d70bb4": {
311
305
  "message": "Cliciwch neu pwyswch shifft a chlicio ar gyfer y golygydd html."
312
306
  },
@@ -1711,6 +1705,9 @@ const locale = {
1711
1705
  "please_enter_a_file_name_f159edc1": {
1712
1706
  "message": "Rhowch enw ffeil"
1713
1707
  },
1708
+ "please_enter_a_valid_video_url_from_a_supported_pl_30dc0596": {
1709
+ "message": "Rhowch URL fideo dilys o blatfform a gefnogir."
1710
+ },
1714
1711
  "please_select_a_file_of_a_supported_type_1fc578f2": {
1715
1712
  "message": "Dewiswch ffeil o fath sy''n cael ei gefnogi"
1716
1713
  },
@@ -1828,6 +1825,12 @@ const locale = {
1828
1825
  "religion_icon_246e0be1": {
1829
1826
  "message": "Eicon Crefydd"
1830
1827
  },
1828
+ "remove_f47dc62a": {
1829
+ "message": "Tynnu"
1830
+ },
1831
+ "remove_filename_3ea029f6": {
1832
+ "message": "Tynnu { filename }"
1833
+ },
1831
1834
  "remove_heading_style_5fdc8855": {
1832
1835
  "message": "Tynnu arddull y pennawd"
1833
1836
  },
@@ -2542,6 +2545,9 @@ const locale = {
2542
2545
  "video_player_for_title_ffd9fbc4": {
2543
2546
  "message": "Chwaraewr fideo ar gyfer { title }"
2544
2547
  },
2548
+ "video_url_889d3263": {
2549
+ "message": "URL Fideo"
2550
+ },
2545
2551
  "view_all_e13bf0a6": {
2546
2552
  "message": "Gweld Pob Un"
2547
2553
  },
@@ -2656,6 +2662,9 @@ const locale = {
2656
2662
  "your_webcam_may_already_be_in_use_6cd64c25": {
2657
2663
  "message": "Efallai bod eich gwe-gamera yn cael ei ddefnyddio’n barod."
2658
2664
  },
2665
+ "youtube_embed_url_5c1018d4": {
2666
+ "message": "URL plannu YouTube"
2667
+ },
2659
2668
  "zeta_5ef24f0e": {
2660
2669
  "message": "Zeta"
2661
2670
  },