filesystem(); $fs->flushImageCache(); // it's possible file just changed by external plugin. $mediaItem = $fs->getImage($id, 'media'); if ($mediaItem === false) { Log::addError('Handle Image Upload Hook triggered, by error in image :' . $id ); return $meta; } if ($mediaItem->getExtension() == 'pdf') { $settings = \wpSPIO()->settings(); if (! $settings->optimizePdfs) { Log::addDebug('Image Upload Hook detected PDF, which is turned off - not optimizing'); return $meta; } } if ($mediaItem->isProcessable()) { $converter = Converter::getConverter($mediaItem, true); if (is_object($converter) && $converter->isConvertable()) { $args = array('runReplacer' => false); $converter->convert($args); $mediaItem = $fs->getImage($id, 'media'); $meta = $converter->getUpdatedMeta(); } $control = new OptimizeController(); $control->addItemToQueue($mediaItem); } else { Log::addWarn('Passed mediaItem is not processable', $mediaItem); } return $meta; // It's a filter, otherwise no thumbs } public function preventImageHook($id) { self::$preventUploadHook[] = $id; } // Placeholder function for heic and such, return placeholder URL in image to help w/ database replacements after conversion. public function checkPlaceHolder($url, $post_id) { if (false === strpos($url, 'heic')) return $url; $extension = pathinfo($url, PATHINFO_EXTENSION); if (false === in_array($extension, ApiConverter::CONVERTABLE_EXTENSIONS)) { return $url; } $fs = \wpSPIO()->filesystem(); $mediaImage = $fs->getImage($post_id, 'media'); if (false === $mediaImage) { return $url; } if (false === $mediaImage->getMeta()->convertMeta()->hasPlaceholder()) { return $url; } $url = str_replace($extension, 'jpg', $url); return $url; } public function processQueueHook($args = array()) { $defaults = array( 'wait' => 3, // amount of time to wait for next round. Prevents high loads 'run_once' => false, // If true queue must be run at least every few minutes. If false, it tries to complete all. 'queues' => array('media','custom'), 'bulk' => false, ); if (wp_doing_cron()) { $this->loadCronCompat(); } $args = wp_parse_args($args, $defaults); $control = new OptimizeController(); if ($args['bulk'] === true) { $control->setBulk(true); } if ($args['run_once'] === true) { return $control->processQueue($args['queues']); } $running = true; $i = 0; while($running) { $results = $control->processQueue($args['queues']); $running = false; foreach($args['queues'] as $qname) { if (property_exists($results, $qname)) { $result = $results->$qname; // If Queue is not completely empty, there should be something to do. if ($result->qstatus != QUEUE::RESULT_QUEUE_EMPTY) { $running = true; continue; } } } sleep($args['wait']); } } public function scanCustomFoldersHook($args = array() ) { $defaults = array( 'force' => false, 'wait' => 3, ); $args = wp_parse_args($args, $defaults); $otherMediaController = OtherMediaController::getInstance(); $running = true; while (true === $running) { $result = $otherMediaController->doNextRefreshableFolder($args); if (false === $result) // stop on false return. { $running = false; } sleep($args['wait']); } } // WP functions that are not loaded during Cron Time. protected function loadCronCompat() { if (! function_exists('download_url')) { include(ABSPATH . "wp-admin/includes/admin.php"); } } /** Filter for Medialibrary items in list and grid view. Because grid uses ajax needs to be caught more general. * @handles pre_get_posts * @param WP_Query $query * * @return WP_Query */ public function filter_listener($query) { global $pagenow; if ( empty( $query->query_vars["post_type"] ) || 'attachment' !== $query->query_vars["post_type"] ) { return $query; } if ( ! in_array( $pagenow, array( 'upload.php', 'admin-ajax.php' ) ) ) { return $query; } $filter = $this->selected_filter_value( 'shortpixel_status', 'all' ); // No filter if ($filter == 'all') { return $query; } // add_filter( 'posts_join', array( $this, 'filter_join' ), 10, 2 ); add_filter( 'posts_where', array( $this, 'filter_add_where' ), 10, 2 ); // add_filter( 'posts_orderby', array( $this, 'query_add_orderby' ), 10, 2 ); return $query; } public function filter_add_where ($where, $query) { global $wpdb; $filter = $this->selected_filter_value( 'shortpixel_status', 'all' ); $tableName = UtilHelper::getPostMetaTable(); switch($filter) { case 'all': break; case 'unoptimized': // The parent <> %d exclusion is meant to also deselect duplicate items ( translations ) since they don't have a status, but shouldn't be in a list like this. $sql = " AND " . $wpdb->posts . '.ID not in ( SELECT attach_id FROM ' . $tableName . " WHERE (parent = %d and status = %d) OR parent <> %d ) "; $where .= $wpdb->prepare($sql, MediaLibraryModel::IMAGE_TYPE_MAIN, ImageModel::FILE_STATUS_SUCCESS, MediaLibraryModel::IMAGE_TYPE_MAIN); break; case 'optimized': $sql = ' AND ' . $wpdb->posts . '.ID in ( SELECT attach_id FROM ' . $tableName . ' WHERE parent = %d and status = %d) '; $where .= $wpdb->prepare($sql, MediaLibraryModel::IMAGE_TYPE_MAIN, ImageModel::FILE_STATUS_SUCCESS); break; case 'prevented': $sql = sprintf('AND %s.ID in (SELECT post_id FROM %s WHERE meta_key = %%s)', $wpdb->posts, $wpdb->postmeta); $sql .= sprintf(' AND %s.ID not in ( SELECT attach_id FROM %s WHERE parent = 0 and status = %s)', $wpdb->posts, $tableName, ImageModel::FILE_STATUS_MARKED_DONE); $where = $wpdb->prepare($sql, '_shortpixel_prevent_optimize'); break; } return $where; } /** * Safely retrieve the selected filter value from a dropdown. * * @param string $key * @param string $default * * @return string */ private function selected_filter_value( $key, $default ) { if ( wp_doing_ajax() ) { if ( isset( $_REQUEST['query'][ $key ] ) ) { $value = sanitize_text_field( $_REQUEST['query'][ $key ] ); } } else { if ( ! isset( $_REQUEST['filter_action'] ) ) { return $default; } if ( ! isset( $_REQUEST[ $key ] ) ) { return $default; } $value = sanitize_text_field( $_REQUEST[ $key ] ); } return ! empty( $value ) ? $value : $default; } /** * When replacing happens. * @hook wp_handle_replace * @integration Enable Media Replace */ public function handleReplaceHook($params) { if(isset($params['post_id'])) { //integration with EnableMediaReplace - that's an upload for replacing an existing ID $post_id = intval($params['post_id']); $fs = \wpSPIO()->filesystem(); $imageObj = $fs->getImage($post_id, 'media'); // In case entry is corrupted data, this might fail. if (is_object($imageObj)) { $imageObj->onDelete(); } } } /** This function is bound to enable-media-replace hook and fire when a file was replaced * * */ public function handleReplaceEnqueue($target, $source, $post_id) { // Delegate this to the hook, so all checks are done there. $this->handleImageUploadHook(array(), $post_id); } public function generatePluginLinks($links) { $in = 'Settings'; array_unshift($links, $in); return $links; } /** Allow certain mime-types if we will be using those. * */ public function addMimes($mimes) { $settings = \wpSPIO()->settings(); if ($settings->createWebp) { if (! isset($mimes['webp'])) $mimes['webp'] = 'image/webp'; } if ($settings->createAvif) { if (! isset($mimes['avif'])) $mimes['avif'] = 'image/avif'; } if (! isset($mimes['heic'])) { $mimes['heic'] = 'image/heic'; } if (! isset($mimes['heif'])) { $mimes['heif'] = 'image/heif'; } return $mimes; } /** Media library gallery view, attempt to add fields that looks like the SPIO status */ public function editAttachmentScreen($fields, $post) { return; // Prevent this thing running on edit media screen. The media library grid is before the screen is set, so just check if we are not on the attachment window. $screen_id = \wpSPIO()->env()->screen_id; if ($screen_id == 'attachment') { return $fields; } $fields["shortpixel-image-optimiser"] = array( "label" => esc_html__("ShortPixel", "shortpixel-image-optimiser"), "input" => "html", "html" => '
--
', ); return $fields; } public function printComparer() { $screen_id = \wpSPIO()->env()->screen_id; if ($screen_id !== 'upload') { return false; } $view = \ShortPixel\Controller\View\ListMediaViewController::getInstance(); $view->loadComparer(); } /** When an image is deleted * @hook delete_attachment * @param int $post_id ID of Post * @return itemHandler ItemHandler object. */ public function onDeleteAttachment($post_id) { Log::addDebug('onDeleteImage - Image Removal Detected ' . $post_id); $result = null; $fs = \wpSPIO()->filesystem(); try { $imageObj = $fs->getImage($post_id, 'media'); //Log::addDebug('OnDelete ImageObj', $imageObj); if ($imageObj !== false) $result = $imageObj->onDelete(); } catch(\Exception $e) { Log::addError('OndeleteImage triggered an error. ' . $e->getMessage(), $e); } return $result; } /** Displays an icon in the toolbar when processing images * hook - admin_bar_menu * @param Obj $wp_admin_bar */ public function toolbar_shortpixel_processing( $wp_admin_bar ) { if (! \wpSPIO()->env()->is_screen_to_use ) return; // not ours, don't load JS and such. $settings = \wpSPIO()->settings(); $access = AccessModel::getInstance(); $quotaController = QuotaController::getInstance(); $extraClasses = " shortpixel-hide"; /*translators: toolbar icon tooltip*/ $id = 'short-pixel-notice-toolbar'; $tooltip = __('ShortPixel optimizing...','shortpixel-image-optimiser'); $icon = "shortpixel.png"; $successLink = $link = admin_url(current_user_can( 'edit_others_posts')? 'upload.php?page=wp-short-pixel-bulk' : 'upload.php'); $blank = ""; if($quotaController->hasQuota() === false) { $extraClasses = " shortpixel-alert shortpixel-quota-exceeded"; /*translators: toolbar icon tooltip*/ $id = 'short-pixel-notice-exceed'; $tooltip = ''; if ($access->userIsAllowed('quota-warning')) { $exceedTooltip = __('ShortPixel quota exceeded. Click for details.','shortpixel-image-optimiser'); //$link = "http://shortpixel.com/login/" . $this->_settings->apiKey; $link = "options-general.php?page=wp-shortpixel-settings"; } else { $exceedTooltip = __('ShortPixel quota exceeded. Click for details.','shortpixel-image-optimiser'); //$link = "http://shortpixel.com/login/" . $this->_settings->apiKey; $link = false; } } $args = array( 'id' => 'shortpixel_processing', 'title' => '
' . __('ShortPixel icon','shortpixel-image-optimiser') . '!' . '
   
' .'
', // 'href' => 'javascript:void(0)', // $link, 'meta' => array('target'=> $blank, 'class' => 'shortpixel-toolbar-processing' . $extraClasses) ); $wp_admin_bar->add_node( $args ); if($quotaController->hasQuota() === false) { $wp_admin_bar->add_node( array( 'id' => 'shortpixel_processing-title', 'parent' => 'shortpixel_processing', 'title' => $exceedTooltip, 'href' => $link )); } } } // class