'; return 'data:image/svg+xml;base64,' . base64_encode( $svg ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- SVG data URI for the admin menu icon. } /** * Body class. * * @param string $classes Classes. * @return string */ public static function body_class( $classes ) { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( $screen && self::$hook && $screen->id === self::$hook ) { $classes .= ' spinix-io-admin-page'; } return $classes; } /** * Assets. * * @param string $hook Hook. */ public static function assets( $hook ) { if ( $hook !== self::$hook ) { return; } wp_enqueue_style( 'spinix-io-admin', SPINIX_IMAGE_OPTIMIZATION_URL . 'assets/css/admin.css', array(), SPINIX_IMAGE_OPTIMIZATION_VERSION ); wp_enqueue_script( 'spinix-io-admin', SPINIX_IMAGE_OPTIMIZATION_URL . 'assets/js/admin.js', array(), SPINIX_IMAGE_OPTIMIZATION_VERSION, true ); /** * Filter the data of the Spinix Image Optimization screen (add-ons add their own). * * @param array $data Data passed to the screen's script. */ $data = (array) apply_filters( 'spinix_io_admin_data', array( 'ajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'spinix_io_admin' ), 'stats' => Spinix_Image_Optimization_Stats::get(), 'settings' => self::settings_for_js(), 'log' => self::log_for_js(), 'webp' => Spinix_Image_Optimization_Environment::webp_supported(), 'i18n' => array( 'saved' => __( 'Settings saved', 'spinix-image-optimization' ), 'error' => __( 'Something went wrong. Please try again.', 'spinix-image-optimization' ), 'start' => __( 'Start bulk optimization', 'spinix-image-optimization' ), 'resume' => __( 'Resume', 'spinix-image-optimization' ), 'pause' => __( 'Pause', 'spinix-image-optimization' ), 'running' => __( 'Optimizing your library…', 'spinix-image-optimization' ), 'runningSub' => __( 'Runs in the background. You can safely close this page.', 'spinix-image-optimization' ), 'paused' => __( 'Paused', 'spinix-image-optimization' ), 'pausedSub' => __( 'Pick up where you left off at any time.', 'spinix-image-optimization' ), 'complete' => __( 'Your library is fully optimized', 'spinix-image-optimization' ), 'completeSub' => __( 'New uploads are optimized automatically.', 'spinix-image-optimization' ), 'idle' => __( 'Ready to optimize', 'spinix-image-optimization' ), 'idleSub' => __( 'Compress every image and convert it to WebP.', 'spinix-image-optimization' ), 'empty' => __( 'No images in your media library yet.', 'spinix-image-optimization' ), 'noActivity' => __( 'Optimized images will show up here.', 'spinix-image-optimization' ), 'of' => __( 'of', 'spinix-image-optimization' ), 'images' => __( 'images', 'spinix-image-optimization' ), 'remaining' => __( 'remaining', 'spinix-image-optimization' ), 'confirm' => __( 'Are you sure?', 'spinix-image-optimization' ), 'cancel' => __( 'Cancel', 'spinix-image-optimization' ), 'done' => __( 'Done', 'spinix-image-optimization' ), 'justNow' => __( 'just now', 'spinix-image-optimization' ), 'minAgo' => __( 'min ago', 'spinix-image-optimization' ), 'active' => __( 'Active', 'spinix-image-optimization' ), 'failed' => __( 'failed', 'spinix-image-optimization' ), 'skipped' => __( 'skipped', 'spinix-image-optimization' ), 'st_optimized' => __( 'Optimized', 'spinix-image-optimization' ), 'st_optimal' => __( 'Already optimal', 'spinix-image-optimization' ), 'origKept' => __( 'Original kept', 'spinix-image-optimization' ), 'origKeptWhy' => __( 'already smaller than WebP', 'spinix-image-optimization' ), 'origUsed' => __( 'Original used', 'spinix-image-optimization' ), /* translators: 1: sizes served as WebP, 2: all sizes. */ 'webpOf' => __( '%1$d of %2$d sizes', 'spinix-image-optimization' ), 'st_failed' => __( 'Failed', 'spinix-image-optimization' ), 'st_skipped' => __( 'Skipped', 'spinix-image-optimization' ), 'st_pending' => __( 'Waiting', 'spinix-image-optimization' ), 'tElapsed' => __( 'elapsed', 'spinix-image-optimization' ), 'tLeft' => __( 'left', 'spinix-image-optimization' ), 'tPerImage' => __( 'per image', 'spinix-image-optimization' ), 'tPerMin' => __( 'images / min', 'spinix-image-optimization' ), 'tDoneRun' => __( 'done this run', 'spinix-image-optimization' ), 'tLastRun' => __( 'Last run', 'spinix-image-optimization' ), 'tImagesIn' => __( 'images in', 'spinix-image-optimization' ), 'tStarting' => __( 'Measuring speed…', 'spinix-image-optimization' ), 'tTook' => __( 'took', 'spinix-image-optimization' ), 'retry' => __( 'Retry', 'spinix-image-optimization' ), 'reoptimize' => __( 'Re-optimize', 'spinix-image-optimization' ), 'optimizeNow' => __( 'Optimize now', 'spinix-image-optimization' ), 'serveOriginal' => __( 'Serve original', 'spinix-image-optimization' ), 'noImages' => __( 'No images here.', 'spinix-image-optimization' ), 'failedOne' => __( '1 image could not be optimized.', 'spinix-image-optimization' ), /* translators: %d: number of images */ 'failedMany' => __( '%d images could not be optimized.', 'spinix-image-optimization' ), ), ) ); wp_localize_script( 'spinix-io-admin', 'spinixIoData', $data ); /** * Fires after the Spinix Image Optimization screen's scripts are enqueued. * * @param string $hook Page hook. */ do_action( 'spinix_io_admin_enqueue', $hook ); } /** * Settings shown on the screen, plus the settings of add-ons (same form, one Save button). * * @return array */ public static function settings_for_js() { /** * Filter the settings edited on the Settings tab. Add-ons add their own keys * (prefixed, e.g. "pro_avif") and save them on 'spinix_io_settings_saved'. * * @param array $settings Settings. */ return (array) apply_filters( 'spinix_io_admin_settings', Spinix_Image_Optimization_Settings::all() ); } /** * Show the "Pro" tab (only while Spinix Image Optimization Pro is not active). * * @return bool */ public static function show_pro_tab() { return (bool) apply_filters( 'spinix_io_show_pro_tab', ! defined( 'SPINIX_IMAGE_OPTIMIZATION_PRO_VERSION' ) ); } /** * Address of the Pro page. * * @return string */ public static function pro_url() { return (string) apply_filters( 'spinix_io_pro_url', 'https://spinixtechnologies.com/' ); } /** * The "Pro" tab: what the Pro add-on adds. Everything in the free plugin keeps * working without it; nothing is locked. */ private static function render_pro_tab() { $features = array( array( __( 'AVIF', 'spinix-image-optimization' ), __( 'AVIF next to WebP, only when it is smaller at the same visual quality.', 'spinix-image-optimization' ) ), array( __( 'Device-sized images', 'spinix-image-optimization' ), __( 'Extra widths for phones and tablets, so every device downloads the right size.', 'spinix-image-optimization' ) ), array( __( 'Real-visitor LCP learning', 'spinix-image-optimization' ), __( 'Learns the real main image of each page from visitors and prioritizes exactly that image.', 'spinix-image-optimization' ) ), array( __( 'Lazy background images', 'spinix-image-optimization' ), __( 'Section backgrounds further down the page load when visitors scroll near them.', 'spinix-image-optimization' ) ), array( __( 'Video and embed lazy loading', 'spinix-image-optimization' ), __( 'YouTube and Vimeo load a light preview until clicked.', 'spinix-image-optimization' ) ), array( __( 'Animated GIF to WebP', 'spinix-image-optimization' ), __( 'Animated GIFs become much smaller animated WebP files.', 'spinix-image-optimization' ) ), array( __( 'Cloud storage and CDN', 'spinix-image-optimization' ), __( 'Images, including WebP and AVIF, served from Amazon S3, Cloudflare R2, DigitalOcean Spaces, Wasabi, Backblaze B2 or a CDN.', 'spinix-image-optimization' ) ), array( __( 'Image Health Report', 'spinix-image-optimization' ), __( 'Every image problem on every page, one-click fixes and an optional report by email.', 'spinix-image-optimization' ) ), array( __( 'Image SEO', 'spinix-image-optimization' ), __( 'Find and fill in missing alt texts, and clean file names on upload.', 'spinix-image-optimization' ) ), array( __( 'WooCommerce', 'spinix-image-optimization' ), __( 'WebP for variation images and gallery zoom / lightbox images.', 'spinix-image-optimization' ) ), array( __( 'Custom folders', 'spinix-image-optimization' ), __( 'WebP for theme and plugin images outside the Media Library.', 'spinix-image-optimization' ) ), array( __( 'Upload resizing and privacy', 'spinix-image-optimization' ), __( 'Scale huge photos down and remove camera / GPS data, with backups.', 'spinix-image-optimization' ) ), ); ?> time() + WEEK_IN_SECONDS ), false ); return; } $stats = Spinix_Image_Optimization_Stats::get(); if ( time() < (int) $r['next'] || (int) $stats['optimized'] < 20 ) { return; } ?>
id === self::$hook ) { delete_transient( 'spinix_io_activation_notice' ); return; } // Shown once, on the Plugins screen or the Dashboard only. if ( $screen && in_array( $screen->id, array( 'plugins', 'dashboard' ), true ) && get_transient( 'spinix_io_activation_notice' ) ) { delete_transient( 'spinix_io_activation_notice' ); $url = admin_url( 'admin.php?page=' . self::SLUG ); echo '

' . esc_html__( 'Spinix Image Optimization is optimizing your existing images in the background.', 'spinix-image-optimization' ) . ' '; echo esc_html__( 'Your original files stay untouched.', 'spinix-image-optimization' ) . ' '; echo '' . esc_html__( 'View progress →', 'spinix-image-optimization' ) . '

'; } if ( $screen && in_array( $screen->id, array( 'plugins', 'dashboard' ), true ) && ! Spinix_Image_Optimization_Environment::webp_supported() ) { echo '

' . esc_html__( 'Spinix Image Optimization: this server cannot create WebP images. Ask your host to enable WebP support in GD or Imagick.', 'spinix-image-optimization' ) . '

'; } } /** * AJAX router. */ public static function ajax() { check_ajax_referer( 'spinix_io_admin', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Not allowed.', 'spinix-image-optimization' ) ), 403 ); } $task = isset( $_POST['task'] ) ? sanitize_key( wp_unslash( $_POST['task'] ) ) : ''; $resp = array(); switch ( $task ) { case 'start': Spinix_Image_Optimization_Bulk::start(); break; case 'pause': Spinix_Image_Optimization_Bulk::pause(); break; case 'process': if ( 'running' === Spinix_Image_Optimization_Bulk::state() ) { $r = Spinix_Image_Optimization_Bulk::process( 8.0 ); $resp['busy'] = ( false === $r ); } break; case 'save': $input = array(); if ( isset( $_POST['settings'] ) && is_array( $_POST['settings'] ) ) { foreach ( wp_unslash( $_POST['settings'] ) as $key => $value ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- every key and value is sanitized on the next lines. if ( is_scalar( $value ) ) { $input[ sanitize_key( $key ) ] = sanitize_textarea_field( (string) $value ); } } } Spinix_Image_Optimization_Settings::save( $input ); /** * Fires after the Settings tab was saved, with every submitted field * (keys sanitized with sanitize_key(), values with sanitize_textarea_field()). * * @param array $input Submitted fields. */ do_action( 'spinix_io_settings_saved', $input ); $resp['settings'] = self::settings_for_js(); break; case 'retry': $resp['count'] = Spinix_Image_Optimization_Bulk::retry_failed(); Spinix_Image_Optimization_Bulk::start(); break; case 'reoptimize': delete_metadata( 'post', 0, Spinix_Image_Optimization_Optimizer::META_STATUS, '', true ); Spinix_Image_Optimization_Css::flush(); Spinix_Image_Optimization_Bulk::start(); break; case 'purge': Spinix_Image_Optimization_Bulk::pause(); update_option( Spinix_Image_Optimization_Bulk::STATE_OPTION, 'idle', false ); Spinix_Image_Optimization_Bulk::timing_clear(); foreach ( array( Spinix_Image_Optimization_Optimizer::META_STATUS, Spinix_Image_Optimization_Optimizer::META_DATA, Spinix_Image_Optimization_Optimizer::META_BEFORE, Spinix_Image_Optimization_Optimizer::META_AFTER, Spinix_Image_Optimization_Optimizer::META_ERROR ) as $key ) { delete_metadata( 'post', 0, $key, '', true ); } Spinix_Image_Optimization_Storage::delete_all(); Spinix_Image_Optimization_Css::flush(); Spinix_Image_Optimization_Storage::ensure_dir(); delete_option( Spinix_Image_Optimization_Bulk::LOG_OPTION ); break; case 'images': $resp['images'] = self::images_page( isset( $_POST['filter'] ) ? sanitize_key( wp_unslash( $_POST['filter'] ) ) : 'all', isset( $_POST['search'] ) ? sanitize_text_field( wp_unslash( $_POST['search'] ) ) : '', isset( $_POST['page'] ) ? max( 1, absint( $_POST['page'] ) ) : 1 ); break; case 'image': $iid = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0; $do = isset( $_POST['do'] ) ? sanitize_key( wp_unslash( $_POST['do'] ) ) : 'optimize'; if ( $iid && 'attachment' === get_post_type( $iid ) ) { if ( 'remove' === $do ) { Spinix_Image_Optimization_Optimizer::delete_files( $iid ); update_post_meta( $iid, Spinix_Image_Optimization_Optimizer::META_STATUS, 'skipped' ); update_post_meta( $iid, Spinix_Image_Optimization_Optimizer::META_ERROR, __( 'Original served by choice.', 'spinix-image-optimization' ) ); } else { Spinix_Image_Optimization_Bulk::release( $iid ); $r = Spinix_Image_Optimization_Optimizer::optimize( $iid, true ); Spinix_Image_Optimization_Bulk::log( array( $r ) ); } $resp['row'] = self::image_row( $iid ); } break; case 'stats': break; case 'review': $choice = isset( $_POST['do'] ) ? sanitize_key( wp_unslash( $_POST['do'] ) ) : ''; update_option( 'spinix_io_review', 'later' === $choice ? array( 'next' => time() + 30 * DAY_IN_SECONDS ) : array( 'done' => 1 ), false ); break; default: if ( ! has_action( 'spinix_io_admin_task_' . $task ) ) { wp_send_json_error( array( 'message' => 'Unknown task' ), 400 ); } /** * Handle an add-on task sent from the Spinix Image Optimization screen * (nonce and capability are already checked). * * @param array $resp Response data, passed by reference via the returned value. */ $resp = (array) apply_filters( 'spinix_io_admin_task_' . $task, $resp ); } $resp['stats'] = Spinix_Image_Optimization_Stats::get(); $resp['log'] = self::log_for_js(); /** * Filter the answer to a request from the Spinix Image Optimization screen. * * @param array $resp Response data. * @param string $task Task. */ wp_send_json_success( apply_filters( 'spinix_io_admin_response', $resp, $task ) ); } /** * One page of the Images list. * * @param string $filter all|optimized|pending|failed|skipped. * @param string $search Search. * @param int $page Page. * @return array */ private static function images_page( $filter, $search, $page ) { $per = 20; $args = array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_mime_type' => 'image', 'posts_per_page' => $per, 'paged' => $page, 'orderby' => 'ID', 'order' => 'DESC', 'fields' => 'ids', ); if ( '' !== $search ) { $args['s'] = $search; } if ( in_array( $filter, array( 'optimized', 'failed', 'skipped' ), true ) ) { $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery array( 'key' => Spinix_Image_Optimization_Optimizer::META_STATUS, 'value' => $filter, ), ); } elseif ( 'pending' === $filter ) { $args['post_mime_type'] = Spinix_Image_Optimization_Optimizer::mimes(); $args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery array( 'key' => Spinix_Image_Optimization_Optimizer::META_STATUS, 'compare' => 'NOT EXISTS', ), ); } $q = new WP_Query( $args ); $rows = array(); update_meta_cache( 'post', $q->posts ); foreach ( $q->posts as $id ) { $rows[] = self::image_row( (int) $id ); } $stats = Spinix_Image_Optimization_Stats::get(); $all = (int) array_sum( (array) wp_count_attachments( 'image' ) ); return array( 'rows' => $rows, 'total' => (int) $q->found_posts, 'pages' => (int) $q->max_num_pages, 'page' => $page, 'counts' => array( 'all' => $all, 'optimized' => $stats['optimized'], 'pending' => $stats['pending'], 'failed' => $stats['failed'], 'skipped' => $stats['skipped'] + max( 0, $all - $stats['total'] ), ), ); } /** * Data for one image row. * * @param int $id Attachment ID. * @return array */ public static function image_row( $id ) { $mime = (string) get_post_mime_type( $id ); $supported = in_array( $mime, Spinix_Image_Optimization_Optimizer::mimes(), true ); $sum = Spinix_Image_Optimization_Optimizer::summary( $id ); $file = get_attached_file( $id ); $status = $supported ? $sum['status'] : 'skipped'; $reason = $sum['error']; if ( ! $supported ) { $reason = 'image/gif' === $mime ? __( 'GIF files are kept as they are (animations would be lost).', 'spinix-image-optimization' ) : __( 'This file type is kept as it is.', 'spinix-image-optimization' ); } elseif ( 'pending' === $status ) { $reason = __( 'Waiting in the queue.', 'spinix-image-optimization' ); } $meta = wp_get_attachment_metadata( $id ); $row = array( 'id' => $id, 'title' => html_entity_decode( get_the_title( $id ), ENT_QUOTES ), 'file' => $file ? wp_basename( $file ) : '', 'dims' => is_array( $meta ) && ! empty( $meta['width'] ) ? $meta['width'] . '×' . $meta['height'] : '', 'thumb' => (string) wp_get_attachment_image_url( $id, 'thumbnail' ), 'edit' => (string) get_edit_post_link( $id, 'raw' ), 'status' => $status, 'supported' => $supported, 'before' => $sum['before'] ? size_format( $sum['before'], 1 ) : ( $file && file_exists( $file ) ? size_format( filesize( $file ), 1 ) : '' ), 'after' => $sum['after'] ? size_format( $sum['after'], 1 ) : '', 'percent' => $sum['percent'], 'webp' => $sum['webp_count'], 'files' => $sum['file_count'], 'optimal' => 'optimized' === $status && 0 === (int) $sum['webp_count'], 'reason' => $reason, 'ms' => $sum['ms'], ); /** * Filter one row of the Images list. * * @param array $row Row data. * @param int $id Attachment ID. */ return (array) apply_filters( 'spinix_io_image_row', $row, $id ); } /** * Toggle row helper. * * @param string $key Setting key. * @param string $title Title. * @param string $desc Description. * @param string $badge Optional badge. */ private static function toggle( $key, $title, $desc, $badge = '' ) { $id = 'spxio-' . $key; ?>


Spinix
0%

0 B
0%
0
0 B → 0 B