Server IP : 209.38.156.173 / Your IP : 216.73.216.122 [ Web Server : Apache/2.4.52 (Ubuntu) System : Linux lakekumayuhotel 5.15.0-136-generic #147-Ubuntu SMP Sat Mar 15 15:53:30 UTC 2025 x86_64 User : root ( 0) PHP Version : 8.1.2-1ubuntu2.22 Disable Function : NONE Domains : 2 Domains MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/html/wp-content/plugins/jetpack/extensions/shared/external-media/media-service/ |
Upload File : |
import jetpackAnalytics from '@automattic/jetpack-analytics'; import apiFetch from '@wordpress/api-fetch'; import { dispatch, select } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { addQueryArgs } from '@wordpress/url'; import { waitFor } from '../../wait-for'; import { store as mediaStore } from '../store'; import { MediaSource } from './types'; // Pexels constants const PEXELS_ID = 'pexels'; const PEXELS_NAME = __( 'Pexels Free Photos', 'jetpack' ); const PEXELS_SEARCH_PLACEHOLDER = __( 'Search Pexels Free Photos', 'jetpack' ); const DEFAULT_PEXELS_SEARCH: MediaSearch = { per_page: 10, search: 'mountain', }; // Google Photos constants const GOOGLE_PHOTOS_ID = 'google_photos'; const GOOGLE_PHOTOS_NAME = __( 'Google Photos', 'jetpack' ); const GOOGLE_PHOTOS_SEARCH_PLACEHOLDER = __( 'Search Google Photos', 'jetpack' ); const DEFAULT_GOOGLE_PHOTOS_SEARCH: MediaSearch = { per_page: 25, search: '', }; /** * External media endpoints. */ // eslint-disable-next-line no-shadow enum WpcomMediaEndpoints { List = '/wpcom/v2/external-media/list/', } /** * WPCOM media type of the WPCOM Media Api. */ // eslint-disable-next-line no-shadow enum WpcomMediaItemType { Image = 'image', Video = 'video', } /** * Gutenberg media category search DTO. */ type MediaSearch = { per_page: number; search: string; }; /** * Gutenberg media item DTO. */ type MediaItem = { caption: string; previewUrl: string; title: string; url: string; }; /** * WPCOM media list item DTO. */ type WpcomMediaItem = { URL: string; caption: string; // Sometimes the title is null, so we need to handle that case. title: string | null; name: string | null; file: string; thumbnails: { thumbnail: string; }; type: WpcomMediaItemType; }; /** * WPCOM media list response DTO. */ type WpcomMediaResponse = { found: number; media: WpcomMediaItem[]; }; /** * Get media URL for a given MediaSource. * * @param {MediaSource} source - MediaSource to get URL for. * @param {MediaSearch} mediaSearch - MediaCategorySearch to filter for. * @returns {string} Media URL. */ const getMediaApiUrl = ( source: MediaSource, mediaSearch: MediaSearch ) => addQueryArgs( `${ WpcomMediaEndpoints.List }${ source }`, { ...( mediaSearch.search && { search: mediaSearch.search } ), number: mediaSearch.per_page || 25, path: 'recent', } ); /** * Maps a WPCOM media item to a Gutenberg media item. * * @param {WpcomMediaItem} item - WPCOM media list item to map. * @returns {MediaItem} Mapped media category item. */ const mapWpcomMediaToMedia = ( item: WpcomMediaItem ): MediaItem => ( { caption: item?.caption ?? '', previewUrl: item.thumbnails.thumbnail, title: item?.title ?? item?.name ?? item.file, url: item.URL, } ); /** * Builds a Gutenberg media category object. * * @param {string} name - Name of the media category. * @param {string} label - Label of the media category. * @param {string} searchPlaceholder - Search placeholder of the media category. * @param {MediaSource} source - MediaSource of the media category. * @param {MediaSearch} defaultSearch - Default search of the media category. * @returns {object} Media category object. */ const buildMediaCategory = ( name: string, label: string, searchPlaceholder: string, source: MediaSource, defaultSearch: MediaSearch ) => ( { name: name, labels: { name: label, search_items: searchPlaceholder, }, mediaType: 'image', fetch: async ( mediaCategorySearch: MediaSearch ) => await apiFetch( { path: getMediaApiUrl( source, { per_page: defaultSearch.per_page, search: mediaCategorySearch?.search === '' ? defaultSearch.search : mediaCategorySearch.search, } ), method: 'GET', } ) .then( ( response: WpcomMediaResponse ) => { const mediaItems = response.media .filter( wpcomMediaItem => wpcomMediaItem.type === WpcomMediaItemType.Image ) .map( mapWpcomMediaToMedia ); jetpackAnalytics.tracks.recordEvent( 'jetpack_editor_media_inserter_external_source', { mediaSource: source.toString(), results: mediaItems.length, search: mediaCategorySearch?.search === '' ? defaultSearch.search : mediaCategorySearch.search, } ); return mediaItems; } ) // Null object pattern, we don't want to break if the API fails. .catch( () => [] ), getReportUrl: null, isExternalResource: true, } ); /** * Get Google Photos media category. * * @returns {object} Google Photos media category. */ const googlePhotosProvider = () => buildMediaCategory( GOOGLE_PHOTOS_ID, GOOGLE_PHOTOS_NAME, GOOGLE_PHOTOS_SEARCH_PLACEHOLDER, MediaSource.GooglePhotos, DEFAULT_GOOGLE_PHOTOS_SEARCH ); /** * Checks if a given MediaSource is connected and calls the callback with the response. * * @param {MediaSource} source - MediaSource to check. * @returns {void} */ const isMediaSourceConnected = async ( source: MediaSource ) => apiFetch< boolean | WpcomMediaResponse >( { path: getMediaApiUrl( source, DEFAULT_PEXELS_SEARCH ), method: 'GET', } ) .then( response => response ) .catch( () => false ); /** * Checks if the inserter is opened. * * @returns {boolean} True if the inserter is opened false otherwise. */ const isInserterOpened = (): boolean => { // Prior to WP 6.5, the isInserterOpened selector was available in core/edit-post. // In WP 6.5, it was moved to core/editor. This check is to support both versions of WordPress. // @to-do: remove exception when Jetpack requires WordPress 6.5. const selectIsInserterOpened = /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ ( select( 'core/editor' ) as any )?.isInserterOpened ?? select( 'core/edit-post' )?.isInserterOpened; const editorIsInserterOpened = selectIsInserterOpened?.(); return ( editorIsInserterOpened || select( 'core/edit-site' )?.isInserterOpened() || select( 'core/edit-widgets' )?.isInserterOpened() ); }; const registerInInserter = ( mediaCategoryProvider: () => object ) => // Remove as soon @types/wordpress__block-editor is up to date // eslint-disable-next-line // @ts-ignore dispatch( 'core/block-editor' )?.registerInserterMediaCategory?.( mediaCategoryProvider() ); /** * Get Pexels media category. * * @returns {object} Pexels media category. */ const pexelsProvider = () => buildMediaCategory( PEXELS_ID, PEXELS_NAME, PEXELS_SEARCH_PLACEHOLDER, MediaSource.Pexels, DEFAULT_PEXELS_SEARCH ); /** * Checks if a given MediaSource is authenticated in the store. * * @param {MediaSource} source - MediaSource to check. * @returns {boolean} True if the MediaSource is authenticated false otherwise. */ const isAuthenticatedByWithMediaComponent = ( source: MediaSource ) => !! select( mediaStore ).isAuthenticated( source ); /** * Adds Google Photos to the media inserter if/when it's connected. * We will not remove Google Photos from the inserter if the user disconnects Google Photos during runtime. */ export const addGooglePhotosToMediaInserter = async () => { waitFor( isInserterOpened ).then( async () => { const isConnected = await isMediaSourceConnected( MediaSource.GooglePhotos ); if ( isConnected ) { registerInInserter( googlePhotosProvider ); return; } waitFor( () => isAuthenticatedByWithMediaComponent( MediaSource.GooglePhotos ) ).then( () => registerInInserter( googlePhotosProvider ) ); } ); }; /** * Adds Pexels to the media inserter. There is no need to check if it's connected because it's always connected. */ export const addPexelsToMediaInserter = () => { waitFor( isInserterOpened ).then( () => registerInInserter( pexelsProvider ) ); }; /** * Authenticates a given MediaSource. * * @param {MediaSource} source - MediaSource to authenticate. * @param {boolean} isAuthenticated - True if the MediaSource is authenticated false otherwise. */ export const authenticateMediaSource = ( source: MediaSource, isAuthenticated: boolean ) => { dispatch( mediaStore ).setAuthenticated( source, isAuthenticated ); };