: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
use Smush\Core\Error_Handler;
use Smush\Core\Stats\Global_Stats;
if ( ! defined( 'WPINC' ) ) {
const PLUGIN_DISCOUNT_PERCENT = 80;
const CDN_POP_LOCATIONS = 123;
* List of smush settings pages.
* @var array $plugin_pages
public static $plugin_pages = array(
'gallery_page_wp-smush-nextgen-bulk',
'nextgen-gallery_page_wp-smush-nextgen-bulk', // Different since NextGen 3.3.6.
'toplevel_page_smush-network',
* @param Media_Library $media_lib Media uploads library.
public function __construct( Media_Library $media_lib ) {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_menu', array( $this, 'add_menu_pages' ) );
add_action( 'network_admin_menu', array( $this, 'add_menu_pages' ) );
add_action( 'admin_init', array( $this, 'smush_i18n' ) );
// Add information to privacy policy page (only during creation).
add_action( 'admin_init', array( $this, 'add_policy' ) );
$this->ajax = new Ajax();
// Init media library UI.
add_filter( 'plugin_action_links_' . WP_SMUSH_BASENAME, array( $this, 'dashboard_link' ) );
add_filter( 'network_admin_plugin_action_links_' . WP_SMUSH_BASENAME, array( $this, 'dashboard_link' ) );
add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 2 );
// Prints a membership validation issue notice in Media Library.
add_action( 'admin_notices', array( $this, 'media_library_membership_notice' ) );
// Plugin conflict notice.
add_action( 'admin_notices', array( $this, 'show_plugin_conflict_notice' ) );
add_action( 'admin_notices', array( $this, 'show_parallel_unavailability_notice' ) );
add_action( 'admin_notices', array( $this, 'show_background_unavailability_notice' ) );
add_action( 'smush_check_for_conflicts', array( $this, 'check_for_conflicts_cron' ) );
add_action( 'activated_plugin', array( $this, 'check_for_conflicts_cron' ) );
add_action( 'deactivated_plugin', array( $this, 'check_for_conflicts_cron' ) );
// Filter built-in wpmudev branding script.
add_filter( 'wpmudev_whitelabel_plugin_pages', array( $this, 'builtin_wpmudev_branding' ) );
add_action( 'wp_smush_header_notices', array( $this, 'maybe_show_local_webp_convert_original_images_notice' ) );
* Load translation files.
public function smush_i18n() {
dirname( WP_SMUSH_BASENAME ) . '/languages'
private function register_scripts() {
* Queue clipboard.js from your plugin if WP's version is below 5.2.0
* since it's only included from 5.2.0 on.
* Use 'clipboard' as the handle so it matches WordPress' handle for the script.
if ( version_compare( $wp_version, '5.2', '<' ) ) {
wp_register_script( 'clipboard', WP_SMUSH_URL . 'app/assets/js/smush-clipboard.min.js', array(), WP_SMUSH_VERSION, true );
* @since 3.8.0 added 'clipboard' dependency.
wp_register_script( 'smush-sui', WP_SMUSH_URL . 'app/assets/js/smush-sui.min.js', array( 'jquery', 'clipboard' ), WP_SHARED_UI_VERSION, true );
wp_register_script( 'smush-admin', WP_SMUSH_URL . 'app/assets/js/smush-admin.min.js', array( 'jquery', 'smush-sui', 'underscore', 'wp-color-picker' ), WP_SMUSH_VERSION, true );
// JS that can be used on all pages in the WP backend.
wp_register_script( 'smush-admin-common', WP_SMUSH_URL . 'app/assets/js/smush-admin-common.min.js', array( 'jquery' ), WP_SMUSH_VERSION, true );
wp_register_style( 'smush-admin', WP_SMUSH_URL . 'app/assets/css/smush-admin.min.css', array(), WP_SMUSH_VERSION );
// Styles that can be used on all pages in the WP backend.
wp_register_style( 'smush-admin-common', WP_SMUSH_URL . 'app/assets/css/smush-global.min.css', array(), WP_SMUSH_VERSION );
WP_Smush::get_instance()->core()->mod->smush->dismiss_update_info();
public function enqueue_scripts() {
wp_enqueue_script( 'smush-global', WP_SMUSH_URL . 'app/assets/js/smush-global.min.js', array(), WP_SMUSH_VERSION, true );
wp_localize_script( 'smush-global', 'smush_global', array(
'nonce' => wp_create_nonce( 'wp-smush-ajax' ),
'opt_in' => Settings::get_instance()->get( 'usage' ),
if ( function_exists( 'get_current_screen' ) ) {
$current_screen = get_current_screen();
$current_page = ! empty( $current_screen ) ? $current_screen->base : $current_page;
if ( ! in_array( $current_page, Core::$external_pages, true ) && false === strpos( $current_page, 'page_smush' ) ) {
// Allows to disable enqueuing smush files on a particular page.
if ( ! apply_filters( 'wp_smush_enqueue', true ) ) {
$this->register_scripts();
// Load on all Smush page only.
if ( isset( $current_screen->id ) && ( in_array( $current_screen->id, self::$plugin_pages, true ) || false !== strpos( $current_screen->id, 'page_smush' ) ) ) {
// Smush admin (smush-admin) includes the Shared UI.
wp_enqueue_style( 'smush-admin' );
wp_enqueue_script( 'smush-wpmudev-sui' );
if ( ! in_array( $current_page, array( 'post', 'post-new', 'page', 'edit-page' ), true ) ) {
// Skip these pages where the script isn't used.
wp_enqueue_script( 'smush-admin' );
// Otherwise, load only the common JS code.
wp_enqueue_script( 'smush-admin-common' );
// We need it on media pages and Smush pages.
wp_enqueue_style( 'smush-admin-common' );
// Localize translatable strings for js.
WP_Smush::get_instance()->core()->localize();
* Adds a Smush pro settings link on plugin page.
* @param array $links Current links.
public function dashboard_link( $links ) {
if ( ! WP_Smush::is_pro() ) {
$upgrade_url = add_query_arg(
'utm_medium' => 'plugin',
'utm_campaign' => 'wp-smush-pro/wp-smush.php' !== WP_SMUSH_BASENAME ? 'smush_pluginlist_upgrade' : 'smush_pluginlist_renew',
esc_url( 'https://wpmudev.com/project/wp-smush-pro/' )
$using_free_version = 'wp-smush-pro/wp-smush.php' !== WP_SMUSH_BASENAME;
if ( $using_free_version ) {
$label = __( 'Upgrade to Smush Pro', 'wp-smushit' );
/* translators: %s: Discount percent */
$text = sprintf( __( 'Upgrade For %s Off!', 'wp-smushit' ), $this->get_plugin_discount() );
$label = __( 'Renew Membership', 'wp-smushit' );
$text = __( 'Renew Membership', 'wp-smushit' );
$links['smush_upgrade'] = '<a href="' . esc_url( $upgrade_url ) . '" aria-label="' . esc_attr( $label ) . '" target="_blank" style="color: #8D00B1;">' . esc_html( $text ) . '</a>';
$docs_link = Helper::get_utm_link(
array( 'utm_campaign' => 'smush_pluginlist_docs' ),
'https://wpmudev.com/docs/wpmu-dev-plugins/smush/'
$links['smush_docs'] = '<a href="' . esc_url( $docs_link ) . '" aria-label="' . esc_attr( __( 'View Smush Documentation', 'wp-smushit' ) ) . '" target="_blank">' . esc_html__( 'Docs', 'wp-smushit' ) . '</a>';
$dashboard_page = is_multisite() && is_network_admin() ? network_admin_url( 'admin.php?page=smush' ) : menu_page_url( 'smush', false );
$links['smush_dashboard'] = '<a href="' . esc_url( $dashboard_page ) . '" aria-label="' . esc_attr( __( 'Go to Smush Dashboard', 'wp-smushit' ) ) . '">' . esc_html__( 'Dashboard', 'wp-smushit' ) . '</a>';
$access = get_site_option( 'wp-smush-networkwide' );
if ( ! is_network_admin() && is_plugin_active_for_network( WP_SMUSH_BASENAME ) && ! $access ) {
// Remove settings link for subsites if Subsite Controls is not set on network permissions tab.
unset( $links['smush_dashboard'] );
return array_reverse( $links );
* Add additional links next to the plugin version.
* @param array $links Links array.
* @param string $file Plugin basename.
public function add_plugin_meta_links( $links, $file ) {
if ( ! defined( 'WP_SMUSH_BASENAME' ) || WP_SMUSH_BASENAME !== $file ) {
if ( 'wp-smush-pro/wp-smush.php' !== WP_SMUSH_BASENAME ) {
$links[] = '<a href="https://wordpress.org/support/plugin/wp-smushit/reviews/#new-post" target="_blank" title="' . esc_attr__( 'Rate Smush', 'wp-smushit' ) . '">' . esc_html__( 'Rate Smush', 'wp-smushit' ) . '</a>';
$links[] = '<a href="https://wordpress.org/support/plugin/wp-smushit/" target="_blank" title="' . esc_attr__( 'Support', 'wp-smushit' ) . '">' . esc_html__( 'Support', 'wp-smushit' ) . '</a>';
if ( isset( $links[2] ) && false !== strpos( $links[2], 'project/wp-smush-pro' ) ) {
'<a href="https://wpmudev.com/project/wp-smush-pro/" target="_blank">%s</a>',
__( 'View details', 'wp-smushit' )
$links[] = '<a href="https://wpmudev.com/get-support/" target="_blank" title="' . esc_attr__( 'Premium Support', 'wp-smushit' ) . '">' . esc_html__( 'Premium Support', 'wp-smushit' ) . '</a>';
$roadmap_link = Helper::get_utm_link(
'utm_campaign' => 'smush_pluginlist_roadmap',
'https://wpmudev.com/roadmap/'
$links[] = '<a href="' . esc_url( $roadmap_link ) . '" target="_blank" title="' . esc_attr__( 'Roadmap', 'wp-smushit' ) . '">' . esc_html__( 'Roadmap', 'wp-smushit' ) . '</a>';
public function add_menu_pages() {
$title = 'wp-smush-pro/wp-smush.php' === WP_SMUSH_BASENAME ? esc_html__( 'Smush Pro', 'wp-smushit' ) : esc_html__( 'Smush', 'wp-smushit' );
if ( Settings::can_access( false, true ) ) {
$this->pages['smush'] = new Pages\Dashboard( 'smush', $title );
$this->pages['dashboard'] = new Pages\Dashboard( 'smush', __( 'Dashboard', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'bulk' ) ) {
$this->pages['bulk'] = new Pages\Bulk( 'smush-bulk', __( 'Bulk Smush', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'directory' ) ) {
$this->pages['directory'] = new Pages\Directory( 'smush-directory', __( 'Directory Smush', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'lazy_load' ) ) {
$this->pages['lazy-load'] = new Pages\Lazy( 'smush-lazy-load', __( 'Lazy Load', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'cdn' ) ) {
$this->pages['cdn'] = new Pages\CDN( 'smush-cdn', __( 'CDN', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'webp' ) ) {
$this->pages['webp'] = new Pages\WebP( 'smush-webp', __( 'Local WebP', 'wp-smushit' ), 'smush' );
if ( Abstract_Page::should_render( 'integrations' ) ) {
$this->pages['integrations'] = new Pages\Integrations( 'smush-integrations', __( 'Integrations', 'wp-smushit' ), 'smush' );
if ( ! is_multisite() || is_network_admin() ) {
$this->pages['settings'] = new Pages\Settings( 'smush-settings', __( 'Settings', 'wp-smushit' ), 'smush' );
if ( ! apply_filters( 'wpmudev_branding_hide_doc_link', false ) && Abstract_Page::should_render( 'tutorials' ) ) {
$this->pages['tutorials'] = new Pages\Tutorials( 'smush-tutorials', __( 'Tutorials', 'wp-smushit' ), 'smush' );
if ( ! WP_Smush::is_pro() ) {
new Pages\Upgrade( 'smush_submenu_upsell', __( 'Upgrade for 80% Off!', 'wp-smushit' ), 'smush', true );
// Add a bulk smush option for NextGen gallery.
if ( defined( 'NGGFOLDER' ) && WP_Smush::get_instance()->core()->nextgen->is_enabled() && WP_Smush::is_pro() && ! is_network_admin() ) {
$this->pages['nextgen'] = new Pages\Nextgen( 'wp-smush-nextgen-bulk', $title, NGGFOLDER, true );
* Add Smush Policy to "Privacy Policy" page during creation.
public function add_policy() {
if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
$content = '<h3>' . __( 'Plugin: Smush', 'wp-smushit' ) . '</h3>';
'<p>' . __( 'Note: Smush does not interact with end users on your website. The only input option Smush has is to a newsletter subscription for site admins only. If you would like to notify your users of this in your privacy policy, you can use the information below.', 'wp-smushit' ) . '</p>';
'<p>' . __( 'Smush sends images to the WPMU DEV servers to optimize them for web use. This includes the transfer of EXIF data. The EXIF data will either be stripped or returned as it is. It is not stored on the WPMU DEV servers.', 'wp-smushit' ) . '</p>';
'<p>' . sprintf( /* translators: %1$s - opening <a>, %2$s - closing </a> */
__( "Smush uses the Stackpath Content Delivery Network (CDN). Stackpath may store web log information of site visitors, including IPs, UA, referrer, Location and ISP info of site visitors for 7 days. Files and images served by the CDN may be stored and served from countries other than your own. Stackpath's privacy policy can be found %1\$shere%2\$s.", 'wp-smushit' ),
'<a href="https://www.stackpath.com/legal/privacy-statement/" target="_blank">',
if ( strpos( WP_SMUSH_DIR, 'wp-smushit' ) !== false ) {
// Only for wordpress.org members.
'<p>' . __( 'Smush uses a third-party email service (Drip) to send informational emails to the site administrator. The administrator\'s email address is sent to Drip and a cookie is set by the service. Only administrator information is collected by Drip.', 'wp-smushit' ) . '</p>';
wp_add_privacy_policy_content(
__( 'WP Smush', 'wp-smushit' ),
wp_kses_post( wpautop( $content, false ) )
* Prints the Membership Validation issue notice
public function media_library_membership_notice() {
// No need to print it for free version.
if ( ! WP_Smush::is_pro() ) {
// Show it on Media Library page only.
$screen = get_current_screen();
if ( ! empty( $screen ) && ( 'upload' === $screen->id || in_array( $screen->id, self::$plugin_pages, true ) || false !== strpos( $screen->id, 'page_smush' ) ) ) {
<div id="wp-smush-invalid-member" data-message="<?php esc_attr_e( 'Validating...', 'wp-smushit' ); ?>" class="hidden notice notice-warning is-dismissible">
/* translators: $1$s: recheck link, $2$s: closing a tag, %3$s; contact link, %4$s: closing a tag */
'It looks like Smush couldn’t verify your WPMU DEV membership so Pro features have been disabled for now. If you think this is an error, run a %1$sre-check%2$s or get in touch with our %3$ssupport team%4$s.',
'<a href="#" id="wp-smush-revalidate-member" data-message="%s">',
'<a href="https://wpmudev.com/contact" target="_blank">',
* Check for plugin conflicts cron.
* @param string $deactivated Holds the slug of activated/deactivated plugin.
public function check_for_conflicts_cron( $deactivated = '' ) {
$conflicting_plugins = array(
'autoptimize/autoptimize.php',
'ewww-image-optimizer/ewww-image-optimizer.php',
'resmushit-image-optimizer/resmushit.php',
'shortpixel-image-optimiser/wp-shortpixel.php',
'tiny-compress-images/tiny-compress-images.php',
'wp-rocket/wp-rocket.php',
'optimole-wp/optimole-wp.php',
'rocket-lazy-load/rocket-lazy-load.php',
'a3-lazy-load/a3-lazy-load.php',
'sg-cachepress/sg-cachepress.php',
'w3-total-cache/w3-total-cache.php',
'wp-fastest-cache/wpFastestCache.php',
'wp-optimize/wp-optimize.php',
$plugins = get_plugins();
$active_plugins = array();
foreach ( $conflicting_plugins as $plugin ) {
if ( ! array_key_exists( $plugin, $plugins ) ) {
if ( ! is_plugin_active( $plugin ) ) {
// Deactivation of the plugin in process.
if ( doing_action( 'deactivated_plugin' ) && $deactivated === $plugin ) {
$active_plugins[] = $plugins[ $plugin ]['Name'];
set_transient( 'wp-smush-conflict_check', $active_plugins, 3600 );
* Display plugin incompatibility notice.
public function show_plugin_conflict_notice() {
// Do not show on lazy load module, there we show an inline notice.
if ( false !== strpos( get_current_screen()->id, 'page_smush-lazy-load' ) ) {
$dismissed = $this->is_notice_dismissed( 'plugin-conflict' );
$conflict_check = get_transient( 'wp-smush-conflict_check' );
// Have never checked before.
if ( false === $conflict_check ) {
wp_schedule_single_event( time(), 'smush_check_for_conflicts' );
// No conflicting plugins detected.
if ( isset( $conflict_check ) && is_array( $conflict_check ) && empty( $conflict_check ) ) {