@instructure/canvas-rce 7.2.0 → 7.3.1

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 (98) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/es/enhance-user-content/doc_previews.js +6 -14
  3. package/es/enhance-user-content/enhance_user_content.js +1 -1
  4. package/es/enhance-user-content/instructure_helper.js +1 -0
  5. package/es/index.d.ts +1 -0
  6. package/es/index.js +2 -1
  7. package/es/rce/AlertMessageArea.js +1 -3
  8. package/es/rce/KeyboardShortcutModal.js +1 -1
  9. package/es/rce/RCEGlobals.d.ts +2 -0
  10. package/es/rce/RCEGlobals.js +1 -0
  11. package/es/rce/RCEVariants.js +3 -3
  12. package/es/rce/RCEWrapper.d.ts +3 -1
  13. package/es/rce/RCEWrapper.js +43 -11
  14. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.d.ts +2 -0
  15. package/es/rce/plugins/instructure_keyboard_shortcuts_header/clickCallback.js +45 -0
  16. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.d.ts +1 -0
  17. package/es/rce/plugins/instructure_keyboard_shortcuts_header/plugin.js +43 -0
  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 +111 -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/components/WordCountModal.js +1 -0
  27. package/es/rce/plugins/instructure_wordcount_header/plugin.d.ts +1 -0
  28. package/es/rce/plugins/instructure_wordcount_header/plugin.js +75 -0
  29. package/es/rce/plugins/shared/ContentSelection.d.ts +1 -2
  30. package/es/rce/plugins/shared/ContentSelection.js +1 -18
  31. package/es/rce/plugins/shared/StudioLtiSupportUtils.d.ts +10 -1
  32. package/es/rce/plugins/shared/StudioLtiSupportUtils.js +110 -1
  33. package/es/rce/plugins/shared/Upload/ComputerPanel.js +1 -1
  34. package/es/rce/plugins/shared/Upload/UploadFileModal.js +37 -4
  35. package/es/rce/plugins/shared/Upload/VideoUrlPanel.d.ts +15 -0
  36. package/es/rce/plugins/shared/Upload/VideoUrlPanel.js +51 -0
  37. package/es/rce/plugins/shared/Upload/videoValidationUtils.d.ts +7 -0
  38. package/es/rce/plugins/shared/Upload/videoValidationUtils.js +58 -0
  39. package/es/rce/plugins/shared/iframeUtils.d.ts +1 -0
  40. package/es/rce/plugins/shared/iframeUtils.js +37 -0
  41. package/es/rce/tinyRCE.js +2 -0
  42. package/es/sidebar/actions/upload.d.ts +1 -0
  43. package/es/sidebar/actions/upload.js +56 -0
  44. package/es/sidebar/containers/sidebarHandlers.d.ts +1 -0
  45. package/es/sidebar/containers/sidebarHandlers.js +2 -1
  46. package/es/translations/locales/ar.js +30 -6
  47. package/es/translations/locales/ca.js +30 -6
  48. package/es/translations/locales/cy.js +30 -6
  49. package/es/translations/locales/da-x-k12.js +30 -6
  50. package/es/translations/locales/da.js +30 -6
  51. package/es/translations/locales/de.js +30 -6
  52. package/es/translations/locales/el.js +6 -0
  53. package/es/translations/locales/en-AU-x-unimelb.js +30 -6
  54. package/es/translations/locales/en-GB-x-ukhe.js +30 -6
  55. package/es/translations/locales/en.js +36 -6
  56. package/es/translations/locales/en_AU.js +30 -6
  57. package/es/translations/locales/en_CA.js +30 -6
  58. package/es/translations/locales/en_CY.js +30 -6
  59. package/es/translations/locales/en_GB.js +30 -6
  60. package/es/translations/locales/es.js +30 -6
  61. package/es/translations/locales/es_ES.js +30 -6
  62. package/es/translations/locales/fa_IR.js +6 -3
  63. package/es/translations/locales/fi.js +30 -6
  64. package/es/translations/locales/fr.js +30 -6
  65. package/es/translations/locales/fr_CA.js +34 -10
  66. package/es/translations/locales/ga.js +30 -6
  67. package/es/translations/locales/he.js +6 -0
  68. package/es/translations/locales/hi.js +30 -6
  69. package/es/translations/locales/ht.js +30 -6
  70. package/es/translations/locales/hu.js +6 -6
  71. package/es/translations/locales/hy.js +6 -0
  72. package/es/translations/locales/id.js +30 -6
  73. package/es/translations/locales/is.js +36 -6
  74. package/es/translations/locales/it.js +30 -6
  75. package/es/translations/locales/ja.js +30 -6
  76. package/es/translations/locales/ko.js +6 -0
  77. package/es/translations/locales/mi.js +30 -6
  78. package/es/translations/locales/ms.js +30 -6
  79. package/es/translations/locales/nb-x-k12.js +30 -6
  80. package/es/translations/locales/nb.js +30 -6
  81. package/es/translations/locales/nl.js +30 -6
  82. package/es/translations/locales/nn.js +6 -6
  83. package/es/translations/locales/pl.js +30 -6
  84. package/es/translations/locales/pt.js +30 -6
  85. package/es/translations/locales/pt_BR.js +30 -6
  86. package/es/translations/locales/ru.js +30 -6
  87. package/es/translations/locales/sl.js +30 -6
  88. package/es/translations/locales/sv-x-k12.js +30 -6
  89. package/es/translations/locales/sv.js +30 -6
  90. package/es/translations/locales/th.js +30 -6
  91. package/es/translations/locales/tr.js +6 -3
  92. package/es/translations/locales/uk_UA.js +6 -3
  93. package/es/translations/locales/vi.js +30 -6
  94. package/es/translations/locales/zh-Hans.js +30 -6
  95. package/es/translations/locales/zh-Hant.js +30 -6
  96. package/es/translations/locales/zh.js +30 -6
  97. package/es/translations/locales/zh_HK.js +30 -6
  98. package/package.json +53 -53
@@ -0,0 +1,5 @@
1
+ export declare const thumbnailViewIcon = "<svg width=\"24\" height=\"17\" viewBox=\"0 0 24 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1209_167295)\"><path d=\"M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z\" fill=\"currentColor\"/><path d=\"M21.7804 14.7887H2.21973V2.21143H21.7804V14.7887ZM9.84978 11.4716L14.5665 8.63826L9.84978 5.80492V11.4716Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1209_167295\"><rect width=\"24\" height=\"17\" fill=\"white\"/></clipPath></defs></svg>";
2
+ export declare const learnViewIcon = "<svg width=\"31\" height=\"22\" viewBox=\"0 0 31 22\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_93_30328)\"><path d=\"M31 22H0V0H31V22ZM1.43353 1.43089V20.5691H29.5665V1.43089H1.43353Z\" fill=\"currentColor\"/><path d=\"M27.9537 3.04102H19.7109V18.9597H27.9537V3.04102Z\" fill=\"currentColor\"/><path d=\"M18.2781 18.9597H3.04688V3.04102H18.2781V18.9597ZM8.02537 14.7989L14.3165 11.0907L8.02537 7.38244V14.7989Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_93_30328\"><rect width=\"31\" height=\"22\" fill=\"white\"/></clipPath></defs></svg>";
3
+ export declare const collabViewIcon = "<svg width=\"24\" height=\"17\" viewBox=\"0 0 24 17\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1209_167302)\"><path d=\"M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z\" fill=\"currentColor\"/><path d=\"M21.6416 2.34961H17.3411V8.15449H21.6416V2.34961Z\" fill=\"currentColor\"/><path d=\"M16.2312 8.15449H2.3584V2.34961H16.2312V8.15449ZM8.60117 7.18701L11.7919 5.39026L8.60117 3.59351V7.18701Z\" fill=\"currentColor\"/><path d=\"M21.6415 12.5771H4.99414V13.4064H21.6415V12.5771Z\" fill=\"currentColor\"/><path d=\"M21.6415 13.8213H4.99414V14.6506H21.6415V13.8213Z\" fill=\"currentColor\"/><path d=\"M3.39886 14.6503C3.97349 14.6503 4.43932 14.1862 4.43932 13.6137C4.43932 13.0412 3.97349 12.5771 3.39886 12.5771C2.82423 12.5771 2.3584 13.0412 2.3584 13.6137C2.3584 14.1862 2.82423 14.6503 3.39886 14.6503Z\" fill=\"currentColor\"/><path d=\"M21.6415 9.39844H4.99414V10.2277H21.6415V9.39844Z\" fill=\"currentColor\"/><path d=\"M21.6415 10.6421H4.99414V11.4714H21.6415V10.6421Z\" fill=\"currentColor\"/><path d=\"M3.39886 11.4716C3.97349 11.4716 4.43932 11.0075 4.43932 10.435C4.43932 9.86253 3.97349 9.39844 3.39886 9.39844C2.82423 9.39844 2.3584 9.86253 2.3584 10.435C2.3584 11.0075 2.82423 11.4716 3.39886 11.4716Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1209_167302\"><rect width=\"24\" height=\"17\" fill=\"white\"/></clipPath></defs></svg>";
4
+ export declare const optionsIcon = "<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#clip0_1217_47603)\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.46326 3.85039H0V2.65039H2.46326C2.7388 1.36449 3.88181 0.400391 5.24999 0.400391C6.61818 0.400391 7.76119 1.36449 8.03673 2.65039H18V3.85039H8.03673C7.76119 5.13629 6.61818 6.10039 5.24999 6.10039C3.88181 6.10039 2.7388 5.13629 2.46326 3.85039ZM3.59999 3.25039C3.59999 2.33912 4.33872 1.60039 5.24999 1.60039C6.16126 1.60039 6.89999 2.33912 6.89999 3.25039C6.89999 4.16166 6.16126 4.90039 5.24999 4.90039C4.33872 4.90039 3.59999 4.16166 3.59999 3.25039Z\" fill=\"currentColor\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M0 8.40039H9.46326C9.7388 7.11449 10.8818 6.15039 12.25 6.15039C13.6182 6.15039 14.7612 7.11449 15.0367 8.40039H18V9.60039H15.0367C14.7612 10.8863 13.6182 11.8504 12.25 11.8504C10.8818 11.8504 9.7388 10.8863 9.46326 9.60039H0V8.40039ZM12.25 7.35039C11.3387 7.35039 10.6 8.08912 10.6 9.00039C10.6 9.91166 11.3387 10.6504 12.25 10.6504C13.1613 10.6504 13.9 9.91166 13.9 9.00039C13.9 8.08912 13.1613 7.35039 12.25 7.35039Z\" fill=\"currentColor\"/><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M5.24999 17.6004C6.61818 17.6004 7.76119 16.6363 8.03673 15.3504H18V14.1504H8.03673C7.76119 12.8645 6.61818 11.9004 5.24999 11.9004C3.88181 11.9004 2.7388 12.8645 2.46326 14.1504H0V15.3504H2.46326C2.7388 16.6363 3.88181 17.6004 5.24999 17.6004ZM5.24999 13.1004C4.33872 13.1004 3.59999 13.8391 3.59999 14.7504C3.59999 15.6617 4.33872 16.4004 5.24999 16.4004C6.16126 16.4004 6.89999 15.6617 6.89999 14.7504C6.89999 13.8391 6.16126 13.1004 5.24999 13.1004Z\" fill=\"currentColor\"/></g><defs><clipPath id=\"clip0_1217_47603\"><rect width=\"18\" height=\"18\" fill=\"white\"/></clipPath></defs></svg>";
5
+ export declare const removeIcon = "<svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M14.8234 16.4118C14.8234 16.7029 14.5852 16.9412 14.294 16.9412H3.70577C3.41459 16.9412 3.17636 16.7029 3.17636 16.4118V5.29412H2.11753V16.4118C2.11753 17.2874 2.83012 18 3.70577 18H14.294C15.1697 18 15.8822 17.2874 15.8822 16.4118V5.29412H14.8234V16.4118ZM6.35284 14.8235H7.41166V6.35294H6.35284V14.8235ZM10.5881 14.8235H11.6469V6.35294H10.5881V14.8235ZM12.5682 3.17647L11.4099 0H6.51176L5.35553 3.17647H0V4.23529H18V3.17647H12.5682ZM6.48105 3.17647L7.25293 1.05882H10.6698L11.4416 3.17647H6.48105Z\" fill=\"currentColor\"/></svg>";
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright (C) 2023 - 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 const thumbnailViewIcon = '<svg width="24" height="17" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1209_167295)"><path d="M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z" fill="currentColor"/><path d="M21.7804 14.7887H2.21973V2.21143H21.7804V14.7887ZM9.84978 11.4716L14.5665 8.63826L9.84978 5.80492V11.4716Z" fill="currentColor"/></g><defs><clipPath id="clip0_1209_167295"><rect width="24" height="17" fill="white"/></clipPath></defs></svg>';
20
+ export const learnViewIcon = '<svg width="31" height="22" viewBox="0 0 31 22" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_93_30328)"><path d="M31 22H0V0H31V22ZM1.43353 1.43089V20.5691H29.5665V1.43089H1.43353Z" fill="currentColor"/><path d="M27.9537 3.04102H19.7109V18.9597H27.9537V3.04102Z" fill="currentColor"/><path d="M18.2781 18.9597H3.04688V3.04102H18.2781V18.9597ZM8.02537 14.7989L14.3165 11.0907L8.02537 7.38244V14.7989Z" fill="currentColor"/></g><defs><clipPath id="clip0_93_30328"><rect width="31" height="22" fill="white"/></clipPath></defs></svg>';
21
+ export const collabViewIcon = '<svg width="24" height="17" viewBox="0 0 24 17" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1209_167302)"><path d="M24 17H0V0H24V17ZM1.10983 1.10569V15.8943H22.8902V1.10569H1.10983Z" fill="currentColor"/><path d="M21.6416 2.34961H17.3411V8.15449H21.6416V2.34961Z" fill="currentColor"/><path d="M16.2312 8.15449H2.3584V2.34961H16.2312V8.15449ZM8.60117 7.18701L11.7919 5.39026L8.60117 3.59351V7.18701Z" fill="currentColor"/><path d="M21.6415 12.5771H4.99414V13.4064H21.6415V12.5771Z" fill="currentColor"/><path d="M21.6415 13.8213H4.99414V14.6506H21.6415V13.8213Z" fill="currentColor"/><path d="M3.39886 14.6503C3.97349 14.6503 4.43932 14.1862 4.43932 13.6137C4.43932 13.0412 3.97349 12.5771 3.39886 12.5771C2.82423 12.5771 2.3584 13.0412 2.3584 13.6137C2.3584 14.1862 2.82423 14.6503 3.39886 14.6503Z" fill="currentColor"/><path d="M21.6415 9.39844H4.99414V10.2277H21.6415V9.39844Z" fill="currentColor"/><path d="M21.6415 10.6421H4.99414V11.4714H21.6415V10.6421Z" fill="currentColor"/><path d="M3.39886 11.4716C3.97349 11.4716 4.43932 11.0075 4.43932 10.435C4.43932 9.86253 3.97349 9.39844 3.39886 9.39844C2.82423 9.39844 2.3584 9.86253 2.3584 10.435C2.3584 11.0075 2.82423 11.4716 3.39886 11.4716Z" fill="currentColor"/></g><defs><clipPath id="clip0_1209_167302"><rect width="24" height="17" fill="white"/></clipPath></defs></svg>';
22
+ export const optionsIcon = '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1217_47603)"><path fill-rule="evenodd" clip-rule="evenodd" d="M2.46326 3.85039H0V2.65039H2.46326C2.7388 1.36449 3.88181 0.400391 5.24999 0.400391C6.61818 0.400391 7.76119 1.36449 8.03673 2.65039H18V3.85039H8.03673C7.76119 5.13629 6.61818 6.10039 5.24999 6.10039C3.88181 6.10039 2.7388 5.13629 2.46326 3.85039ZM3.59999 3.25039C3.59999 2.33912 4.33872 1.60039 5.24999 1.60039C6.16126 1.60039 6.89999 2.33912 6.89999 3.25039C6.89999 4.16166 6.16126 4.90039 5.24999 4.90039C4.33872 4.90039 3.59999 4.16166 3.59999 3.25039Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M0 8.40039H9.46326C9.7388 7.11449 10.8818 6.15039 12.25 6.15039C13.6182 6.15039 14.7612 7.11449 15.0367 8.40039H18V9.60039H15.0367C14.7612 10.8863 13.6182 11.8504 12.25 11.8504C10.8818 11.8504 9.7388 10.8863 9.46326 9.60039H0V8.40039ZM12.25 7.35039C11.3387 7.35039 10.6 8.08912 10.6 9.00039C10.6 9.91166 11.3387 10.6504 12.25 10.6504C13.1613 10.6504 13.9 9.91166 13.9 9.00039C13.9 8.08912 13.1613 7.35039 12.25 7.35039Z" fill="currentColor"/><path fill-rule="evenodd" clip-rule="evenodd" d="M5.24999 17.6004C6.61818 17.6004 7.76119 16.6363 8.03673 15.3504H18V14.1504H8.03673C7.76119 12.8645 6.61818 11.9004 5.24999 11.9004C3.88181 11.9004 2.7388 12.8645 2.46326 14.1504H0V15.3504H2.46326C2.7388 16.6363 3.88181 17.6004 5.24999 17.6004ZM5.24999 13.1004C4.33872 13.1004 3.59999 13.8391 3.59999 14.7504C3.59999 15.6617 4.33872 16.4004 5.24999 16.4004C6.16126 16.4004 6.89999 15.6617 6.89999 14.7504C6.89999 13.8391 6.16126 13.1004 5.24999 13.1004Z" fill="currentColor"/></g><defs><clipPath id="clip0_1217_47603"><rect width="18" height="18" fill="white"/></clipPath></defs></svg>';
23
+ export const removeIcon = '<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M14.8234 16.4118C14.8234 16.7029 14.5852 16.9412 14.294 16.9412H3.70577C3.41459 16.9412 3.17636 16.7029 3.17636 16.4118V5.29412H2.11753V16.4118C2.11753 17.2874 2.83012 18 3.70577 18H14.294C15.1697 18 15.8822 17.2874 15.8822 16.4118V5.29412H14.8234V16.4118ZM6.35284 14.8235H7.41166V6.35294H6.35284V14.8235ZM10.5881 14.8235H11.6469V6.35294H10.5881V14.8235ZM12.5682 3.17647L11.4099 0H6.51176L5.35553 3.17647H0V4.23529H18V3.17647H12.5682ZM6.48105 3.17647L7.25293 1.05882H10.6698L11.4416 3.17647H6.48105Z" fill="currentColor"/></svg>';
@@ -56,6 +56,7 @@ export const WordCountModal = ({
56
56
  label: formatMessage('Word Count'),
57
57
  mountNode: instuiPopupMountNodeFn(),
58
58
  open: true,
59
+ onDismiss: onDismiss,
59
60
  "data-mce-component": true
60
61
  }, /*#__PURE__*/React.createElement(Modal.Header, null, /*#__PURE__*/React.createElement(CloseButton, {
61
62
  placement: "end",
@@ -0,0 +1,75 @@
1
+ /*
2
+ * Copyright (C) 2024 - 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 tinymce from 'tinymce';
20
+ import formatMessage from '../../../format-message';
21
+ import { debounce } from '@instructure/debounce';
22
+ const clickCallbackPromise = import('../instructure_wordcount/clickCallback');
23
+ const TOOLTIP_MESSAGE = formatMessage('View word and character counts');
24
+ const UPDATE_DEBOUNCE_MS = 100;
25
+ function formatWordCount(count) {
26
+ return formatMessage(`{count, plural,
27
+ =0 {0 words}
28
+ one {1 word}
29
+ other {# words}
30
+ }`, {
31
+ count
32
+ });
33
+ }
34
+ tinymce.PluginManager.add('instructure_wordcount_header', function (ed) {
35
+ function updateWordCountDisplay() {
36
+ const count = ed.plugins.wordcount.body.getWordCount();
37
+ const button = ed.getContainer()?.querySelector(`[title*="${TOOLTIP_MESSAGE}"]`);
38
+ if (!button) {
39
+ return;
40
+ }
41
+ const textSpan = button.querySelector('.tox-tbtn__select-label');
42
+ if (textSpan) {
43
+ textSpan.textContent = formatWordCount(count);
44
+ }
45
+ const tooltip = `${TOOLTIP_MESSAGE} - ${formatWordCount(count)}`;
46
+ button.setAttribute('title', tooltip);
47
+ button.setAttribute('aria-label', tooltip);
48
+ }
49
+ ed.addCommand('instructureWordCountHeader', () => {
50
+ clickCallbackPromise.then(module => module.default(ed, document, {
51
+ skipEditorFocus: false
52
+ }));
53
+ });
54
+ ed.ui.registry.addButton('instructure_wordcount_header', {
55
+ text: formatWordCount(0),
56
+ tooltip: TOOLTIP_MESSAGE,
57
+ onAction: () => ed.execCommand('instructureWordCountHeader')
58
+ });
59
+ ed.on('PostRender', () => {
60
+ updateWordCountDisplay();
61
+ });
62
+ const debouncedUpdate = debounce(updateWordCountDisplay, UPDATE_DEBOUNCE_MS, {
63
+ trailing: true
64
+ });
65
+ ed.on('NodeChange', debouncedUpdate);
66
+ ed.on('KeyUp', debouncedUpdate);
67
+ ed.on('SetContent', debouncedUpdate);
68
+ ed.on('Change', debouncedUpdate);
69
+ ed.on('Undo', debouncedUpdate);
70
+ ed.on('Redo', debouncedUpdate);
71
+ ed.on('Paste', debouncedUpdate);
72
+ ed.on('init', () => {
73
+ updateWordCountDisplay();
74
+ });
75
+ });
@@ -26,7 +26,7 @@ export function asLink($element: any, editor: any): {
26
26
  export function asVideoElement($element: any): {
27
27
  $element: any;
28
28
  type: string;
29
- id: any;
29
+ id: string | null;
30
30
  titleText: any;
31
31
  appliedHeight: any;
32
32
  appliedWidth: any;
@@ -62,7 +62,6 @@ export function isFileLink($element: any, editor: any): boolean;
62
62
  export function isImageEmbed($element: any): boolean;
63
63
  export function isVideoElement($element: any): boolean;
64
64
  export function isAudioElement($element: any): boolean;
65
- export function findMediaPlayerIframe(elem: any): any;
66
65
  export const LINK_TYPE: "link";
67
66
  export const FILE_LINK_TYPE: "file-link";
68
67
  export const IMAGE_EMBED_TYPE: "image-embed";
@@ -21,6 +21,7 @@ import { isOnlyTextSelected } from '../../contentInsertionUtils';
21
21
  import formatMessage from '../../../format-message';
22
22
  import { isStudioEmbeddedMedia } from './StudioLtiSupportUtils';
23
23
  import { parseUrlPath } from '../../../util/url-util';
24
+ import { findMediaPlayerIframe } from './iframeUtils';
24
25
  const FILE_DOWNLOAD_PATH_REGEX = /^\/(courses\/\d+\/)?files\/\d+\/download$/;
25
26
  export const LINK_TYPE = 'link';
26
27
  export const FILE_LINK_TYPE = 'file-link';
@@ -208,22 +209,4 @@ export function isVideoElement($element) {
208
209
  }
209
210
  export function isAudioElement($element) {
210
211
  return isMediaElement($element, 'audio');
211
- }
212
- export function findMediaPlayerIframe(elem) {
213
- if (!elem) return null;
214
- if (elem.tagName === 'IFRAME') {
215
- // we have the iframe
216
- return elem;
217
- }
218
- if (elem.firstElementChild?.tagName === 'IFRAME') {
219
- // we have the shim tinymce puts around the iframe
220
- return elem.firstElementChild;
221
- }
222
- if (elem.classList.contains('mce-shim')) {
223
- // tinymce puts a <span class='mce-shin'> after the iframe (since v5, I think)
224
- if (elem.previousSibling?.tagName === 'IFRAME') {
225
- return elem.previousSibling;
226
- }
227
- }
228
- return null;
229
212
  }
@@ -1,4 +1,4 @@
1
- import { EditorEvent, Events } from 'tinymce';
1
+ import { Editor, EditorEvent, Events } from 'tinymce';
2
2
  /**
3
3
  * Interface for content item's 'custom' field, specifically for what is expected to come from Studio
4
4
  *
@@ -35,3 +35,12 @@ export declare function parseStudioOptions(element: Element | null): ParsedStudi
35
35
  * underlying iframe has a `data-studio-resizable='false'`
36
36
  */
37
37
  export declare function handleBeforeObjectSelected(e: EditorEvent<Events.ObjectSelectedEvent>): void;
38
+ export declare function findStudioLtiIframeFromSelection(selectedNode: Node): HTMLIFrameElement | null;
39
+ export declare const notifyStudioEmbedTypeChange: (editor: Editor, embedType: "thumbnail_embed" | "learn_embed" | "collaboration_embed") => void;
40
+ export type EmbedType = 'thumbnail_embed' | 'learn_embed' | 'collaboration_embed';
41
+ export declare const updateStudioIframeDimensions: (editor: Editor, width: number, height: number, embedType: EmbedType, resizable?: boolean) => void;
42
+ type ValidStudioEmbedType = 'thumbnail_embed' | 'learn_embed' | 'collaboration_embed';
43
+ export declare const isValidEmbedType: (embedType: any) => embedType is ValidStudioEmbedType;
44
+ export declare const isValidDimension: (value: any) => value is number;
45
+ export declare const isValidResizable: (value: any) => value is boolean;
46
+ export {};
@@ -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,112 @@ 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, resizable) => {
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
+ if (resizable !== undefined) {
145
+ // Update both the actual attribute and the TinyMCE prefixed version
146
+ // This ensures they stay in sync when content is saved and reloaded
147
+ editor.dom.setAttrib(tinymceIframeShim, 'data-studio-resizable', String(resizable));
148
+ editor.dom.setAttrib(tinymceIframeShim, 'data-mce-p-data-studio-resizable', String(resizable));
149
+
150
+ // Force TinyMCE to update the overlay by setting/removing data-mce-resize
151
+ if (!resizable) {
152
+ tinymceIframeShim.setAttribute('data-mce-resize', 'false');
153
+ } else {
154
+ tinymceIframeShim.removeAttribute('data-mce-resize');
155
+ }
156
+ }
157
+ const href = editor.dom.getAttrib(tinymceIframeShim, 'data-mce-p-src');
158
+ if (href && embedType) {
159
+ if (embedType) {
160
+ // Replace thumbnail_embed, learn_embed, or collaboration_embed with the new embed type
161
+ const updatedHref = href.replace(/(thumbnail_embed|learn_embed|collaboration_embed)/g, embedType);
162
+
163
+ // 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
164
+ editor.dom.setAttrib(tinymceIframeShim, 'data-mce-p-src', updatedHref);
165
+ editor.nodeChanged();
166
+ }
167
+ }
168
+ editor.fire('ObjectResized', {
169
+ // @ts-expect-error - needed for aligning tooltip with new iframe size
170
+ target: videoContainer,
171
+ width,
172
+ height
173
+ });
174
+ };
175
+ export const isValidEmbedType = embedType => {
176
+ return typeof embedType === 'string' && ['thumbnail_embed', 'learn_embed', 'collaboration_embed'].includes(embedType);
177
+ };
178
+ export const isValidDimension = value => {
179
+ return typeof value === 'number' && !isNaN(value) && isFinite(value) && value > 0;
180
+ };
181
+ export const isValidResizable = value => {
182
+ return typeof value === 'boolean';
183
+ };
@@ -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;