diff --git a/includes/Admin/Dashboard.php b/includes/Admin/Dashboard.php index b46e5e22c861..0037f088dcb0 100644 --- a/includes/Admin/Dashboard.php +++ b/includes/Admin/Dashboard.php @@ -410,6 +410,8 @@ public function get_dashboard_settings(): array { if ( ! $max_upload_size ) { $max_upload_size = 0; } + $mime_types = $this->types->get_allowed_mime_types(); + $allowed_image_mime_types = $mime_types['image']; $settings = [ 'isRTL' => is_rtl(), @@ -419,7 +421,7 @@ public function get_dashboard_settings(): array { 'archiveURL' => $this->story_post_type->get_archive_link(), 'defaultArchiveURL' => $this->story_post_type->get_archive_link( true ), 'cdnURL' => trailingslashit( WEBSTORIES_CDN_URL ), - 'allowedImageMimeTypes' => $this->types->get_allowed_image_mime_types(), + 'allowedImageMimeTypes' => $allowed_image_mime_types, 'version' => WEBSTORIES_VERSION, 'encodeMarkup' => $this->decoder->supports_decoding(), 'api' => [ diff --git a/includes/Admin/Editor.php b/includes/Admin/Editor.php index cecea1019a29..ae4daf6b4676 100644 --- a/includes/Admin/Editor.php +++ b/includes/Admin/Editor.php @@ -346,9 +346,6 @@ public function get_editor_settings(): array { /** This filter is documented in wp-admin/includes/post.php */ $show_locked_dialog = apply_filters( 'show_post_locked_dialog', true, $post, $user ); $nonce = wp_create_nonce( 'wp_rest' ); - $mime_types = $this->types->get_allowed_mime_types(); - $image_mime_types = $this->types->get_allowed_image_mime_types(); - $audio_mime_types = $this->types->get_allowed_audio_mime_types(); $story = new Story(); $story->load_from_post( $post ); @@ -357,12 +354,7 @@ public function get_editor_settings(): array { 'autoSaveInterval' => \defined( 'AUTOSAVE_INTERVAL' ) ? AUTOSAVE_INTERVAL : null, 'isRTL' => is_rtl(), 'locale' => $this->locale->get_locale_settings(), - 'allowedFileTypes' => $this->types->get_allowed_file_types(), - 'allowedImageFileTypes' => $this->types->get_file_type_exts( $image_mime_types ), - 'allowedImageMimeTypes' => $image_mime_types, - 'allowedAudioFileTypes' => $this->types->get_file_type_exts( $audio_mime_types ), - 'allowedAudioMimeTypes' => $audio_mime_types, - 'allowedMimeTypes' => $mime_types, + 'allowedMimeTypes' => $this->types->get_allowed_mime_types(), 'postType' => $this->story_post_type->get_slug(), 'storyId' => $story_id, 'dashboardLink' => $dashboard_url, diff --git a/includes/Media/SVG.php b/includes/Media/SVG.php index 57a73bf5414f..6eb51f0029be 100644 --- a/includes/Media/SVG.php +++ b/includes/Media/SVG.php @@ -163,7 +163,7 @@ public function mime_types_add_svg( array $mime_types ): array { * @return array */ public function web_stories_allowed_mime_types( array $mime_types ): array { - $mime_types['image'][] = self::MIME_TYPE; + $mime_types['vector'][] = self::MIME_TYPE; return $mime_types; } diff --git a/includes/Media/Types.php b/includes/Media/Types.php index 0a063d7d1964..820e3067364e 100644 --- a/includes/Media/Types.php +++ b/includes/Media/Types.php @@ -30,27 +30,6 @@ * Class Types */ class Types { - /** - * Returns a list of allowed file types. - * - * @since 1.0.0 - * - * @return array List of allowed file types. - */ - public function get_allowed_file_types(): array { - $allowed_mime_types = $this->get_allowed_mime_types(); - $mime_types = []; - - foreach ( $allowed_mime_types as $mimes ) { - // Otherwise this throws a warning on PHP < 7.3. - if ( ! empty( $mimes ) ) { - array_push( $mime_types, ...$mimes ); - } - } - - return $this->get_file_type_exts( $mime_types ); - } - /** * Returns a list of allowed file types. * @@ -82,16 +61,22 @@ public function get_file_type_exts( array $mime_types = [] ): array { */ public function get_allowed_mime_types(): array { $default_allowed_mime_types = [ - 'image' => [ + 'image' => [ 'image/webp', 'image/png', 'image/jpeg', 'image/jpg', 'image/gif', ], - // TODO: Update once audio elements are supported. - 'audio' => [], - 'video' => [ + 'audio' => [ + 'audio/mpeg', + 'audio/aac', + 'audio/wav', + 'audio/ogg', + ], + 'caption' => [ 'text/vtt' ], + 'vector' => [], + 'video' => [ 'video/mp4', 'video/webm', ], @@ -120,55 +105,4 @@ public function get_allowed_mime_types(): array { return $allowed_mime_types; } - - /** - * Returns a list of image mime types. - * - * @since 1.4.0 - * - * @return array List of allowed mime types. - */ - public function get_allowed_image_mime_types(): array { - $mime_type = $this->get_allowed_mime_types(); - $allowed_mime_type = $mime_type['image']; - $image_mime_type = [ - 'image/webp', - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ]; - - /** - * Filter list of allowed poster image mime types. - * - * @since 1.4.0 - * - * @param array $image_mime_type List of allowed mime types. Defaults to 'image/png', 'image/jpeg', 'image/jpg','image/gif'. - * @param array $allowed_mime_type Allowed mime types from get_allowed_mime_types. - */ - $image_mime_type = apply_filters( 'web_stories_allowed_image_mime_types', $image_mime_type, $allowed_mime_type ); - - return array_values( array_intersect( $allowed_mime_type, $image_mime_type ) ); - } - - /** - * Returns a list of audio mime types. - * - * Used for the allowlist when adding background audio. - * - * @since 1.11.0 - * - * @return array List of allowed mime types. - */ - public function get_allowed_audio_mime_types(): array { - $allowed_mime_types = [ - 'audio/mpeg', - 'audio/aac', - 'audio/wav', - 'audio/ogg', - ]; - - return array_values( array_intersect( $allowed_mime_types, get_allowed_mime_types() ) ); - } } diff --git a/includes/REST_API/Hotlinking_Controller.php b/includes/REST_API/Hotlinking_Controller.php index 45c8aec00944..c5cff58b07a1 100644 --- a/includes/REST_API/Hotlinking_Controller.php +++ b/includes/REST_API/Hotlinking_Controller.php @@ -231,7 +231,7 @@ public function parse_url( $request ) { $ext = end( $exts ); } - $allowed_mime_types = $this->types->get_allowed_mime_types(); + $allowed_mime_types = $this->get_allowed_mime_types(); $type = ''; foreach ( $allowed_mime_types as $key => $mime_types ) { if ( \in_array( $mime_type, $mime_types, true ) ) { @@ -429,7 +429,7 @@ public function get_item_schema(): array { return $this->add_additional_fields_schema( $this->schema ); } - $allowed_mime_types = $this->types->get_allowed_mime_types(); + $allowed_mime_types = $this->get_allowed_mime_types(); $types = array_keys( $allowed_mime_types ); $allowed_mime_types = array_merge( ...array_values( $allowed_mime_types ) ); $exts = $this->types->get_file_type_exts( $allowed_mime_types ); @@ -570,4 +570,23 @@ public function stream_headers( $handle, $header ): int { return \strlen( $header ); } + + /** + * Returns a list of allowed mime types per media type (image, audio, video). + * + * @since 1.19.0 + * + * @return array List of allowed mime types. + */ + protected function get_allowed_mime_types(): array { + $mime_type = $this->types->get_allowed_mime_types(); + // TODO: Update once audio elements are supported. + $mime_type['audio'] = []; + // TODO(#10515): Add support hotlinking vtt files. + unset( $mime_type['caption'] ); + // Do not support hotlinking SVGs for security reasons. + unset( $mime_type['vector'] ); + + return $mime_type; + } } diff --git a/includes/REST_API/Stories_Media_Controller.php b/includes/REST_API/Stories_Media_Controller.php index ee0385e5e42a..e56bdc928b3a 100644 --- a/includes/REST_API/Stories_Media_Controller.php +++ b/includes/REST_API/Stories_Media_Controller.php @@ -427,6 +427,11 @@ public function get_item_schema(): array { * @return array Array of supported media types. */ protected function get_media_types(): array { - return $this->types->get_allowed_mime_types(); + $mime_type = $this->types->get_allowed_mime_types(); + // TODO: Update once audio elements are supported. + $mime_type['audio'] = []; + unset( $mime_type['caption'] ); + + return $mime_type; } } diff --git a/packages/media/src/getExtensionFromMimeType.js b/packages/media/src/getExtensionFromMimeType.js deleted file mode 100644 index 65d51a4d9c9e..000000000000 --- a/packages/media/src/getExtensionFromMimeType.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * External dependencies - */ -import mime from 'mime/lite'; - -/** - * Get the file extension for a given mime type. - * - * @param {string} mimeType Mime type. - * @return {string} File extension. - */ -export default function getExtensionFromMimeType(mimeType) { - return mime.getExtension(mimeType); -} diff --git a/packages/media/src/index.js b/packages/media/src/index.js index ee30d8ca5c94..2f367215b09c 100644 --- a/packages/media/src/index.js +++ b/packages/media/src/index.js @@ -28,7 +28,7 @@ export { default as formatMsToHMS } from './formatMsToHMS'; export { default as generateVideoStrip } from './generateVideoStrip'; export { default as getFileNameFromUrl } from './getFileNameFromUrl'; export { default as getFileNameWithExt } from './getFileNameWithExt'; -export { default as getExtensionFromMimeType } from './getExtensionFromMimeType'; +export * from './mimeTypes'; export { default as getFirstFrameOfVideo } from './getFirstFrameOfVideo'; export { default as getImageDimensions } from './getImageDimensions'; export { default as getMsFromHMS } from './getMsFromHMS'; diff --git a/packages/media/src/mimeTypes.js b/packages/media/src/mimeTypes.js new file mode 100644 index 000000000000..d8f2afdde304 --- /dev/null +++ b/packages/media/src/mimeTypes.js @@ -0,0 +1,60 @@ +/* + * Copyright 2021 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies + */ +import mime from 'mime/lite'; + +/** + * Get the file extension for a given mime type. + * + * @param {string} mimeType Mime type. + * @return {string} File extension. + */ +export function getExtensionFromMimeType(mimeType) { + return mime.getExtension(mimeType); +} + +/** + * Get all possible file extensions for a given mime type. + * + * Some mime types can map to multiple file extensions, but + * the mime package does not offer a way to retrieve all of them. + * + * Since this is only needed for user-facing error messages + * (e.g. "You can upload jpeg, png, mp4, ..."), this function + * contains a simple, opinionated, hardcoded list of extensions for the + * most relevant mime types as used in the story editor. + * + * @see https://github.com/broofa/mime/issues/254 + * @param {string} mimeType Mime type. + * @return {string[]} File extension. + */ +export function getExtensionsFromMimeType(mimeType) { + switch (mimeType) { + case 'audio/mpeg': + return ['mp3', 'm3a']; + case 'audio/aac': + return ['aac']; + case 'audio/ogg': + return ['oga', 'ogg']; + case 'image/jpeg': + return ['jpg', 'jpeg']; + default: + return [getExtensionFromMimeType(mimeType)].filter(Boolean); + } +} diff --git a/packages/media/src/test/mimeTypes.js b/packages/media/src/test/mimeTypes.js new file mode 100644 index 000000000000..d8dd2b647331 --- /dev/null +++ b/packages/media/src/test/mimeTypes.js @@ -0,0 +1,37 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Internal dependencies + */ +import { getExtensionsFromMimeType } from '../mimeTypes'; + +describe('getExtensionsFromMimeType', () => { + it('returns an array of file extensions', () => { + expect(getExtensionsFromMimeType('audio/mpeg')).toStrictEqual([ + 'mp3', + 'm3a', + ]); + expect(getExtensionsFromMimeType('audio/mp3')).toStrictEqual(['mp3']); + expect(getExtensionsFromMimeType('audio/aac')).toStrictEqual(['aac']); + expect(getExtensionsFromMimeType('audio/ogg')).toStrictEqual([ + 'oga', + 'ogg', + ]); + expect(getExtensionsFromMimeType('video/mp4')).toStrictEqual(['mp4']); + expect(getExtensionsFromMimeType('invalid/type')).toStrictEqual([]); + }); +}); diff --git a/packages/story-editor/src/app/highlights/quickActions/test/useQuickActions.js b/packages/story-editor/src/app/highlights/quickActions/test/useQuickActions.js index 5f00464d87c8..25eabe6a4e6a 100644 --- a/packages/story-editor/src/app/highlights/quickActions/test/useQuickActions.js +++ b/packages/story-editor/src/app/highlights/quickActions/test/useQuickActions.js @@ -345,10 +345,12 @@ describe('useQuickActions', () => { useStoryTriggersDispatch.mockReturnValue(mockDispatchStoryEvent); mockUseConfig.mockReturnValue({ - allowedFileTypes: [], allowedMimeTypes: { image: [], + vector: [], video: [], + caption: [], + audio: [], }, capabilities: { hasUploadMediaAction: true }, isRTL: true, @@ -968,10 +970,12 @@ describe('MediaPicker', () => { }); mockUseConfig.mockReturnValue({ - allowedFileTypes: ['pepperoni', 'cheese'], allowedMimeTypes: { image: ['image/gif'], + vector: [], video: ['muted'], + caption: [], + audio: [], }, capabilities: { hasUploadMediaAction: true }, isRTL: true, diff --git a/packages/story-editor/src/app/highlights/quickActions/useQuickActions.js b/packages/story-editor/src/app/highlights/quickActions/useQuickActions.js index df630e9ee6e1..8bcab15b1c52 100644 --- a/packages/story-editor/src/app/highlights/quickActions/useQuickActions.js +++ b/packages/story-editor/src/app/highlights/quickActions/useQuickActions.js @@ -27,7 +27,10 @@ import { Icons, } from '@googleforcreators/design-system'; import { trackEvent } from '@googleforcreators/tracking'; -import { resourceList } from '@googleforcreators/media'; +import { + resourceList, + getExtensionsFromMimeType, +} from '@googleforcreators/media'; /** * Internal dependencies @@ -65,9 +68,9 @@ const { export const MediaPicker = ({ render, ...props }) => { const { - allowedFileTypes, allowedMimeTypes: { image: allowedImageMimeTypes, + vector: allowedVectorMimeTypes, video: allowedVideoMimeTypes, }, MediaUpload, @@ -107,7 +110,19 @@ export const MediaPicker = ({ render, ...props }) => { const { showSnackbar } = useSnackbar(); // Media Upload Props - let allowedMimeTypes = [...allowedImageMimeTypes, ...allowedVideoMimeTypes]; + let allowedMimeTypes = useMemo( + () => [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVectorMimeTypes, allowedVideoMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedMimeTypes.map((type) => getExtensionsFromMimeType(type)).flat(), + [allowedMimeTypes] + ); if (isTranscodingEnabled) { allowedMimeTypes = allowedMimeTypes.concat(TRANSCODABLE_MIME_TYPES); } diff --git a/packages/story-editor/src/app/media/local/test/useContextValueProvider.js b/packages/story-editor/src/app/media/local/test/useContextValueProvider.js index ca47dd15208d..8180b2f661d0 100644 --- a/packages/story-editor/src/app/media/local/test/useContextValueProvider.js +++ b/packages/story-editor/src/app/media/local/test/useContextValueProvider.js @@ -215,7 +215,11 @@ describe('useContextValueProvider', () => { const configState = { api: {}, allowedMimeTypes: { + image: [], + vector: [], video: [], + caption: [], + audio: [], }, capabilities: { hasUploadMediaAction: true }, }; diff --git a/packages/story-editor/src/app/media/test/useUploadMedia.js b/packages/story-editor/src/app/media/test/useUploadMedia.js index 12eba8828a80..ee2cc34c92cd 100644 --- a/packages/story-editor/src/app/media/test/useUploadMedia.js +++ b/packages/story-editor/src/app/media/test/useUploadMedia.js @@ -70,7 +70,10 @@ jest.mock('../../config', () => ({ useConfig: jest.fn(() => ({ allowedMimeTypes: { image: [], + vector: [], video: [], + caption: [], + audio: [], }, })), })); diff --git a/packages/story-editor/src/app/uploader/test/useUploader.js b/packages/story-editor/src/app/uploader/test/useUploader.js index c173d12a912c..5fd81313af27 100644 --- a/packages/story-editor/src/app/uploader/test/useUploader.js +++ b/packages/story-editor/src/app/uploader/test/useUploader.js @@ -36,6 +36,7 @@ function setup(args) { const configValue = { api: {}, allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], image: [ 'image/png', 'image/jpeg', @@ -43,16 +44,10 @@ function setup(args) { 'image/gif', 'image/webp', ], + caption: ['text/vtt'], + vector: [], video: ['video/mp4', 'video/webm'], }, - allowedFileTypes: ['png', 'jpeg', 'jpg', 'gif', 'mp4', 'webp', 'webm'], - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], maxUpload: 104857600, capabilities: { hasUploadMediaAction: true, @@ -118,7 +113,7 @@ describe('useUploader', () => { await expect(() => validateFileForUpload({ size: 20000, type: 'video/quicktime' }) ).toThrow( - 'Please choose only png, jpeg, jpg, gif, mp4, webp, or webm to upload.' + 'Please choose only png, jpg, jpeg, gif, webp, mp4, or webm to upload.' ); }); @@ -206,7 +201,9 @@ describe('useUploader', () => { it('formats the error message correctly if there is only one file type supported', async () => { const { actions: { validateFileForUpload }, - } = setup({ allowedFileTypes: ['mp4'] }); + } = setup({ + allowedMimeTypes: { image: [], video: ['video/mp4'], vector: [] }, + }); await expect(() => validateFileForUpload({ size: 20000, type: 'video/quicktime' }) @@ -216,7 +213,7 @@ describe('useUploader', () => { it('formats the error message correctly if no file types are supported', async () => { const { actions: { validateFileForUpload }, - } = setup({ allowedFileTypes: [] }); + } = setup({ allowedMimeTypes: { image: [], video: [], vector: [] } }); await expect(() => validateFileForUpload({ size: 20000, type: 'video/quicktime' }) diff --git a/packages/story-editor/src/app/uploader/useUploader.js b/packages/story-editor/src/app/uploader/useUploader.js index e3d17b663f94..74dfc0905cb6 100644 --- a/packages/story-editor/src/app/uploader/useUploader.js +++ b/packages/story-editor/src/app/uploader/useUploader.js @@ -19,7 +19,10 @@ */ import { useCallback, useMemo } from '@googleforcreators/react'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; -import { getFileName } from '@googleforcreators/media'; +import { + getFileName, + getExtensionsFromMimeType, +} from '@googleforcreators/media'; /** * Internal dependencies @@ -40,14 +43,23 @@ function useUploader() { maxUpload, allowedMimeTypes: { image: allowedImageMimeTypes, + vector: allowedVectorMimeTypes, video: allowedVideoMimeTypes, }, - allowedFileTypes, capabilities: { hasUploadMediaAction }, } = useConfig(); const allowedMimeTypes = useMemo( - () => [...allowedImageMimeTypes, ...allowedVideoMimeTypes], - [allowedImageMimeTypes, allowedVideoMimeTypes] + () => [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVectorMimeTypes, allowedVideoMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedMimeTypes.map((type) => getExtensionsFromMimeType(type)).flat(), + [allowedMimeTypes] ); const isValidType = useCallback( diff --git a/packages/story-editor/src/components/canvas/test/_utils.js b/packages/story-editor/src/components/canvas/test/_utils.js index 4aeaacd634e0..1f44a1a8bcd9 100644 --- a/packages/story-editor/src/components/canvas/test/_utils.js +++ b/packages/story-editor/src/components/canvas/test/_utils.js @@ -54,7 +54,10 @@ export function TestFrameElement({ const configContext = { ...inputConfigContext, allowedMimeTypes: { + audio: [], image: [], + caption: [], + vector: [], video: [], ...(inputConfigContext && inputConfigContext.allowedMimeTypes), }, @@ -122,7 +125,10 @@ export function TestDisplayElement({ const configContext = { ...inputConfigContext, allowedMimeTypes: { + audio: [], image: [], + caption: [], + vector: [], video: [], ...(inputConfigContext && inputConfigContext.allowedMimeTypes), }, diff --git a/packages/story-editor/src/components/form/linkIcon.js b/packages/story-editor/src/components/form/linkIcon.js index 5a17b5b0ed4a..5edf3ad26c6b 100644 --- a/packages/story-editor/src/components/form/linkIcon.js +++ b/packages/story-editor/src/components/form/linkIcon.js @@ -19,6 +19,7 @@ */ import styled from 'styled-components'; import { useMemo } from '@googleforcreators/react'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; import PropTypes from 'prop-types'; /** @@ -36,10 +37,18 @@ const StyledMedia = styled(Media)` function LinkIcon({ handleChange, icon, isLoading = false, ...rest }) { const { - allowedImageMimeTypes, - allowedImageFileTypes, + allowedMimeTypes: { image: allowedImageMimeTypes }, capabilities: { hasUploadMediaAction }, } = useConfig(); + + const allowedImageFileTypes = useMemo( + () => + allowedImageMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedImageMimeTypes] + ); + const iconErrorMessage = useMemo(() => { let message = __( 'No image file types are currently supported.', diff --git a/packages/story-editor/src/components/form/mediaUploadButton.js b/packages/story-editor/src/components/form/mediaUploadButton.js index 63259cd868c3..bd1df4fa0813 100644 --- a/packages/story-editor/src/components/form/mediaUploadButton.js +++ b/packages/story-editor/src/components/form/mediaUploadButton.js @@ -19,6 +19,7 @@ */ import { useSnackbar } from '@googleforcreators/design-system'; import { useCallback, useMemo } from '@googleforcreators/react'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; import PropTypes from 'prop-types'; @@ -31,14 +32,30 @@ import { TRANSCODABLE_MIME_TYPES } from '../../app/media'; function MediaUploadButton({ onInsert, renderButton, buttonInsertText }) { const { - allowedFileTypes, allowedMimeTypes: { image: allowedImageMimeTypes, + vector: allowedVectorMimeTypes, video: allowedVideoMimeTypes, }, MediaUpload, } = useConfig(); + const allowedUploadMimeTypes = useMemo( + () => [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVectorMimeTypes, allowedVideoMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedUploadMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedUploadMimeTypes] + ); + const { canTranscodeResource, resetWithFetch, @@ -72,11 +89,21 @@ function MediaUploadButton({ onInsert, renderButton, buttonInsertText }) { return [ ...TRANSCODABLE_MIME_TYPES, ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, ...allowedVideoMimeTypes, ]; } - return [...allowedImageMimeTypes, ...allowedVideoMimeTypes]; - }, [allowedImageMimeTypes, allowedVideoMimeTypes, isTranscodingEnabled]); + return [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ]; + }, [ + allowedImageMimeTypes, + allowedVectorMimeTypes, + allowedVideoMimeTypes, + isTranscodingEnabled, + ]); const transcodableMimeTypes = useMemo(() => { return TRANSCODABLE_MIME_TYPES.filter( diff --git a/packages/story-editor/src/components/library/panes/media/local/hotlink/hotlinkModal.js b/packages/story-editor/src/components/library/panes/media/local/hotlink/hotlinkModal.js index 588d1e0338b0..a7960d782069 100644 --- a/packages/story-editor/src/components/library/panes/media/local/hotlink/hotlinkModal.js +++ b/packages/story-editor/src/components/library/panes/media/local/hotlink/hotlinkModal.js @@ -17,6 +17,7 @@ * External dependencies */ import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; import { withProtocol } from '@googleforcreators/url'; import { Input } from '@googleforcreators/design-system'; import { @@ -24,6 +25,7 @@ import { useRef, useLayoutEffect, useCallback, + useMemo, } from '@googleforcreators/react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; @@ -43,10 +45,29 @@ const InputWrapper = styled.form` `; function HotlinkModal({ isOpen, onClose }) { - const { allowedFileTypes } = useConfig(); + const { + allowedMimeTypes: { + image: allowedImageMimeTypes, + vector: allowedVectorMimeTypes, + video: allowedVideoMimeTypes, + }, + } = useConfig(); const [errorMsg, setErrorMsg] = useState(false); const inputRef = useRef(null); + const allowedMimeTypes = useMemo( + () => [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVectorMimeTypes, allowedVideoMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedMimeTypes.map((type) => getExtensionsFromMimeType(type)).flat(), + [allowedMimeTypes] + ); useLayoutEffect(() => { // Wait one tick to ensure the input has been loaded. const timeout = setTimeout(() => { diff --git a/packages/story-editor/src/components/library/panes/media/local/hotlink/useInsert.js b/packages/story-editor/src/components/library/panes/media/local/hotlink/useInsert.js index ce8dedff7313..929fcb6cb9fb 100644 --- a/packages/story-editor/src/components/library/panes/media/local/hotlink/useInsert.js +++ b/packages/story-editor/src/components/library/panes/media/local/hotlink/useInsert.js @@ -17,7 +17,7 @@ /** * External dependencies */ -import { useCallback, useState } from '@googleforcreators/react'; +import { useCallback, useState, useMemo } from '@googleforcreators/react'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; import { getImageFromVideo, @@ -25,6 +25,7 @@ import { getVideoLength, preloadVideo, hasVideoGotAudio, + getExtensionsFromMimeType, } from '@googleforcreators/media'; import { v4 as uuidv4 } from 'uuid'; import { trackError, trackEvent } from '@googleforcreators/tracking'; @@ -69,8 +70,26 @@ function useInsert({ link, setLink, setErrorMsg, onClose }) { })); const { capabilities: { hasUploadMediaAction }, - allowedFileTypes, + allowedMimeTypes: { + image: allowedImageMimeTypes, + vector: allowedVectorMimeTypes, + video: allowedVideoMimeTypes, + }, } = useConfig(); + + const allowedMimeTypes = useMemo( + () => [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVectorMimeTypes, allowedVideoMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedMimeTypes.map((type) => getExtensionsFromMimeType(type)).flat(), + [allowedMimeTypes] + ); const { actions: { getHotlinkInfo }, } = useAPI(); diff --git a/packages/story-editor/src/components/library/panes/media/local/stories/hotlinkDialog.js b/packages/story-editor/src/components/library/panes/media/local/stories/hotlinkDialog.js index c8811667d18c..1dfa5a6dc744 100644 --- a/packages/story-editor/src/components/library/panes/media/local/stories/hotlinkDialog.js +++ b/packages/story-editor/src/components/library/panes/media/local/stories/hotlinkDialog.js @@ -42,6 +42,7 @@ export const _default = ({ insertElement, ...args }) => { }; const configContext = { allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], image: [ 'image/png', 'image/jpeg', @@ -49,10 +50,10 @@ export const _default = ({ insertElement, ...args }) => { 'image/gif', 'image/webp', ], - audio: [], + caption: ['text/vtt'], + vector: [], video: ['video/mp4', 'video/webm'], }, - allowedFileTypes: ['png', 'jpeg', 'jpg', 'gif', 'mp4', 'webp', 'webm'], capabilities: { hasUploadMediaAction: true }, }; return ( diff --git a/packages/story-editor/src/components/library/test/_utils/index.js b/packages/story-editor/src/components/library/test/_utils/index.js index 912155c5bf48..c80b7269daa9 100644 --- a/packages/story-editor/src/components/library/test/_utils/index.js +++ b/packages/story-editor/src/components/library/test/_utils/index.js @@ -33,6 +33,7 @@ export async function arrange({ mediaResponse = [] }) { const config = { api: {}, allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], image: [ 'image/png', 'image/jpeg', @@ -40,16 +41,10 @@ export async function arrange({ mediaResponse = [] }) { 'image/gif', 'image/webp', ], + caption: ['text/vtt'], + vector: [], video: ['video/mp4', 'video/webm'], }, - allowedFileTypes: ['png', 'jpeg', 'jpg', 'gif', 'mp4', 'webp', 'webm'], - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], capabilities: { hasUploadMediaAction: true, }, diff --git a/packages/story-editor/src/components/panels/design/captions/test/captions.js b/packages/story-editor/src/components/panels/design/captions/test/captions.js index a1addcd4c46f..8ef39ee74596 100644 --- a/packages/story-editor/src/components/panels/design/captions/test/captions.js +++ b/packages/story-editor/src/components/panels/design/captions/test/captions.js @@ -43,6 +43,7 @@ describe('Panels/Captions', () => { capabilities: { hasUploadMediaAction: true, }, + allowedMimeTypes: { caption: ['text/vtt'] }, ...config, MediaUpload, }; diff --git a/packages/story-editor/src/components/panels/design/link/test/link.js b/packages/story-editor/src/components/panels/design/link/test/link.js index 1d1118c6dc7d..3ef7024d492e 100644 --- a/packages/story-editor/src/components/panels/design/link/test/link.js +++ b/packages/story-editor/src/components/panels/design/link/test/link.js @@ -42,13 +42,19 @@ function arrange(selectedElements) { capabilities: { hasUploadMediaAction: true, }, - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], + allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ], + caption: ['text/vtt'], + vector: [], + video: ['video/mp4', 'video/webm'], + }, MediaUpload, }; diff --git a/packages/story-editor/src/components/panels/design/pageBackground/test/pageBackground.js b/packages/story-editor/src/components/panels/design/pageBackground/test/pageBackground.js index 02987d603b42..b39421f7e99d 100644 --- a/packages/story-editor/src/components/panels/design/pageBackground/test/pageBackground.js +++ b/packages/story-editor/src/components/panels/design/pageBackground/test/pageBackground.js @@ -47,17 +47,18 @@ function MediaUpload({ render, onSelect }) { function arrange(selectedElements) { const configValue = { - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], allowedMimeTypes: { - image: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], - video: [], + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ], + caption: ['text/vtt'], + vector: [], + video: ['video/mp4', 'video/webm'], }, capabilities: { hasUploadMediaAction: true, diff --git a/packages/story-editor/src/components/panels/design/pageBackgroundAudio/test/backgroundAudio.js b/packages/story-editor/src/components/panels/design/pageBackgroundAudio/test/backgroundAudio.js index 45caacdb9d1c..4cc14b0138b3 100644 --- a/packages/story-editor/src/components/panels/design/pageBackgroundAudio/test/backgroundAudio.js +++ b/packages/story-editor/src/components/panels/design/pageBackgroundAudio/test/backgroundAudio.js @@ -44,13 +44,19 @@ function arrange({ capabilities: { hasUploadMediaAction, }, - allowedAudioMimeTypes: [ - 'audio/mpeg', - 'audio/aac', - 'audio/wav', - 'audio/ogg', - ], - allowedAudioFileTypes: ['mp3', 'aac', 'wav', 'ogg'], + allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ], + caption: ['text/vtt'], + vector: [], + video: ['video/mp4', 'video/webm'], + }, MediaUpload, }; diff --git a/packages/story-editor/src/components/panels/design/videoAccessibility/test/videoAccessibility.js b/packages/story-editor/src/components/panels/design/videoAccessibility/test/videoAccessibility.js index 6e8985300325..8a9b1dffa9f8 100644 --- a/packages/story-editor/src/components/panels/design/videoAccessibility/test/videoAccessibility.js +++ b/packages/story-editor/src/components/panels/design/videoAccessibility/test/videoAccessibility.js @@ -38,13 +38,19 @@ function MediaUpload({ render, onSelect }) { function arrange(selectedElements) { const configValue = { - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], + allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ], + caption: ['text/vtt'], + vector: [], + video: ['video/mp4', 'video/webm'], + }, capabilities: { hasUploadMediaAction: true, }, diff --git a/packages/story-editor/src/components/panels/design/videoAccessibility/videoAccessibility.js b/packages/story-editor/src/components/panels/design/videoAccessibility/videoAccessibility.js index 81465e07dbf9..e345c1a00c39 100644 --- a/packages/story-editor/src/components/panels/design/videoAccessibility/videoAccessibility.js +++ b/packages/story-editor/src/components/panels/design/videoAccessibility/videoAccessibility.js @@ -19,6 +19,7 @@ */ import PropTypes from 'prop-types'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; import { useCallback, useMemo } from '@googleforcreators/react'; import styled from 'styled-components'; @@ -73,11 +74,18 @@ function VideoAccessibilityPanel({ selectedElements, pushUpdate }) { const rawPoster = getCommonValue(selectedElements, 'poster'); const poster = getCommonValue(selectedElements, 'poster', resource.poster); const { - allowedImageMimeTypes, - allowedImageFileTypes, + allowedMimeTypes: { image: allowedImageMimeTypes }, capabilities: { hasUploadMediaAction }, } = useConfig(); + const allowedImageFileTypes = useMemo( + () => + allowedImageMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedImageMimeTypes] + ); + const handleChangePoster = useCallback( /** * Handle video poster change. diff --git a/packages/story-editor/src/components/panels/document/backgroundAudio/test/backgroundAudio.js b/packages/story-editor/src/components/panels/document/backgroundAudio/test/backgroundAudio.js index ff2a871e3704..4fc727e65986 100644 --- a/packages/story-editor/src/components/panels/document/backgroundAudio/test/backgroundAudio.js +++ b/packages/story-editor/src/components/panels/document/backgroundAudio/test/backgroundAudio.js @@ -39,13 +39,19 @@ function arrange({ backgroundAudio, hasUploadMediaAction = true } = {}) { capabilities: { hasUploadMediaAction, }, - allowedAudioMimeTypes: [ - 'audio/mpeg', - 'audio/aac', - 'audio/wav', - 'audio/ogg', - ], - allowedAudioFileTypes: ['mp3', 'aac', 'wav', 'ogg'], + allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ], + caption: ['text/vtt'], + vector: [], + video: ['video/mp4', 'video/webm'], + }, MediaUpload, }; diff --git a/packages/story-editor/src/components/panels/shared/backgroundAudioPanelContent.js b/packages/story-editor/src/components/panels/shared/backgroundAudioPanelContent.js index 08b717d4bfb2..8779d6eb0984 100644 --- a/packages/story-editor/src/components/panels/shared/backgroundAudioPanelContent.js +++ b/packages/story-editor/src/components/panels/shared/backgroundAudioPanelContent.js @@ -28,8 +28,11 @@ import { themeHelpers, } from '@googleforcreators/design-system'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; -import { useCallback } from '@googleforcreators/react'; -import { ResourcePropTypes } from '@googleforcreators/media'; +import { useCallback, useMemo } from '@googleforcreators/react'; +import { + ResourcePropTypes, + getExtensionsFromMimeType, +} from '@googleforcreators/media'; import { v4 as uuidv4 } from 'uuid'; /** @@ -64,12 +67,17 @@ function BackgroundAudioPanelContent({ audioId, }) { const { - allowedAudioMimeTypes, - allowedAudioFileTypes, + allowedMimeTypes: { audio: allowedAudioMimeTypes }, capabilities: { hasUploadMediaAction }, MediaUpload, } = useConfig(); - + const allowedAudioFileTypes = useMemo( + () => + allowedAudioMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedAudioMimeTypes] + ); const { resource, tracks = [], loop = true } = backgroundAudio || {}; const onSelectErrorMessage = sprintf( diff --git a/packages/story-editor/src/components/panels/shared/captionsPanelContent.js b/packages/story-editor/src/components/panels/shared/captionsPanelContent.js index 615d2073c932..a8f455a2dddc 100644 --- a/packages/story-editor/src/components/panels/shared/captionsPanelContent.js +++ b/packages/story-editor/src/components/panels/shared/captionsPanelContent.js @@ -76,11 +76,15 @@ function CaptionsPanelContent({ clearFileText = __('Remove file', 'web-stories'), }) { const { + allowedMimeTypes: { caption: allowedCaptionMimeTypes = [] }, capabilities: { hasUploadMediaAction }, MediaUpload, } = useConfig(); - if (!hasUploadMediaAction && !tracks.length) { + if ( + (!hasUploadMediaAction && !tracks?.length) || + !allowedCaptionMimeTypes?.length + ) { return null; } @@ -128,7 +132,7 @@ function CaptionsPanelContent({ 'Please choose a VTT file to use as caption.', 'web-stories' )} - type={['text/vtt']} + type={allowedCaptionMimeTypes} title={captionText} buttonInsertText={__('Select caption', 'web-stories')} render={renderUploadButton} diff --git a/packages/story-editor/src/components/publishModal/content/storyPreview.js b/packages/story-editor/src/components/publishModal/content/storyPreview.js index c0de1fc29dc6..3b2b58826996 100644 --- a/packages/story-editor/src/components/publishModal/content/storyPreview.js +++ b/packages/story-editor/src/components/publishModal/content/storyPreview.js @@ -24,9 +24,10 @@ import { THEME_CONSTANTS, MEDIA_VARIANTS, } from '@googleforcreators/design-system'; -import { useCallback } from '@googleforcreators/react'; +import { useCallback, useMemo } from '@googleforcreators/react'; import { PAGE_RATIO } from '@googleforcreators/units'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; /** * Internal dependencies */ @@ -191,25 +192,22 @@ const StoryPreview = () => { }) ); - const { - allowedImageFileTypes, - allowedImageMimeTypes, - hasUploadMediaAction, - publisher, - } = useConfig( - ({ - allowedImageFileTypes, - allowedImageMimeTypes, - capabilities, - metadata, - }) => ({ - allowedImageFileTypes, - allowedImageMimeTypes, + const { allowedImageMimeTypes, hasUploadMediaAction, publisher } = useConfig( + ({ allowedMimeTypes, capabilities, metadata }) => ({ + allowedImageMimeTypes: allowedMimeTypes?.image, hasUploadMediaAction: capabilities?.hasUploadMediaAction, publisher: metadata?.publisher, }) ); + const allowedImageFileTypes = useMemo( + () => + allowedImageMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedImageMimeTypes] + ); + // Honor 2:3 aspect ratio that cover previews have const mediaWidth = 232; const mediaHeight = Math.round(mediaWidth / PAGE_RATIO); diff --git a/packages/story-editor/src/components/publishModal/test/content.js b/packages/story-editor/src/components/publishModal/test/content.js index f3b84bc09944..695a3544d1e7 100644 --- a/packages/story-editor/src/components/publishModal/test/content.js +++ b/packages/story-editor/src/components/publishModal/test/content.js @@ -45,8 +45,7 @@ describe('publishModal/content', () => { }); const configContextValue = { - allowedImageFileTypes: [], - allowedImageMimeTypes: [], + allowedMimeTypes: { image: [] }, metadata: { publisher: '', }, diff --git a/packages/story-editor/src/components/publishModal/test/storyPreview.js b/packages/story-editor/src/components/publishModal/test/storyPreview.js index fae6ae3f0b29..5aaad9bf47e3 100644 --- a/packages/story-editor/src/components/publishModal/test/storyPreview.js +++ b/packages/story-editor/src/components/publishModal/test/storyPreview.js @@ -38,7 +38,6 @@ describe('publishModal/storyPreview', () => { const view = (props) => { const { - allowedImageFileTypes = [], allowedImageMimeTypes = [], featuredMedia = '', hasUploadMediaAction = false, @@ -50,8 +49,7 @@ describe('publishModal/storyPreview', () => { return renderWithTheme( [ + ...allowedImageMimeTypes, + ...allowedVectorMimeTypes, + ...allowedVideoMimeTypes, + ], + [allowedImageMimeTypes, allowedVideoMimeTypes, allowedVectorMimeTypes] + ); + const allowedFileTypes = useMemo( + () => + allowedMimeTypes.map((type) => getExtensionsFromMimeType(type)).flat(), + [allowedMimeTypes] + ); let description = __('No file types are currently supported.', 'web-stories'); diff --git a/packages/story-editor/src/getDefaultConfig.js b/packages/story-editor/src/getDefaultConfig.js index fbd03a1c10bb..1ab620ad2c00 100644 --- a/packages/story-editor/src/getDefaultConfig.js +++ b/packages/story-editor/src/getDefaultConfig.js @@ -61,32 +61,11 @@ const getDefaultConfig = () => ({ weekdaysInitials: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], weekStartsOn: 1, }, - allowedFileTypes: [ - 'gif', - 'jpe', - 'jpeg', - 'jpg', - 'm4v', - 'mp4', - 'png', - 'svg', - 'svgz', - 'webm', - 'webp', - ], - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png', 'webp'], - allowedImageMimeTypes: ['image/webp', 'image/png', 'image/jpeg', 'image/gif'], - allowedAudioFileTypes: ['aac', 'm4a', 'm4b', 'mp3', 'oga', 'ogg', 'wav'], - allowedAudioMimeTypes: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], allowedMimeTypes: { - image: [ - 'image/webp', - 'image/png', - 'image/jpeg', - 'image/gif', - 'image/svg+xml', - ], - audio: [], + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], + image: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'], + caption: ['text/vtt'], + vector: ['image/svg+xml'], video: ['video/mp4', 'video/webm'], }, isRTL: false, diff --git a/packages/story-editor/src/karma/fixture/fixture.js b/packages/story-editor/src/karma/fixture/fixture.js index 5a2ab1c2c773..94d8b755a7c3 100644 --- a/packages/story-editor/src/karma/fixture/fixture.js +++ b/packages/story-editor/src/karma/fixture/fixture.js @@ -92,14 +92,12 @@ export const FIXTURE_DEFAULT_CONFIG = { storyId: 1, api: {}, allowedMimeTypes: { + audio: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], image: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'], + caption: ['text/vtt'], + vector: ['image/svg+xml'], video: ['video/mp4', 'video/webm'], }, - allowedFileTypes: ['png', 'jpeg', 'jpg', 'gif', 'mp4', 'webp', 'webm'], - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], - allowedAudioFileTypes: ['mp3', 'aac', 'wav', 'ogg'], - allowedAudioMimeTypes: ['audio/mpeg', 'audio/aac', 'audio/wav', 'audio/ogg'], capabilities: { hasUploadMediaAction: true, }, diff --git a/packages/wp-story-editor/src/components/documentPane/publish/publish.js b/packages/wp-story-editor/src/components/documentPane/publish/publish.js index 75d929e58c58..ebabe9ed39bd 100644 --- a/packages/wp-story-editor/src/components/documentPane/publish/publish.js +++ b/packages/wp-story-editor/src/components/documentPane/publish/publish.js @@ -21,10 +21,12 @@ import PropTypes from 'prop-types'; import { useState, useEffect, + useMemo, useCallback, forwardRef, } from '@googleforcreators/react'; import styled from 'styled-components'; +import { getExtensionsFromMimeType } from '@googleforcreators/media'; import { __, sprintf, translateToExclusiveList } from '@googleforcreators/i18n'; import { Link, @@ -126,13 +128,20 @@ function PublishPanel({ nameOverride }) { const { getPublisherLogos, addPublisherLogo } = apiCallbacks; const { - allowedImageMimeTypes, - allowedImageFileTypes, + allowedMimeTypes: { image: allowedImageMimeTypes }, dashboardSettingsLink, capabilities: { hasUploadMediaAction, canManageSettings }, MediaUpload, } = useConfig(); + const allowedImageFileTypes = useMemo( + () => + allowedImageMimeTypes + .map((type) => getExtensionsFromMimeType(type)) + .flat(), + [allowedImageMimeTypes] + ); + const [publisherLogos, setPublisherLogos] = useState([]); useEffect(() => { diff --git a/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js b/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js index 83d8d2060bca..3cdec96270db 100644 --- a/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js +++ b/packages/wp-story-editor/src/components/documentPane/publish/test/publish.js @@ -68,13 +68,9 @@ function arrange( const config = { capabilities, - allowedImageFileTypes: ['gif', 'jpe', 'jpeg', 'jpg', 'png'], - allowedImageMimeTypes: [ - 'image/png', - 'image/jpeg', - 'image/jpg', - 'image/gif', - ], + allowedMimeTypes: { + image: ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'], + }, apiCallbacks: { getAuthors: jest.fn().mockResolvedValue({}), }, diff --git a/phpunit-integration-multisite.xml.dist b/phpunit-integration-multisite.xml.dist index b98359f0b4fa..80623e2c75b7 100644 --- a/phpunit-integration-multisite.xml.dist +++ b/phpunit-integration-multisite.xml.dist @@ -23,6 +23,12 @@ + + + ms-excluded + + + ./ diff --git a/tests/phpunit/integration/tests/Admin/Dashboard.php b/tests/phpunit/integration/tests/Admin/Dashboard.php index 35324543fde4..f0092c2dfdde 100644 --- a/tests/phpunit/integration/tests/Admin/Dashboard.php +++ b/tests/phpunit/integration/tests/Admin/Dashboard.php @@ -17,15 +17,6 @@ namespace Google\Web_Stories\Tests\Integration\Admin; -use Google\Web_Stories\Admin\Google_Fonts; -use Google\Web_Stories\Assets; -use Google\Web_Stories\Context; -use Google\Web_Stories\Decoder; -use Google\Web_Stories\Font_Post_Type; -use Google\Web_Stories\Integrations\Site_Kit; -use Google\Web_Stories\Locale; -use Google\Web_Stories\Settings; -use Google\Web_Stories\Story_Post_Type; use Google\Web_Stories\Tests\Integration\Capabilities_Setup; use Google\Web_Stories\Tests\Integration\DependencyInjectedTestCase; @@ -162,7 +153,7 @@ public function test_enqueue_assets(): void { $experiments->method( 'get_experiment_statuses' ) ->willReturn( [] ); - $assets = $this->getMockBuilder( Assets::class )->setMethods( [ 'get_asset_metadata' ] )->getMock(); + $assets = $this->getMockBuilder( \Google\Web_Stories\Assets::class )->setMethods( [ 'get_asset_metadata' ] )->getMock(); $assets->method( 'get_asset_metadata' ) ->willReturn( [ @@ -174,20 +165,26 @@ public function test_enqueue_assets(): void { ] ); - $post_type = new Story_Post_Type( new Settings(), $experiments ); - $font_post_type = new Font_Post_Type( $post_type ); + $site_kit = $this->injector->make( \Google\Web_Stories\Integrations\Site_Kit::class ); + $decoder = $this->injector->make( \Google\Web_Stories\Decoder::class ); + $locale = $this->injector->make( \Google\Web_Stories\Locale::class ); + $google_fonts = $this->injector->make( \Google\Web_Stories\Admin\Google_Fonts::class ); + $font_post_type = $this->injector->make( \Google\Web_Stories\Font_Post_Type::class ); + $post_type = $this->injector->make( \Google\Web_Stories\Story_Post_Type::class ); + $context = $this->injector->make( \Google\Web_Stories\Context::class ); + $types = $this->injector->make( \Google\Web_Stories\Media\Types::class ); $this->instance = new \Google\Web_Stories\Admin\Dashboard( $experiments, - $this->createMock( Site_Kit::class ), - $this->createMock( Decoder::class ), - $this->createMock( Locale::class ), - ( new Google_Fonts() ), + $site_kit, + $decoder, + $locale, + $google_fonts, $assets, $font_post_type, $post_type, - new Context( $post_type ), - $this->createMock( \Google\Web_Stories\Media\Types::class ) + $context, + $types ); $this->instance->add_menu_page(); diff --git a/tests/phpunit/integration/tests/Media/SVG.php b/tests/phpunit/integration/tests/Media/SVG.php index 5629ef9dd4ae..861e0efb2fc3 100644 --- a/tests/phpunit/integration/tests/Media/SVG.php +++ b/tests/phpunit/integration/tests/Media/SVG.php @@ -128,8 +128,8 @@ public function test_mime_types_add_svg(): void { public function test_web_stories_allowed_mime_types(): void { $allowed_mime_types = $this->instance->web_stories_allowed_mime_types( [] ); - $this->assertArrayHasKey( 'image', $allowed_mime_types ); - $this->assertContains( 'image/svg+xml', $allowed_mime_types['image'] ); + $this->assertArrayHasKey( 'vector', $allowed_mime_types ); + $this->assertContains( 'image/svg+xml', $allowed_mime_types['vector'] ); } /** diff --git a/tests/phpunit/integration/tests/Media/Types.php b/tests/phpunit/integration/tests/Media/Types.php index 0ac6ecc7a6bc..27a8afce7ed8 100644 --- a/tests/phpunit/integration/tests/Media/Types.php +++ b/tests/phpunit/integration/tests/Media/Types.php @@ -38,31 +38,46 @@ public function set_up(): void { /** * @covers ::get_allowed_mime_types + * @group ms-excluded */ public function test_get_allowed_mime_types(): void { if ( $this->supportsWebP() ) { $expected = [ - 'image' => [ + 'image' => [ 'image/webp', 'image/png', 'image/jpeg', 'image/gif', ], - 'audio' => [], - 'video' => [ + 'audio' => [ + 'audio/mpeg', + 'audio/aac', + 'audio/wav', + 'audio/ogg', + ], + 'caption' => [ 'text/vtt' ], + 'vector' => [], + 'video' => [ 'video/mp4', 'video/webm', ], ]; } else { $expected = [ - 'image' => [ + 'image' => [ 'image/png', 'image/jpeg', 'image/gif', ], - 'audio' => [], - 'video' => [ + 'audio' => [ + 'audio/mpeg', + 'audio/aac', + 'audio/wav', + 'audio/ogg', + ], + 'caption' => [ 'text/vtt' ], + 'vector' => [], + 'video' => [ 'video/mp4', 'video/webm', ], @@ -75,26 +90,52 @@ public function test_get_allowed_mime_types(): void { } /** - * @covers ::get_allowed_image_mime_types + * @group ms-required + * @covers ::get_allowed_mime_types */ - public function test_get_allowed_image_mime_types(): void { + public function test_get_allowed_mime_types_multisite(): void { if ( $this->supportsWebP() ) { $expected = [ - 'image/webp', - 'image/png', - 'image/jpeg', - 'image/gif', + 'image' => [ + 'image/webp', + 'image/png', + 'image/jpeg', + 'image/gif', + ], + 'audio' => [ + 'audio/mpeg', + 'audio/wav', + 'audio/ogg', + ], + 'caption' => [ 'text/vtt' ], + 'vector' => [], + 'video' => [ + 'video/mp4', + 'video/webm', + ], ]; } else { - $expected = [ - 'image/png', - 'image/jpeg', - 'image/gif', + 'image' => [ + 'image/png', + 'image/jpeg', + 'image/gif', + ], + 'audio' => [ + 'audio/mpeg', + 'audio/wav', + 'audio/ogg', + ], + 'caption' => [ 'text/vtt' ], + 'vector' => [], + 'video' => [ + 'video/mp4', + 'video/webm', + ], ]; } - $actual = $this->instance->get_allowed_image_mime_types(); + $actual = $this->instance->get_allowed_mime_types(); $this->assertEqualSets( $expected, $actual ); }