PluginProbe
Gutenberg / 12.1.0
Gutenberg v12.1.0
23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 7.4.0 All 402 releases
gutenberg / build / media-utils / index.js

index.js in Gutenberg 12.1.0, at build/media-utils/index.js

715 lines 22.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ (function() { // webpackBootstrap
2 /******/ "use strict";
3 /******/ // The require scope
4 /******/ var __webpack_require__ = {};
5 /******/
6 /************************************************************************/
7 /******/ /* webpack/runtime/compat get default export */
8 /******/ !function() {
9 /******/ // getDefaultExport function for compatibility with non-harmony modules
10 /******/ __webpack_require__.n = function(module) {
11 /******/ var getter = module && module.__esModule ?
12 /******/ function() { return module['default']; } :
13 /******/ function() { return module; };
14 /******/ __webpack_require__.d(getter, { a: getter });
15 /******/ return getter;
16 /******/ };
17 /******/ }();
18 /******/
19 /******/ /* webpack/runtime/define property getters */
20 /******/ !function() {
21 /******/ // define getter functions for harmony exports
22 /******/ __webpack_require__.d = function(exports, definition) {
23 /******/ for(var key in definition) {
24 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
25 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
26 /******/ }
27 /******/ }
28 /******/ };
29 /******/ }();
30 /******/
31 /******/ /* webpack/runtime/hasOwnProperty shorthand */
32 /******/ !function() {
33 /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
34 /******/ }();
35 /******/
36 /******/ /* webpack/runtime/make namespace object */
37 /******/ !function() {
38 /******/ // define __esModule on exports
39 /******/ __webpack_require__.r = function(exports) {
40 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
41 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
42 /******/ }
43 /******/ Object.defineProperty(exports, '__esModule', { value: true });
44 /******/ };
45 /******/ }();
46 /******/
47 /************************************************************************/
48 var __webpack_exports__ = {};
49 // ESM COMPAT FLAG
50 __webpack_require__.r(__webpack_exports__);
51
52 // EXPORTS
53 __webpack_require__.d(__webpack_exports__, {
54 "MediaUpload": function() { return /* reexport */ media_upload; },
55 "uploadMedia": function() { return /* reexport */ uploadMedia; }
56 });
57
58 ;// CONCATENATED MODULE: external "lodash"
59 var external_lodash_namespaceObject = window["lodash"];
60 ;// CONCATENATED MODULE: external ["wp","element"]
61 var external_wp_element_namespaceObject = window["wp"]["element"];
62 ;// CONCATENATED MODULE: external ["wp","i18n"]
63 var external_wp_i18n_namespaceObject = window["wp"]["i18n"];
64 ;// CONCATENATED MODULE: ./packages/media-utils/build-module/components/media-upload/index.js
65 /**
66 * External dependencies
67 */
68
69 /**
70 * WordPress dependencies
71 */
72
73
74
75 const {
76 wp
77 } = window;
78 const DEFAULT_EMPTY_GALLERY = [];
79 /**
80 * Prepares the Featured Image toolbars and frames.
81 *
82 * @return {wp.media.view.MediaFrame.Select} The default media workflow.
83 */
84
85 const getFeaturedImageMediaFrame = () => {
86 return wp.media.view.MediaFrame.Select.extend({
87 /**
88 * Enables the Set Featured Image Button.
89 *
90 * @param {Object} toolbar toolbar for featured image state
91 * @return {void}
92 */
93 featuredImageToolbar(toolbar) {
94 this.createSelectToolbar(toolbar, {
95 text: wp.media.view.l10n.setFeaturedImage,
96 state: this.options.state
97 });
98 },
99
100 /**
101 * Handle the edit state requirements of selected media item.
102 *
103 * @return {void}
104 */
105 editState() {
106 const selection = this.state('featured-image').get('selection');
107 const view = new wp.media.view.EditImage({
108 model: selection.single(),
109 controller: this
110 }).render(); // Set the view to the EditImage frame using the selected image.
111
112 this.content.set(view); // After bringing in the frame, load the actual editor via an ajax call.
113
114 view.loadEditor();
115 },
116
117 /**
118 * Create the default states.
119 *
120 * @return {void}
121 */
122 createStates: function createStates() {
123 this.on('toolbar:create:featured-image', this.featuredImageToolbar, this);
124 this.on('content:render:edit-image', this.editState, this);
125 this.states.add([new wp.media.controller.FeaturedImage(), new wp.media.controller.EditImage({
126 model: this.options.editImage
127 })]);
128 }
129 });
130 };
131 /**
132 * Prepares the Gallery toolbars and frames.
133 *
134 * @return {wp.media.view.MediaFrame.Post} The default media workflow.
135 */
136
137
138 const getGalleryDetailsMediaFrame = () => {
139 /**
140 * Custom gallery details frame.
141 *
142 * @see https://github.com/xwp/wp-core-media-widgets/blob/905edbccfc2a623b73a93dac803c5335519d7837/wp-admin/js/widgets/media-gallery-widget.js
143 * @class GalleryDetailsMediaFrame
144 * @class
145 */
146 return wp.media.view.MediaFrame.Post.extend({
147 /**
148 * Set up gallery toolbar.
149 *
150 * @return {void}
151 */
152 galleryToolbar() {
153 const editing = this.state().get('editing');
154 this.toolbar.set(new wp.media.view.Toolbar({
155 controller: this,
156 items: {
157 insert: {
158 style: 'primary',
159 text: editing ? wp.media.view.l10n.updateGallery : wp.media.view.l10n.insertGallery,
160 priority: 80,
161 requires: {
162 library: true
163 },
164
165 /**
166 * @fires wp.media.controller.State#update
167 */
168 click() {
169 const controller = this.controller,
170 state = controller.state();
171 controller.close();
172 state.trigger('update', state.get('library')); // Restore and reset the default state.
173
174 controller.setState(controller.options.state);
175 controller.reset();
176 }
177
178 }
179 }
180 }));
181 },
182
183 /**
184 * Handle the edit state requirements of selected media item.
185 *
186 * @return {void}
187 */
188 editState() {
189 const selection = this.state('gallery').get('selection');
190 const view = new wp.media.view.EditImage({
191 model: selection.single(),
192 controller: this
193 }).render(); // Set the view to the EditImage frame using the selected image.
194
195 this.content.set(view); // After bringing in the frame, load the actual editor via an ajax call.
196
197 view.loadEditor();
198 },
199
200 /**
201 * Create the default states.
202 *
203 * @return {void}
204 */
205 createStates: function createStates() {
206 this.on('toolbar:create:main-gallery', this.galleryToolbar, this);
207 this.on('content:render:edit-image', this.editState, this);
208 this.states.add([new wp.media.controller.Library({
209 id: 'gallery',
210 title: wp.media.view.l10n.createGalleryTitle,
211 priority: 40,
212 toolbar: 'main-gallery',
213 filterable: 'uploaded',
214 multiple: 'add',
215 editable: false,
216 library: wp.media.query((0,external_lodash_namespaceObject.defaults)({
217 type: 'image'
218 }, this.options.library))
219 }), new wp.media.controller.EditImage({
220 model: this.options.editImage
221 }), new wp.media.controller.GalleryEdit({
222 library: this.options.selection,
223 editing: this.options.editing,
224 menu: 'gallery',
225 displaySettings: false,
226 multiple: true
227 }), new wp.media.controller.GalleryAdd()]);
228 }
229 });
230 }; // the media library image object contains numerous attributes
231 // we only need this set to display the image in the library
232
233
234 const slimImageObject = img => {
235 const attrSet = ['sizes', 'mime', 'type', 'subtype', 'id', 'url', 'alt', 'link', 'caption'];
236 return (0,external_lodash_namespaceObject.pick)(img, attrSet);
237 };
238
239 const getAttachmentsCollection = ids => {
240 return wp.media.query({
241 order: 'ASC',
242 orderby: 'post__in',
243 post__in: ids,
244 posts_per_page: -1,
245 query: true,
246 type: 'image'
247 });
248 };
249
250 class MediaUpload extends external_wp_element_namespaceObject.Component {
251 constructor(_ref) {
252 let {
253 allowedTypes,
254 gallery = false,
255 unstableFeaturedImageFlow = false,
256 modalClass,
257 multiple = false,
258 title = (0,external_wp_i18n_namespaceObject.__)('Select or Upload Media')
259 } = _ref;
260 super(...arguments);
261 this.openModal = this.openModal.bind(this);
262 this.onOpen = this.onOpen.bind(this);
263 this.onSelect = this.onSelect.bind(this);
264 this.onUpdate = this.onUpdate.bind(this);
265 this.onClose = this.onClose.bind(this);
266
267 if (gallery) {
268 this.buildAndSetGalleryFrame();
269 } else {
270 const frameConfig = {
271 title,
272 multiple
273 };
274
275 if (!!allowedTypes) {
276 frameConfig.library = {
277 type: allowedTypes
278 };
279 }
280
281 this.frame = wp.media(frameConfig);
282 }
283
284 if (modalClass) {
285 this.frame.$el.addClass(modalClass);
286 }
287
288 if (unstableFeaturedImageFlow) {
289 this.buildAndSetFeatureImageFrame();
290 }
291
292 this.initializeListeners();
293 }
294
295 initializeListeners() {
296 // When an image is selected in the media frame...
297 this.frame.on('select', this.onSelect);
298 this.frame.on('update', this.onUpdate);
299 this.frame.on('open', this.onOpen);
300 this.frame.on('close', this.onClose);
301 }
302 /**
303 * Sets the Gallery frame and initializes listeners.
304 *
305 * @return {void}
306 */
307
308
309 buildAndSetGalleryFrame() {
310 const {
311 addToGallery = false,
312 allowedTypes,
313 multiple = false,
314 value = DEFAULT_EMPTY_GALLERY
315 } = this.props; // If the value did not changed there is no need to rebuild the frame,
316 // we can continue to use the existing one.
317
318 if (value === this.lastGalleryValue) {
319 return;
320 }
321
322 this.lastGalleryValue = value; // If a frame already existed remove it.
323
324 if (this.frame) {
325 this.frame.remove();
326 }
327
328 let currentState;
329
330 if (addToGallery) {
331 currentState = 'gallery-library';
332 } else {
333 currentState = value && value.length ? 'gallery-edit' : 'gallery';
334 }
335
336 if (!this.GalleryDetailsMediaFrame) {
337 this.GalleryDetailsMediaFrame = getGalleryDetailsMediaFrame();
338 }
339
340 const attachments = getAttachmentsCollection(value);
341 const selection = new wp.media.model.Selection(attachments.models, {
342 props: attachments.props.toJSON(),
343 multiple
344 });
345 this.frame = new this.GalleryDetailsMediaFrame({
346 mimeType: allowedTypes,
347 state: currentState,
348 multiple,
349 selection,
350 editing: value && value.length ? true : false
351 });
352 wp.media.frame = this.frame;
353 this.initializeListeners();
354 }
355 /**
356 * Initializes the Media Library requirements for the featured image flow.
357 *
358 * @return {void}
359 */
360
361
362 buildAndSetFeatureImageFrame() {
363 const featuredImageFrame = getFeaturedImageMediaFrame();
364 const attachments = getAttachmentsCollection(this.props.value);
365 const selection = new wp.media.model.Selection(attachments.models, {
366 props: attachments.props.toJSON()
367 });
368 this.frame = new featuredImageFrame({
369 mimeType: this.props.allowedTypes,
370 state: 'featured-image',
371 multiple: this.props.multiple,
372 selection,
373 editing: this.props.value ? true : false
374 });
375 wp.media.frame = this.frame;
376 }
377
378 componentWillUnmount() {
379 this.frame.remove();
380 }
381
382 onUpdate(selections) {
383 const {
384 onSelect,
385 multiple = false
386 } = this.props;
387 const state = this.frame.state();
388 const selectedImages = selections || state.get('selection');
389
390 if (!selectedImages || !selectedImages.models.length) {
391 return;
392 }
393
394 if (multiple) {
395 onSelect(selectedImages.models.map(model => slimImageObject(model.toJSON())));
396 } else {
397 onSelect(slimImageObject(selectedImages.models[0].toJSON()));
398 }
399 }
400
401 onSelect() {
402 const {
403 onSelect,
404 multiple = false
405 } = this.props; // Get media attachment details from the frame state
406
407 const attachment = this.frame.state().get('selection').toJSON();
408 onSelect(multiple ? attachment : attachment[0]);
409 }
410
411 onOpen() {
412 var _this$props$value;
413
414 this.updateCollection(); // Handle both this.props.value being either (number[]) multiple ids
415 // (for galleries) or a (number) singular id (e.g. image block).
416
417 const hasMedia = Array.isArray(this.props.value) ? !!((_this$props$value = this.props.value) !== null && _this$props$value !== void 0 && _this$props$value.length) : !!this.props.value;
418
419 if (!hasMedia) {
420 return;
421 }
422
423 const isGallery = this.props.gallery;
424 const selection = this.frame.state().get('selection');
425
426 if (!isGallery) {
427 (0,external_lodash_namespaceObject.castArray)(this.props.value).forEach(id => {
428 selection.add(wp.media.attachment(id));
429 });
430 } // Load the images so they are available in the media modal.
431
432
433 const attachments = getAttachmentsCollection((0,external_lodash_namespaceObject.castArray)(this.props.value)); // Once attachments are loaded, set the current selection.
434
435 attachments.more().done(function () {
436 var _attachments$models;
437
438 if (isGallery && attachments !== null && attachments !== void 0 && (_attachments$models = attachments.models) !== null && _attachments$models !== void 0 && _attachments$models.length) {
439 selection.add(attachments.models);
440 }
441 });
442 }
443
444 onClose() {
445 const {
446 onClose
447 } = this.props;
448
449 if (onClose) {
450 onClose();
451 }
452 }
453
454 updateCollection() {
455 const frameContent = this.frame.content.get();
456
457 if (frameContent && frameContent.collection) {
458 const collection = frameContent.collection; // clean all attachments we have in memory.
459
460 collection.toArray().forEach(model => model.trigger('destroy', model)); // reset has more flag, if library had small amount of items all items may have been loaded before.
461
462 collection.mirroring._hasMore = true; // request items
463
464 collection.more();
465 }
466 }
467
468 openModal() {
469 if (this.props.gallery) {
470 this.buildAndSetGalleryFrame();
471 }
472
473 this.frame.open();
474 }
475
476 render() {
477 return this.props.render({
478 open: this.openModal
479 });
480 }
481
482 }
483
484 /* harmony default export */ var media_upload = (MediaUpload);
485 //# sourceMappingURL=index.js.map
486 ;// CONCATENATED MODULE: ./packages/media-utils/build-module/components/index.js
487
488 //# sourceMappingURL=index.js.map
489 ;// CONCATENATED MODULE: external ["wp","apiFetch"]
490 var external_wp_apiFetch_namespaceObject = window["wp"]["apiFetch"];
491 var external_wp_apiFetch_default = /*#__PURE__*/__webpack_require__.n(external_wp_apiFetch_namespaceObject);
492 ;// CONCATENATED MODULE: external ["wp","blob"]
493 var external_wp_blob_namespaceObject = window["wp"]["blob"];
494 ;// CONCATENATED MODULE: ./packages/media-utils/build-module/utils/upload-media.js
495
496
497 /**
498 * External dependencies
499 */
500
501 /**
502 * WordPress dependencies
503 */
504
505
506
507
508 /**
509 * Browsers may use unexpected mime types, and they differ from browser to browser.
510 * This function computes a flexible array of mime types from the mime type structured provided by the server.
511 * Converts { jpg|jpeg|jpe: "image/jpeg" } into [ "image/jpeg", "image/jpg", "image/jpeg", "image/jpe" ]
512 * The computation of this array instead of directly using the object,
513 * solves the problem in chrome where mp3 files have audio/mp3 as mime type instead of audio/mpeg.
514 * https://bugs.chromium.org/p/chromium/issues/detail?id=227004
515 *
516 * @param {?Object} wpMimeTypesObject Mime type object received from the server.
517 * Extensions are keys separated by '|' and values are mime types associated with an extension.
518 *
519 * @return {?Array} An array of mime types or the parameter passed if it was "falsy".
520 */
521
522 function getMimeTypesArray(wpMimeTypesObject) {
523 if (!wpMimeTypesObject) {
524 return wpMimeTypesObject;
525 }
526
527 return (0,external_lodash_namespaceObject.flatMap)(wpMimeTypesObject, (mime, extensionsString) => {
528 const [type] = mime.split('/');
529 const extensions = extensionsString.split('|');
530 return [mime, ...(0,external_lodash_namespaceObject.map)(extensions, extension => `${type}/${extension}`)];
531 });
532 }
533 /**
534 * Media Upload is used by audio, image, gallery, video, and file blocks to
535 * handle uploading a media file when a file upload button is activated.
536 *
537 * TODO: future enhancement to add an upload indicator.
538 *
539 * @param {Object} $0 Parameters object passed to the function.
540 * @param {?Array} $0.allowedTypes Array with the types of media that can be uploaded, if unset all types are allowed.
541 * @param {?Object} $0.additionalData Additional data to include in the request.
542 * @param {Array} $0.filesList List of files.
543 * @param {?number} $0.maxUploadFileSize Maximum upload size in bytes allowed for the site.
544 * @param {Function} $0.onError Function called when an error happens.
545 * @param {Function} $0.onFileChange Function called each time a file or a temporary representation of the file is available.
546 * @param {?Object} $0.wpAllowedMimeTypes List of allowed mime types and file extensions.
547 */
548
549 async function uploadMedia(_ref) {
550 let {
551 allowedTypes,
552 additionalData = {},
553 filesList,
554 maxUploadFileSize,
555 onError = external_lodash_namespaceObject.noop,
556 onFileChange,
557 wpAllowedMimeTypes = null
558 } = _ref;
559 // Cast filesList to array
560 const files = [...filesList];
561 const filesSet = [];
562
563 const setAndUpdateFiles = (idx, value) => {
564 (0,external_wp_blob_namespaceObject.revokeBlobURL)((0,external_lodash_namespaceObject.get)(filesSet, [idx, 'url']));
565 filesSet[idx] = value;
566 onFileChange((0,external_lodash_namespaceObject.compact)(filesSet));
567 }; // Allowed type specified by consumer
568
569
570 const isAllowedType = fileType => {
571 if (!allowedTypes) {
572 return true;
573 }
574
575 return (0,external_lodash_namespaceObject.some)(allowedTypes, allowedType => {
576 // If a complete mimetype is specified verify if it matches exactly the mime type of the file.
577 if ((0,external_lodash_namespaceObject.includes)(allowedType, '/')) {
578 return allowedType === fileType;
579 } // Otherwise a general mime type is used and we should verify if the file mimetype starts with it.
580
581
582 return (0,external_lodash_namespaceObject.startsWith)(fileType, `${allowedType}/`);
583 });
584 }; // Allowed types for the current WP_User
585
586
587 const allowedMimeTypesForUser = getMimeTypesArray(wpAllowedMimeTypes);
588
589 const isAllowedMimeTypeForUser = fileType => {
590 return (0,external_lodash_namespaceObject.includes)(allowedMimeTypesForUser, fileType);
591 }; // Build the error message including the filename
592
593
594 const triggerError = error => {
595 error.message = [(0,external_wp_element_namespaceObject.createElement)("strong", {
596 key: "filename"
597 }, error.file.name), ': ', error.message];
598 onError(error);
599 };
600
601 const validFiles = [];
602
603 for (const mediaFile of files) {
604 // Verify if user is allowed to upload this mime type.
605 // Defer to the server when type not detected.
606 if (allowedMimeTypesForUser && mediaFile.type && !isAllowedMimeTypeForUser(mediaFile.type)) {
607 triggerError({
608 code: 'MIME_TYPE_NOT_ALLOWED_FOR_USER',
609 message: (0,external_wp_i18n_namespaceObject.__)('Sorry, you are not allowed to upload this file type.'),
610 file: mediaFile
611 });
612 continue;
613 } // Check if the block supports this mime type.
614 // Defer to the server when type not detected.
615
616
617 if (mediaFile.type && !isAllowedType(mediaFile.type)) {
618 triggerError({
619 code: 'MIME_TYPE_NOT_SUPPORTED',
620 message: (0,external_wp_i18n_namespaceObject.__)('Sorry, this file type is not supported here.'),
621 file: mediaFile
622 });
623 continue;
624 } // verify if file is greater than the maximum file upload size allowed for the site.
625
626
627 if (maxUploadFileSize && mediaFile.size > maxUploadFileSize) {
628 triggerError({
629 code: 'SIZE_ABOVE_LIMIT',
630 message: (0,external_wp_i18n_namespaceObject.__)('This file exceeds the maximum upload size for this site.'),
631 file: mediaFile
632 });
633 continue;
634 } // Don't allow empty files to be uploaded.
635
636
637 if (mediaFile.size <= 0) {
638 triggerError({
639 code: 'EMPTY_FILE',
640 message: (0,external_wp_i18n_namespaceObject.__)('This file is empty.'),
641 file: mediaFile
642 });
643 continue;
644 }
645
646 validFiles.push(mediaFile); // Set temporary URL to create placeholder media file, this is replaced
647 // with final file from media gallery when upload is `done` below
648
649 filesSet.push({
650 url: (0,external_wp_blob_namespaceObject.createBlobURL)(mediaFile)
651 });
652 onFileChange(filesSet);
653 }
654
655 for (let idx = 0; idx < validFiles.length; ++idx) {
656 const mediaFile = validFiles[idx];
657
658 try {
659 const savedMedia = await createMediaFromFile(mediaFile, additionalData);
660 const mediaObject = { ...(0,external_lodash_namespaceObject.omit)(savedMedia, ['alt_text', 'source_url']),
661 alt: savedMedia.alt_text,
662 caption: (0,external_lodash_namespaceObject.get)(savedMedia, ['caption', 'raw'], ''),
663 title: savedMedia.title.raw,
664 url: savedMedia.source_url
665 };
666 setAndUpdateFiles(idx, mediaObject);
667 } catch (error) {
668 // Reset to empty on failure.
669 setAndUpdateFiles(idx, null);
670 let message;
671
672 if ((0,external_lodash_namespaceObject.has)(error, ['message'])) {
673 message = (0,external_lodash_namespaceObject.get)(error, ['message']);
674 } else {
675 message = (0,external_wp_i18n_namespaceObject.sprintf)( // translators: %s: file name
676 (0,external_wp_i18n_namespaceObject.__)('Error while uploading file %s to the media library.'), mediaFile.name);
677 }
678
679 onError({
680 code: 'GENERAL',
681 message,
682 file: mediaFile
683 });
684 }
685 }
686 }
687 /**
688 * @param {File} file Media File to Save.
689 * @param {?Object} additionalData Additional data to include in the request.
690 *
691 * @return {Promise} Media Object Promise.
692 */
693
694 function createMediaFromFile(file, additionalData) {
695 // Create upload payload
696 const data = new window.FormData();
697 data.append('file', file, file.name || file.type.replace('/', '.'));
698 (0,external_lodash_namespaceObject.forEach)(additionalData, (value, key) => data.append(key, value));
699 return external_wp_apiFetch_default()({
700 path: '/wp/v2/media',
701 body: data,
702 method: 'POST'
703 });
704 }
705 //# sourceMappingURL=upload-media.js.map
706 ;// CONCATENATED MODULE: ./packages/media-utils/build-module/utils/index.js
707
708 //# sourceMappingURL=index.js.map
709 ;// CONCATENATED MODULE: ./packages/media-utils/build-module/index.js
710
711
712 //# sourceMappingURL=index.js.map
713 (window.wp = window.wp || {}).mediaUtils = __webpack_exports__;
714 /******/ })()
715 ;