diff --git a/src/admin/class-script-loader.php b/src/admin/class-script-loader.php index 5169344..7f7d8bc 100644 --- a/src/admin/class-script-loader.php +++ b/src/admin/class-script-loader.php @@ -27,6 +27,23 @@ public function __construct() { add_action( 'et_fb_enqueue_assets', [ $this, 'enqueue_cimo_assets' ] ); // Enqueue for the admin area in general. add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_cimo_assets' ] ); + + // Enqueue in frontend for forms plugin + // These hooks only run if the respective forms exist in the frontend. + if( CIMO_BUILD === 'premium' ) { + // Fluent Forms + add_action( 'fluentform/load_form_assets', [ $this, 'enqueue_cimo_assets' ] ); + // Gravity Forms + add_action( 'gform_enqueue_scripts', [ $this, 'enqueue_cimo_assets' ] ); + // WS Forms + add_action( 'wsf_enqueue_scripts', [ $this, 'enqueue_cimo_assets' ] ); + // Ninja Forms + add_action( 'ninja_forms_enqueue_scripts', [ $this, 'enqueue_cimo_assets' ]); + // Contact Form 7 + add_action( 'wpcf7_contact_form', [ $this, 'enqueue_cimo_assets' ] ); + // JetFormBuilder + add_action( 'jet-form-builder/before-start-form-row', [ $this, 'enqueue_cimo_assets' ] ); + } } /** @@ -84,6 +101,8 @@ public function enqueue_cimo_assets() { [ 'restUrl' => rest_url( 'cimo/v1/' ), 'nonce' => wp_create_nonce( 'wp_rest' ), + 'isFrontend' => ! is_admin(), + 'isLoggedIn' => is_user_logged_in(), 'webpQuality' => ! empty( $settings['webp_quality'] ) ? (int) $settings['webp_quality'] : 80, 'maxImageDimension' => ! empty( $settings['max_image_dimension'] ) ? (int) $settings['max_image_dimension'] : 0, 'videoOptimizationEnabled' => isset( $settings['video_optimization_enabled'] ) ? (int) $settings['video_optimization_enabled'] : 1, diff --git a/src/admin/js/media-manager/drop-zone.js b/src/admin/js/media-manager/drop-zone.js index 674edad..e79e25e 100644 --- a/src/admin/js/media-manager/drop-zone.js +++ b/src/admin/js/media-manager/drop-zone.js @@ -10,12 +10,22 @@ import { getFileConverter, requiresFileConversion } from '~cimo/shared/converter import { watchForEditorIframe } from '~cimo/shared/util' import { saveMetadata } from '~cimo/shared/metadata-saver' import { ProgressModal } from './progress-modal' +import { applyFilters } from '@wordpress/hooks' /** * Intercept editor media uploads and convert images to WebP on the client * before uploading to WordPress. This affects the block editor only. */ +// Allowed locations to be able to select files. +const ALLOWED_LOCATIONS = applyFilters( 'cimo.dropZone.allowedLocations', [ + '.media-frame-uploader', // Allowed to drop in the Media Manager + '.media-upload-form', // Allowed to drop in the admin Media > Add Media File + '.editor-post-featured-image', // Allowed to drop in the featured image drop zone + '.editor-styles-wrapper', // Allowed to drop in the block editor when adding new image blocks + '.uploader-window', // Allowed to drop in the admin Media > Library grid view +] ) + // Add event listener to the Media Manager's drop zone function addDropZoneListenerToMediaManager( targetDocument ) { if ( ! targetDocument ) { @@ -45,12 +55,21 @@ function addDropZoneListenerToMediaManager( targetDocument ) { // TODO: We also want to filter out the target so we can set when this // is triggered. We might break other funcitonality that we don't have // the conversion to happen. - if ( ! event.target.closest( '.media-frame-uploader' ) && // Allowed to drop in the Media Manager - ! event.target.closest( '.supports-drag-drop' ).querySelector( '.media-frame-uploader' ) && // Allowed a fallback to drop in the Media Manager - ! event.target.closest( '.media-upload-form' ) && // Allowed to drop in the admin Media > Add Media File. - ! event.target.closest( '.editor-post-featured-image' ) && // Allowed to drop in the featured image drop zone. - ! event.target.closest( '.editor-styles-wrapper' ) && // Allowed to drop in the block editor when adding new image blocks - ! event.target.closest( '.uploader-window' ) ) { // Allowed to drop in the admin Media > Library grid view + + // Find the matched element based on the locations + let matchedElement + for ( const location of ALLOWED_LOCATIONS ) { + matchedElement = event.target.closest( location ) + if ( matchedElement ) { + break + } + } + + // Allowed a fallback to drop in the Media Manager + matchedElement = matchedElement || event.target.closest( '.supports-drag-drop' ) + ?.querySelector( '.media-frame-uploader' ) + + if ( ! matchedElement ) { return } @@ -140,9 +159,11 @@ function addDropZoneListenerToMediaManager( targetDocument ) { // Target the current dropzone event.target.dispatchEvent( dropEvent ) } else { - // Find the file input inside the Media Manager modal // TODO: There might be a better way to do this. - const fileInput = document.querySelector( '.media-modal input[type="file"]' ) || + // Find the file input based on the matched element + const fileInput = matchedElement.querySelector( 'input[type="file"]' ) || + // Find inside the Media Manager modal + document.querySelector( '.media-modal input[type="file"]' ) || // Fallback, this is the Media > Add Media File document.querySelector( '.media-upload-form input[type="file"]' ) || // Just in case diff --git a/src/admin/js/media-manager/select-files.js b/src/admin/js/media-manager/select-files.js index 5323291..9879add 100644 --- a/src/admin/js/media-manager/select-files.js +++ b/src/admin/js/media-manager/select-files.js @@ -10,6 +10,15 @@ import { getFileConverter, requiresFileConversion } from '~cimo/shared/converter import { watchForEditorIframe } from '~cimo/shared/util' import { saveMetadata } from '~cimo/shared/metadata-saver' import { ProgressModal } from './progress-modal' +import { applyFilters } from '@wordpress/hooks' + +// Allowed locations to be able to select files. +const ALLOWED_LOCATIONS = applyFilters( 'cimo.selectFiles.allowedLocations', [ + '.components-form-file-upload', // Allow uploads to the image block + '.media-frame', // Allow uploads from the Media Manager + '.media-upload-form', // Allow uploads from the admin Media > Add Media File + '.moxie-shim', // Allow uploads from the admin Media > Library grid view +] ) // Add event listener to the Media Manager's drop zone function addSelectFilesListenerToFileUploads( targetDocument ) { @@ -46,10 +55,8 @@ function addSelectFilesListenerToFileUploads( targetDocument ) { // selects that we want to, like the media manager picker or the image // block uploader. // Allow these locations to be able to select files. - if ( ! event.target.closest( '.components-form-file-upload' ) && // Allow uploads to the image block - ! event.target.closest( '.media-frame' ) && // Allow uploads from the Media Manager - ! event.target.closest( '.media-upload-form' ) && // Allow uploads from the admin Media > Add Media File - ! event.target.closest( '.moxie-shim' ) ) { // Allow uploads from the admin Media > Library grid view + const isAllowed = ALLOWED_LOCATIONS.some( selector => event.target.closest( selector ) ) + if ( ! isAllowed ) { return } diff --git a/src/admin/js/page/admin-settings.js b/src/admin/js/page/admin-settings.js index a71ea8a..0a781f6 100644 --- a/src/admin/js/page/admin-settings.js +++ b/src/admin/js/page/admin-settings.js @@ -935,6 +935,15 @@ const AdminSettings = () => { { __( 'Optimize SVG files on upload', 'cimo-image-optimizer' ) } +
  • + + { /* Frontend Forms Icon */ } + + + + { __( 'Frontend Forms Support', 'cimo-image-optimizer' ) } + +

  • diff --git a/src/shared/metadata-saver.js b/src/shared/metadata-saver.js index 10de99c..7fab631 100644 --- a/src/shared/metadata-saver.js +++ b/src/shared/metadata-saver.js @@ -11,6 +11,13 @@ * @param {Array} _metadataArray - Array of metadata objects (each must have a filename key) */ export const saveMetadata = _metadataArray => { + const { isFrontend = false, isLoggedIn = false } = window.cimoSettings ?? {} + + // If we're on the frontend and the user is not logged in, we can't save metadata, so we just return. + if ( isFrontend && ! isLoggedIn ) { + return Promise.resolve() + } + if ( ! Array.isArray( _metadataArray ) ) { return Promise.resolve() } @@ -50,7 +57,11 @@ export const saveMetadata = _metadataArray => { .then( response => { if ( ! response.ok ) { return response.json().then( err => { - throw new Error( err.message || response.statusText ) + const newError = new Error( err.message || response.statusText ) + newError.status = response.status + newError.code = err.code + + throw newError } ) } return response.json() @@ -65,6 +76,18 @@ export const saveMetadata = _metadataArray => { resolve( data ) } ) .catch( error => { + const isUnauthorized = error.status === 401 || + error.status === 403 || + error.code === 'rest_cannot_create' || + error.code === 'rest_forbidden' + + // If the error is due to unauthorized access, we resolve the promise without rejecting, + // since this is not a failure and we don't want to spam users. + if ( isUnauthorized ) { + resolve() + return + } + // eslint-disable-next-line no-console console.error( `Failed to save metadata for filenames: [${ metadataArray.map( m => m.filename ).join( ', ' ) }]:`,