fix
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"version":1,"defects":{"Test_WRIO_Image_Query::test_get_ids_with_offset":5,"Test_WRIO_Image_Query::test_count_consistency_optimized":5,"Test_WRIO_Image_Query::test_pagination_works":5},"times":{"Test_WRIO_Image_Query::test_get_required_types_returns_array":0.005,"Test_WRIO_Image_Query::test_count_total_attachments_returns_int":0.002,"Test_WRIO_Image_Query::test_count_optimized_returns_int":0.002,"Test_WRIO_Image_Query::test_count_unoptimized_returns_int":0.001,"Test_WRIO_Image_Query::test_count_error_returns_int":0.001,"Test_WRIO_Image_Query::test_get_optimized_ids_returns_array":0.001,"Test_WRIO_Image_Query::test_get_unoptimized_ids_returns_array":0.001,"Test_WRIO_Image_Query::test_get_error_ids_returns_array":0.001,"Test_WRIO_Image_Query::test_get_ids_with_limit":0.002,"Test_WRIO_Image_Query::test_get_ids_with_offset":0.001,"Test_WRIO_Image_Query::test_cache_clearing":0.001,"Test_WRIO_Image_Query::test_count_consistency_optimized":0.002,"Test_WRIO_Image_Query::test_count_consistency_unoptimized":0.002,"Test_WRIO_Image_Query::test_pagination_works":0.002,"Test_WRIO_Image_Query::test_wpml_exclude_parameter":0.001}}
|
||||
65
wp-content/plugins/robin-image-optimizer/CHANGELOG.md
Normal file
65
wp-content/plugins/robin-image-optimizer/CHANGELOG.md
Normal file
@@ -0,0 +1,65 @@
|
||||
##### Version 2.0.4 (2026-03-12)
|
||||
|
||||
### Improvements
|
||||
|
||||
- Enhanced logging with more detailed error reporting.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Included error messages in the dashboard logging reports.
|
||||
- Resolved an issue where error handling could disrupt the processing queue.
|
||||
|
||||
##### Version 2.0.3 (2026-01-23)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Update dependencies.
|
||||
- Fixed compatibility with other products.
|
||||
- Fixed display for optimization log metrics.
|
||||
- Enhanced security.
|
||||
|
||||
##### Version 2.0.2 (2026-01-12)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fix license encoding for Freemius license keys with special characters.
|
||||
|
||||
##### Version 2.0.1 (2026-01-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- License validation for users use Freemius license keys.
|
||||
- Various PHP warnings.
|
||||
- Enhanced security.
|
||||
|
||||
#### Version 2.0.0 (2026-01-09)
|
||||
|
||||
### New Features
|
||||
|
||||
- Added AVIF image format conversion capability [PRO]
|
||||
- Free tier now includes WebP format and unlimited optimizations
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Updated all dependencies to latest versions
|
||||
- Enabled auto-optimize by default for new installations
|
||||
- Improved optimization quality and performance
|
||||
- Updated optimization libraries
|
||||
- Refactored admin interface with separate tabs as individual pages in the sidebar
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed critical error with export logs tool permission checking
|
||||
- Fixed UI bugs including label toggle behavior, text overflow on tabs, text contrast, and padding inconsistencies
|
||||
- Fixed 429 rate-limiting error
|
||||
- Fixed GIF optimization failing for large files
|
||||
- Fixed errors related to WebP conversion
|
||||
- Fixed EXIF data being stripped during optimization
|
||||
- Fixed custom folder path checking for non-standard WordPress folder structures
|
||||
|
||||
#### Version 1.9.0 (2025-10-20)
|
||||
|
||||
Robin Image Optimizer has been acquired by Themeisle 🎉
|
||||
We’re happy to announce that Themeisle is now the new owner of Robin Image Optimizer. This acquisition will help ensure the plugin’s continued development, better support, and exciting new updates in the future.
|
||||
|
||||
Your existing setup and optimizations will continue to work as usual — no action is required on your part.
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Activator for the Robin image optimizer
|
||||
*
|
||||
* @see Factory600_Activator
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class WIO_Activation extends Wbcr_Factory600_Activator {
|
||||
|
||||
/**
|
||||
* Runs activation actions.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function activate() {
|
||||
WRIO_Plugin::app()->logger->info( 'Parent plugin start installation!' );
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'backup_origin_images', 1 );
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'save_exif_data', 1 );
|
||||
|
||||
// Enable auto-optimize on upload by default for new installations only.
|
||||
if ( 0 === $this->get_plugin_version_in_db() ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'auto_optimize_when_upload', 1 );
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'convert_webp_format', 1 );
|
||||
}
|
||||
|
||||
if ( function_exists( 'wrio_is_license_activate' ) && wrio_is_license_activate() ) {
|
||||
WRIO_Plugin::app()->logger->info( 'Premium plugin start installation!' );
|
||||
require_once WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php';
|
||||
wrio_premium_activate();
|
||||
WRIO_Plugin::app()->logger->info( 'Premium plugin installation complete!' );
|
||||
}
|
||||
|
||||
$db_version = RIO_Process_Queue::get_db_version();
|
||||
$plugin_version_in_db = $this->get_plugin_version_in_db();
|
||||
$current_plugin_version = $this->plugin->getPluginVersion();
|
||||
|
||||
$create_table_log_message = "Plugin installation: try create plugin tables.\r\n";
|
||||
$create_table_log_message .= "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t-DB Version: {$db_version}\r\n";
|
||||
$create_table_log_message .= "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t-Plugin Version in DB: {$plugin_version_in_db}\r\n";
|
||||
$create_table_log_message .= "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t-Current Plugin Version: {$current_plugin_version}";
|
||||
|
||||
WRIO_Plugin::app()->logger->info( $create_table_log_message );
|
||||
|
||||
RIO_Process_Queue::try_create_plugin_tables();
|
||||
|
||||
WBCR\Factory_Templates_759\Helpers::flushPageCache();
|
||||
|
||||
WRIO_Plugin::app()->logger->info( 'Parent plugin installation complete!' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get previous plugin version
|
||||
*
|
||||
* @since 1.3.8
|
||||
* @return number
|
||||
*/
|
||||
public function get_plugin_version_in_db() {
|
||||
if ( WRIO_Plugin::app()->isNetworkActive() ) {
|
||||
return get_site_option( WRIO_Plugin::app()->getOptionName( 'plugin_version' ), 0 );
|
||||
}
|
||||
|
||||
return get_option( WRIO_Plugin::app()->getOptionName( 'plugin_version' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs activation actions.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function deactivate() {
|
||||
WRIO_Plugin::app()->logger->info( 'Parent plugin start deactivation!' );
|
||||
|
||||
if ( class_exists( 'WRIO_Cron' ) ) {
|
||||
WRIO_Cron::stop();
|
||||
}
|
||||
|
||||
if ( function_exists( 'wrio_is_license_activate' ) && wrio_is_license_activate() ) {
|
||||
WRIO_Plugin::app()->logger->info( 'Premium plugin start deactivation!' );
|
||||
require_once WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php';
|
||||
wrio_premium_deactivate();
|
||||
WRIO_Plugin::app()->logger->info( 'Premium plugin deactivation complete!' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( 'Parent plugin deactivation complete!' );
|
||||
}
|
||||
}
|
||||
108
wp-content/plugins/robin-image-optimizer/admin/ajax/backup.php
Normal file
108
wp-content/plugins/robin-image-optimizer/admin/ajax/backup.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* Back-up related filters.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX обработчик восстановления из резервной копии
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_restore_backup',
|
||||
function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$max_process_per_request = 25;
|
||||
|
||||
// $blog_id = WRIO_Plugin::app()->request->post( 'blog_id', null, true );
|
||||
|
||||
/*
|
||||
if ( $blog_id !== null ) {
|
||||
switch_to_blog( $blog_id );
|
||||
}*/
|
||||
|
||||
// Total number of remained images to restore
|
||||
$remane_count = 0;
|
||||
|
||||
$total = 0;
|
||||
|
||||
$filter_results = apply_filters( 'wbcr/rio/backup/restore_filter', $max_process_per_request );
|
||||
|
||||
if ( isset( $filter_results['remane'] ) ) {
|
||||
$remane_count += $filter_results['remane'];
|
||||
}
|
||||
|
||||
if ( isset( $filter_results['total'] ) ) {
|
||||
$total += $filter_results['total'];
|
||||
}
|
||||
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
|
||||
$total += $media_library->getOptimizedCount();
|
||||
|
||||
$restored_data = $media_library->restoreAllFromBackup( $max_process_per_request );
|
||||
|
||||
if ( isset( $restored_data['remain'] ) ) {
|
||||
$remane_count += $restored_data['remain'];
|
||||
}
|
||||
|
||||
/*
|
||||
if ( $blog_id !== null ) {
|
||||
restore_current_blog();
|
||||
}*/
|
||||
|
||||
$restored_data['total'] = $total;
|
||||
|
||||
if ( $total > 0 ) {
|
||||
$restored_data['percent'] = 100 - ( $remane_count * 100 / $total );
|
||||
} else {
|
||||
$restored_data['percent'] = 0;
|
||||
}
|
||||
|
||||
// если изображения закончились - посылаем команду завершения
|
||||
if ( $remane_count <= 0 ) {
|
||||
$restored_data['end'] = true;
|
||||
}
|
||||
|
||||
wp_send_json( $restored_data );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX обработчик очистки папки с бекапами
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_clear_backup',
|
||||
function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$backup = WIO_Backup::get_instance();
|
||||
$blogs = WRIO_Plugin::app()->request->post( 'blogs', [], true );
|
||||
|
||||
if ( ! empty( $blogs ) ) {
|
||||
foreach ( $blogs as $blog_id ) {
|
||||
switch_to_blog( intval( $blog_id ) );
|
||||
$backup->removeBlogBackupDir();
|
||||
restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
$backup->removeBackupDir();
|
||||
}
|
||||
|
||||
wp_send_json( true );
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
31
wp-content/plugins/robin-image-optimizer/admin/ajax/logs.php
Normal file
31
wp-content/plugins/robin-image-optimizer/admin/ajax/logs.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Used to clean-up logs.
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wrio_logs_cleanup',
|
||||
function () {
|
||||
check_admin_referer( 'wrio_clean_logs', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
if ( ! WRIO_Plugin::app()->logger->clean_up() ) {
|
||||
wp_send_json_error(
|
||||
[
|
||||
'message' => esc_html__( 'Failed to clear logs. Please try again later.', 'robin-image-optimizer' ),
|
||||
'type' => 'danger',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json(
|
||||
[
|
||||
'message' => esc_html__( 'Logs cleared successfully.', 'robin-image-optimizer' ),
|
||||
'type' => 'success',
|
||||
]
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax action to migrate old architecture based on post meta into new table.
|
||||
*
|
||||
* @see RIO_Process_Queue for further information.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_wrio_meta_migrations', 'wbcr_rio_migrate_postmeta_to_process_queue' );
|
||||
|
||||
/**
|
||||
* Migrating postmeta to newly created table.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @see RIO_Process_Queue as referce for new table.
|
||||
*/
|
||||
function wbcr_rio_migrate_postmeta_to_process_queue() {
|
||||
global $wpdb;
|
||||
|
||||
check_admin_referer( 'wrio-meta-migrations' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$error = (int) WRIO_Plugin::app()->request->post( 'error', 0 );
|
||||
|
||||
if ( $error ) {
|
||||
WRIO_Plugin::app()->logger->error( 'Previous migration was not completed due to an error.' );
|
||||
}
|
||||
|
||||
$limit = (int) WRIO_Plugin::app()->request->post( 'limit', 150 );
|
||||
|
||||
$processed_items = 0;
|
||||
|
||||
WRIO_Plugin::app()->logger->info( 'Start meta migration. Limit ' . $limit );
|
||||
WRIO_Plugin::app()->logger->memory_usage();
|
||||
|
||||
$attachments = wbcr_rio_get_meta_to_migrate();
|
||||
|
||||
if ( isset( $attachments->posts ) && ( $attachments_total = count( $attachments->posts ) ) > 0 ) {
|
||||
|
||||
if ( $attachments_total < $limit ) {
|
||||
$limit = $attachments_total;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( 'Finded ' . $attachments_total . ' attachments for migration.' );
|
||||
|
||||
if ( function_exists( 'wp_raise_memory_limit' ) ) {
|
||||
wp_raise_memory_limit( 'image' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->memory_usage();
|
||||
|
||||
/**
|
||||
* @var WP_Post $attachment
|
||||
*/
|
||||
for ( $i = 0; $i < $limit; $i++ ) {
|
||||
$attachment = $attachments->posts[ $i ];
|
||||
$post_meta = get_post_custom( $attachment->ID );
|
||||
|
||||
$extra_data = new RIO_Attachment_Extra_Data();
|
||||
|
||||
$is_backed_up = false;
|
||||
$original_size = 0;
|
||||
$final_size = 0;
|
||||
|
||||
if ( isset( $post_meta['wio_backuped'][0] ) && $post_meta['wio_backuped'][0] ) {
|
||||
$is_backed_up = true;
|
||||
}
|
||||
|
||||
if ( isset( $post_meta['wio_thumbnails_count'][0] ) ) {
|
||||
$extra_data->set_thumbnails_count( intval( $post_meta['wio_thumbnails_count'][0] ) );
|
||||
}
|
||||
|
||||
if ( isset( $post_meta['wio_original_size'][0] ) ) {
|
||||
$original_size = intval( $post_meta['wio_original_size'][0] );
|
||||
}
|
||||
|
||||
if ( isset( $post_meta['wio_optimized_size'][0] ) ) {
|
||||
$final_size = intval( $post_meta['wio_optimized_size'][0] );
|
||||
}
|
||||
|
||||
if ( isset( $post_meta['wio_original_main_size'][0] ) ) {
|
||||
$extra_data->set_original_main_size( intval( $post_meta['wio_original_main_size'][0] ) );
|
||||
}
|
||||
|
||||
if ( isset( $post_meta['wio_error'][0] ) ) {
|
||||
$extra_data->set_error( 'optimization' );
|
||||
$extra_data->set_error_msg( $post_meta['wio_error'][0] );
|
||||
}
|
||||
|
||||
$level = 'normal';
|
||||
|
||||
if ( isset( $post_meta['wio_optimization_level'][0] ) && ! empty( $post_meta['wio_optimization_level'][0] ) ) {
|
||||
$level = $post_meta['wio_optimization_level'][0];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'server_id' => null,
|
||||
'object_id' => $attachment->ID,
|
||||
'object_name' => $wpdb->posts,
|
||||
'item_type' => 'attachment',
|
||||
'result_status' => ! $final_size ? 'error' : 'success',
|
||||
'processing_level' => $level,
|
||||
'is_backed_up' => $is_backed_up,
|
||||
'original_size' => $original_size,
|
||||
'final_size' => $final_size,
|
||||
'original_mime_type' => $attachment->post_mime_type,
|
||||
'final_mime_type' => $attachment->post_mime_type,
|
||||
'extra_data' => (string) $extra_data,
|
||||
'created_at' => time(),
|
||||
];
|
||||
|
||||
$format = [
|
||||
'%s',
|
||||
'%d',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
'%d',
|
||||
'%d',
|
||||
'%d',
|
||||
'%s',
|
||||
'%s',
|
||||
'%s',
|
||||
'%d',
|
||||
];
|
||||
|
||||
$rows_inserted = $wpdb->insert( RIO_Process_Queue::table_name(), $data, $format );
|
||||
|
||||
if ( $rows_inserted > 0 ) {
|
||||
++$processed_items;
|
||||
|
||||
$attachment_id = absint( $attachment->ID );
|
||||
$wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id='{$attachment_id}' AND meta_key LIKE 'wio_%'" );
|
||||
}
|
||||
}
|
||||
|
||||
$left_items = $attachments_total - $processed_items;
|
||||
// translators: %s is the number of items left to migrate
|
||||
$message = sprintf( __( 'left to migrate: %s items', 'robin-image-optimizer' ), $left_items );
|
||||
$need_more_time = true;
|
||||
|
||||
WRIO_Plugin::app()->logger->info( 'Succefull migrated ' . $processed_items . ' items.' );
|
||||
} else {
|
||||
WRIO_Plugin::app()->logger->info( 'Succefull migrated all items. Finishing-up...' );
|
||||
|
||||
// Assumed to be 2 after 010105.php migration
|
||||
RIO_Process_Queue::update_db_version( 2 );
|
||||
|
||||
$need_more_time = false;
|
||||
$message = __( 'Finishing-up...', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->memory_usage();
|
||||
|
||||
wp_send_json_success(
|
||||
[
|
||||
'need_more_time' => $need_more_time,
|
||||
'message' => $message,
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax действие, которое выполняется для смены текущего multisite блога
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
add_action( 'wp_ajax_wbcr_rio_update_current_blog', function () {
|
||||
check_ajax_referer( 'update_blog_id', 'wpnonce' );
|
||||
$blog_id = (int) WRIO_Plugin::app()->request->post( 'current_blog_id' );
|
||||
$context = sanitize_text_field( WRIO_Plugin::app()->request->post( 'context' ) );
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'current_blog', $blog_id );
|
||||
$image_statistics = WIO_OptimizationTools::getImageStatistics( $context );
|
||||
|
||||
switch_to_blog( $blog_id );
|
||||
$statistic_data = $image_statistics->load();
|
||||
restore_current_blog();
|
||||
|
||||
wp_send_json_success( array(
|
||||
'statistic' => $statistic_data,
|
||||
) );
|
||||
} );*/
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax действие, которое выполняется при сохранении настроек
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX обработчик массовой сохранения уровня сжатия
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_settings_update_level',
|
||||
function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$level = sanitize_text_field( $_POST['level'] );
|
||||
|
||||
if ( ! $level ) {
|
||||
die();
|
||||
}
|
||||
|
||||
if ( ! in_array( $level, [ 'normal', 'aggresive', 'ultra' ] ) ) {
|
||||
die();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'image_optimization_level', $level );
|
||||
die();
|
||||
}
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,921 @@
|
||||
/**
|
||||
* Styles for the Widget to be displayed in the plugin
|
||||
*/
|
||||
|
||||
@positiveColor: #bbd2a1;
|
||||
@webpPositiveColor: #8CC152;
|
||||
@negativeColor: #D2D3D6;
|
||||
@neutralColor: #828282;
|
||||
|
||||
@errorColor: #fb5d49;
|
||||
@waringColor: #ffb635;
|
||||
|
||||
@greyButtonBg: #f3f3f3;
|
||||
@greyButtonColor: #656565;
|
||||
|
||||
@greenButtonBg: #c9deb2;
|
||||
@greenButtonColor: #586549;
|
||||
|
||||
@orangeButtonBg: #fdd599;
|
||||
@orangeButtonColor: #a57b3c;
|
||||
|
||||
#WBCR {
|
||||
.premium-label(@position: relative, @positionTop:-8px, @positionLeft:-10px, @positionRight:auto) {
|
||||
display: inline-block;
|
||||
position: @position;
|
||||
content: 'PRO';
|
||||
background: #ff5722;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
font-style: normal;
|
||||
padding: 4px 6px;
|
||||
margin-left: 4px;
|
||||
vertical-align: top;
|
||||
top: @positionTop;
|
||||
left: @positionLeft;
|
||||
right: @positionRight;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
.factory-bootstrap-000 {
|
||||
select.form-control {
|
||||
background: #efefef url(data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E) no-repeat right 5px top 55% !important;
|
||||
}
|
||||
|
||||
.btn-default.active, .factory-bootstrap-000 .btn-default:active {
|
||||
text-shadow: none;
|
||||
color: #fff;
|
||||
background-color: #33aad5;
|
||||
-webkit-box-shadow: inset 0 1px 1px #0074a2;
|
||||
box-shadow: inset 0 1px 3px #0074a2;
|
||||
border-top: 1px solid #0074a2;
|
||||
border-bottom: 1px solid #0074a2;
|
||||
border-left: 1px solid #0074a2;
|
||||
}
|
||||
|
||||
.btn-group > .btn:last-of-type {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.wio-clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
// This code hides tabs custom folders and nextgen gallery
|
||||
// -----------------------------------------------
|
||||
#io_folders_statistic-wbcr_clearfy-tab, #io_nextgen_gallery_statistic-wbcr_clearfy-tab {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Styling tabs on statistics pages
|
||||
// -----------------------------------------------
|
||||
.wrio-statistic-nav {
|
||||
margin: 0;
|
||||
//box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
background: #efefef;
|
||||
|
||||
ul {
|
||||
margin: 0 0 0 10px;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
//width: 300px;
|
||||
margin: 0 5px 0 0;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 -2px 0 #eaeaea;
|
||||
|
||||
&:hover {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
&.active {
|
||||
//box-shadow: 0 -2px 0 #c9deb2;
|
||||
background: #f7f7f7;
|
||||
border-top: 1px solid #d4d4d4;
|
||||
border-left: 1px solid #d4d4d4;
|
||||
border-right: 1px solid #d4d4d4;
|
||||
border-bottom: 1px solid #f7f7f7;
|
||||
margin-bottom: -1px;
|
||||
|
||||
a {
|
||||
color: #222;
|
||||
|
||||
.wrio-statistic-tab-percent {
|
||||
border: 2px dashed #8bc34a;
|
||||
color: #5e8237;
|
||||
}
|
||||
}
|
||||
|
||||
.dashicons, .dashicons-before:before {
|
||||
color: #ff8b66;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-statistic-tab {
|
||||
display: block;
|
||||
padding: 10px 20px 10px 20px;
|
||||
text-decoration: none;
|
||||
color: #d4d4d4;
|
||||
font-size: 22px;
|
||||
line-height: 2;
|
||||
|
||||
&:active, &:focus {
|
||||
background: 0;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.dashicons, .dashicons-before:before {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 30px;
|
||||
line-height: 1.5;
|
||||
margin-right: 15px;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
|
||||
.wrio-statistic-tab-percent {
|
||||
display: inline-block;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 100px;
|
||||
border: 2px dashed #e4e4e4;
|
||||
padding: 5px;
|
||||
margin-left: 30px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
color: #bdbdbd;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-statistic-tab-premium-label:after {
|
||||
.premium-label(absolute, 10px, auto, 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Table style. The table is used for optimization log
|
||||
.wrio-table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
box-sizing: border-box;
|
||||
border-spacing: 3px;
|
||||
background: #fff;
|
||||
border-top: 2px dashed #cac9c9;
|
||||
|
||||
th, td {
|
||||
padding: 16px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background: #f3f3f3;
|
||||
color: #777777;
|
||||
box-shadow: 0 1px 0 rgb(216, 216, 216);
|
||||
|
||||
&:nth-child(2n+1) {
|
||||
background: #f9f9f9
|
||||
}
|
||||
}
|
||||
|
||||
tr.wrio-error {
|
||||
background-color: #ffe9e9 !important;
|
||||
}
|
||||
|
||||
.wrio-table-spinner {
|
||||
background: url("../img/quick-start-loader.gif") center center no-repeat;
|
||||
}
|
||||
|
||||
.wrio-table-highlighter {
|
||||
display: inline-block;
|
||||
padding: 3px 7px;
|
||||
background: @greyButtonBg;
|
||||
}
|
||||
|
||||
.wbcr-rio-server-success {
|
||||
color: @positiveColor;
|
||||
}
|
||||
|
||||
.wbcr-rio-server-error {
|
||||
color: @errorColor;
|
||||
}
|
||||
|
||||
.wbcr-rio-server-warning {
|
||||
color: @waringColor;
|
||||
}
|
||||
|
||||
&.wbcr-rio-folders-table {
|
||||
td:nth-child(3) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-servers {
|
||||
padding: 20px 20px;
|
||||
|
||||
label {
|
||||
span {
|
||||
display: block;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
color: #b7b2b2;
|
||||
}
|
||||
}
|
||||
|
||||
#wrio-change-optimization-server {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
max-width: 400px;
|
||||
margin-right: 15px;
|
||||
margin-bottom: 0;
|
||||
border: 1px solid #d2d0d0;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.wrio-servers-info {
|
||||
margin: 0 0 0;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.wrio-server-status-wrap {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
|
||||
.wrio-server-status {
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
&.wrio-down {
|
||||
background: #ff5722;
|
||||
}
|
||||
|
||||
&.wrio-stable {
|
||||
background: #8bc34a;
|
||||
}
|
||||
|
||||
&.wrio-server-check-proccess {
|
||||
display: inline-block;
|
||||
height: 10px;
|
||||
width: 30px;
|
||||
background: url("../img/quick-start-loader.gif") center no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.wrio-premium-user-balance-wrap {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
margin-left: 10px;
|
||||
|
||||
.wrio-premium-user-balance {
|
||||
color: #fff;
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
background: #ffc107;
|
||||
}
|
||||
|
||||
.wrio-premium-user-balance-check-proccess {
|
||||
display: inline-block;
|
||||
height: 10px;
|
||||
width: 30px;
|
||||
background: url("../img/quick-start-loader.gif") center no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-premium-user-update-wrap {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
margin-left: 10px;
|
||||
|
||||
.wrio-premium-user-update {
|
||||
color: #fff;
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
background: #ffc107;
|
||||
}
|
||||
|
||||
.wrio-premium-user-update-check-proccess {
|
||||
display: inline-block;
|
||||
height: 10px;
|
||||
width: 30px;
|
||||
background: url("../img/quick-start-loader.gif") center no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wio-columns {
|
||||
overflow: hidden;
|
||||
padding: 15px 0;
|
||||
counter-reset: cols;
|
||||
|
||||
[class^="col-"] {
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.col-1-3 {
|
||||
width: 33.333%;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.col-2-3 {
|
||||
width: 66.666%;
|
||||
padding-left: 28px
|
||||
}
|
||||
|
||||
.col-1-2 {
|
||||
width: 50%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.col-statistics.col-statistics {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.col-chart.col-chart {
|
||||
width: 40%;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background: #f1f1f1b3;
|
||||
color: #abacaf;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
text-align: left;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.wio-col {
|
||||
float: left;
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
-webkit-flex-basis: 50%;
|
||||
-ms-flex-preferred-size: 50%;
|
||||
flex-basis: 50%;
|
||||
}
|
||||
|
||||
.wio-col {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.wio-col + .wio-col {
|
||||
padding-right: 0;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.wio-col:target {
|
||||
animation: wiohello 1s 3 linear backwards;
|
||||
}
|
||||
|
||||
.wio-number-you-optimized {
|
||||
margin-bottom: 1.35em;
|
||||
overflow: hidden;
|
||||
|
||||
#wio-total-optimized-attachments-pct {
|
||||
color: @neutralColor;
|
||||
}
|
||||
|
||||
.wio-number {
|
||||
display: table-cell;
|
||||
padding-right: 15px;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
color: @neutralColor;
|
||||
}
|
||||
|
||||
.wio-text {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
color: @neutralColor;
|
||||
}
|
||||
|
||||
& > p {
|
||||
display: table;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-bars {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.wio-bars p {
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.wio-bars + .wio-number-you-optimized {
|
||||
border-bottom: 0;
|
||||
padding-top: 0.85em;
|
||||
}
|
||||
|
||||
.wio-bars + .wio-number-you-optimized p {
|
||||
color: darken(@positiveColor, 10);
|
||||
}
|
||||
|
||||
.wio-bar-negative {
|
||||
.wio-progress {
|
||||
background: @negativeColor;
|
||||
}
|
||||
|
||||
.wio-barnb {
|
||||
color: darken(@negativeColor, 20);
|
||||
}
|
||||
}
|
||||
|
||||
.wio-progress {
|
||||
height: 8px;
|
||||
transition: width .3s;
|
||||
|
||||
/*.wio-bar-negative {
|
||||
width: 92% !important;
|
||||
}*/
|
||||
}
|
||||
|
||||
.wio-bar-positive {
|
||||
.wio-progress {
|
||||
background: @positiveColor;
|
||||
}
|
||||
|
||||
.wio-barnb {
|
||||
color: darken(@positiveColor, 10);
|
||||
}
|
||||
}
|
||||
|
||||
.wio-bar-primary {
|
||||
.wio-progress {
|
||||
background: @positiveColor;
|
||||
}
|
||||
|
||||
.wio-barnb {
|
||||
color: darken(@positiveColor, 10);
|
||||
}
|
||||
}
|
||||
|
||||
.wio-bar-webp {
|
||||
.wio-progress {
|
||||
background: @webpPositiveColor;
|
||||
}
|
||||
|
||||
.wio-barnb {
|
||||
color: darken(@webpPositiveColor, 10);
|
||||
}
|
||||
}
|
||||
|
||||
.wio-right-outside-number .wio-barnb {
|
||||
display: block;
|
||||
margin-right: -5.25em;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
line-height: .8;
|
||||
}
|
||||
|
||||
/* Doughnut */
|
||||
|
||||
.wio-chart {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.wio-chart-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.wio-chart-container canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wio-overview-chart-container {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.wio-overview-chart-container-webp {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.wio-chart-wrapper {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.wio-chart-percent {
|
||||
color: #afafaf;
|
||||
position: absolute;
|
||||
font-size: 35px;
|
||||
font-weight: 600;
|
||||
left: 50%;
|
||||
bottom: 167px;
|
||||
z-index: 100;
|
||||
transform: translateX(-45%);
|
||||
-webkit-transform: translateX(-45%);
|
||||
-ms-transform: translateX(-45%);
|
||||
}
|
||||
|
||||
.wio-chart-percent span {
|
||||
font-size: 20px;
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
#wio-overview-chart-legend {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend li {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
padding: 3px 8px 2px 31px;
|
||||
font-size: 9px;
|
||||
cursor: default;
|
||||
-webkit-transition: background-color 200ms ease-in-out;
|
||||
-moz-transition: background-color 200ms ease-in-out;
|
||||
-o-transition: background-color 200ms ease-in-out;
|
||||
transition: background-color 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend li span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.wio-optimize-button {
|
||||
min-width: 180px;
|
||||
padding: 12px 30px;
|
||||
background: @greenButtonBg;
|
||||
color: @greenButtonColor;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
font-size: 14px;
|
||||
text-transform: uppercase !important;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: lighten(@greenButtonBg, 10%);
|
||||
color: lighten(@greenButtonColor, 30%);
|
||||
}
|
||||
|
||||
&.wio-running {
|
||||
color: @orangeButtonColor;
|
||||
background: @orangeButtonBg url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
|
||||
padding-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-global-optim-phrase {
|
||||
width: 180px;
|
||||
padding-top: 20px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wio-total-percent, .wio-total-percent-webp {
|
||||
color: darken(@positiveColor, 20);
|
||||
}
|
||||
|
||||
#wio-start-msg-top, #wio-start-msg-right, #wio-start-msg-complete {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wio-text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
span.wio-num {
|
||||
display: inline !important;
|
||||
position: inherit !important;
|
||||
}
|
||||
|
||||
|
||||
// WIDGETS SPACE
|
||||
// -----------------------------------------------
|
||||
.wio-image-optimize-board {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.wio-page-statistic {
|
||||
padding: 20px;
|
||||
|
||||
|
||||
.wio-optimize-statistic {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.wrio-statistic-buttons-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-stat-totals {
|
||||
padding: 20px 20px;
|
||||
background: #efefef;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
|
||||
&__counter {
|
||||
display: inline-block;
|
||||
background: #ffffff;
|
||||
min-width: 30px;
|
||||
height: 25px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 3px;
|
||||
border: 1px dashed #bdb5b5;
|
||||
color: #222;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__loading {
|
||||
color: #fff;
|
||||
background: #fff url("../img/quick-start-loader.gif") center no-repeat !important;
|
||||
background-size: 16px 16px !important;
|
||||
}
|
||||
|
||||
&__totals {
|
||||
|
||||
}
|
||||
|
||||
&__optimized {
|
||||
background: #8bc34a;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-optimization-progress {
|
||||
//margin-top: 30px;
|
||||
background: none;
|
||||
padding: 0;
|
||||
|
||||
h4 {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/*button {
|
||||
padding: 5px 10px;
|
||||
border: 0;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase !important;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
background: @greyButtonBg;
|
||||
color: @greyButtonColor;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&.wbcr-rio-loading {
|
||||
width: 56px;
|
||||
font-size: 0;
|
||||
background: @greyButtonBg url("../img/quick-start-loader.gif") center no-repeat;
|
||||
}
|
||||
|
||||
&.wbcr-rio-selected {
|
||||
background: #f3efe2;
|
||||
color: #d8d8d8;
|
||||
|
||||
&:active {
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
.wbcr-rio-warning-message {
|
||||
padding: 20px;
|
||||
background: #efefef;
|
||||
font-size: 15px;
|
||||
color: #b7b7b7;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-widget {
|
||||
padding: 0 !important;
|
||||
|
||||
.wio-chart-percent {
|
||||
font-size: 44px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wio-bars {
|
||||
width: 60%;
|
||||
margin-left: 155px;
|
||||
}
|
||||
|
||||
.col-chart.col-chart {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.col-controls {
|
||||
width: 45%;
|
||||
padding-left: 5px;
|
||||
padding-top: 110px
|
||||
}
|
||||
|
||||
.wio-doughnut-legend {
|
||||
/*padding-top:30px;*/
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wio-widget-bottom {
|
||||
display: table;
|
||||
padding-top: 20px !important;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.wio-widget-bottom li {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.wio-widget-bottom li:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
// FORMS SPACE
|
||||
// -----------------------------------------------
|
||||
.factory-checkbox {
|
||||
&.wrio-checkbox-premium-label:after {
|
||||
.premium-label();
|
||||
}
|
||||
}
|
||||
|
||||
.factory-checkbox-disabled {
|
||||
input, button {
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
opacity: .65;
|
||||
filter: alpha(opacity=65);
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
#wrio-webp-options, #wrio-error-log-options {
|
||||
h3 {
|
||||
font-size: 14px;
|
||||
margin: 0 0 10px 0;
|
||||
font-weight: 600;
|
||||
color: #565656;
|
||||
}
|
||||
|
||||
.wrio-webp-options-info {
|
||||
color: #8a8787;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0;
|
||||
|
||||
li {
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
clear: both;;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wrio-webp-options-radio, .wrio-error-log-options-checkbox {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-top: 2px;
|
||||
margin-right: 8px;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-webp-options-info, .wrio-error-log-options-info {
|
||||
padding-left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MEDIA SPACE
|
||||
// -----------------------------------------------
|
||||
|
||||
@media (max-width: 830px) {
|
||||
.wio [class^="col-"] {
|
||||
float: none;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.wio .col-1-3,
|
||||
.wio .col-1-2 {
|
||||
width: auto;
|
||||
padding: 0 28px;
|
||||
clear: both;
|
||||
padding-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wiohello {
|
||||
0%, 100% {
|
||||
background: #FFF;
|
||||
}
|
||||
50% {
|
||||
background: #F4F7F9;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
|
||||
.wio-columns {
|
||||
.col-statistics.col-statistics, .col-chart.col-chart {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (max-width: 808px) {
|
||||
.wio-columns {
|
||||
.col-statistics.col-statistics, .col-chart.col-chart {
|
||||
width: auto;
|
||||
float: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.col-chart.col-chart {
|
||||
margin-top: 3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
|
||||
.wio-overview-chart-container {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
|
||||
.wio-overview-chart-container {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.wio-global-optim-phrase {
|
||||
padding-top: 0;
|
||||
width: auto;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* == Custom column & Metabox
|
||||
*/
|
||||
.column-wio_optimized_file.column-wio_optimized_file {
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.column-wio_optimized_file > * {
|
||||
max-width: 235px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media (min-width: 1151px) and (max-width: 1800px) {
|
||||
.column-wio_optimized_file.column-wio_optimized_file {
|
||||
width: 235px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 783px) and (max-width: 1150px) {
|
||||
.column-wio_optimized_file.column-wio_optimized_file {
|
||||
width: 13em;
|
||||
}
|
||||
table.media .column-title .has-media-icon ~ .row-actions.row-actions {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media (max-width: 782px) {
|
||||
table.media .column-wio_optimized_file.column-wio_optimized_file {
|
||||
text-align: left;
|
||||
}
|
||||
table.media .wio-datas-more-action,
|
||||
table.media .wio-datas-actions-links {
|
||||
text-align: center;
|
||||
}
|
||||
table.media .column-wio_optimized_file > *,
|
||||
table.media .column-wio_optimized_file .wio-datas-actions-links a {
|
||||
max-width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media (min-width: 783px) and (max-width: 1150px), (max-width: 360px) {
|
||||
table.media .wio-hide-if-small {
|
||||
position: absolute;
|
||||
margin: -1px;
|
||||
padding: 0;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
border: 0;
|
||||
word-wrap: normal !important; /* Many screen reader and browser combinations announce broken words as they would appear visually. */
|
||||
}
|
||||
}
|
||||
.compat-field-wio .label {
|
||||
vertical-align: top;
|
||||
}
|
||||
.compat-field-wio ul.wio-datas-list {
|
||||
margin-top: 7px;
|
||||
font-size: 11px;
|
||||
}
|
||||
ul.wio-datas-list.wio-datas-list {
|
||||
margin: 0 auto;
|
||||
color: #555;
|
||||
font-size: 10px;
|
||||
}
|
||||
ul.wio-datas-list .big {
|
||||
font-size: 12px;
|
||||
color: #40B1D0;
|
||||
}
|
||||
.wio-data-item {
|
||||
overflow: hidden;
|
||||
}
|
||||
li.wio-data-item {
|
||||
clear: both;
|
||||
margin-bottom: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
ul.wio-datas-list .wio-data-item span.data,
|
||||
ul.wio-datas-list .wio-data-item strong {
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
ul.wio-datas-list .wio-data-item span.data {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.compat-field-wio .wio-datas-list .wio-data-item .data {
|
||||
width: 130px;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
ul.wio-datas-list .wio-data-item strong {
|
||||
text-align: left;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.media-sidebar .wio-datas-list .wio-data-item .data {
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
.media-sidebar .wio-datas-list .wio-data-item strong {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
.media-sidebar .wio-datas-list .wio-data-item .wio-chart {
|
||||
float: left;
|
||||
}
|
||||
.wio-datas-more-action.wio-datas-more-action {
|
||||
margin: .4em auto;
|
||||
background: linear-gradient(to bottom, transparent, transparent 49%, rgba(0,0,0,.075) 50%, rgba(0,0,0,.075) 58%, transparent 58%, transparent);
|
||||
}
|
||||
.wio-datas-more-action a {
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
background: #40B1D0;
|
||||
color: #FFF;
|
||||
text-transform: uppercase;
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
line-height: 1.9;
|
||||
text-decoration: none;
|
||||
}
|
||||
.wio-datas-more-action a.is-open {
|
||||
background: #555;
|
||||
}
|
||||
.wio-datas-more-action a.is-open .dashicons {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
.wio-datas-more-action a .dashicons {
|
||||
font-size: 14px;
|
||||
vertical-align: middle;
|
||||
line-height: .8;
|
||||
}
|
||||
.wio-datas-more-action a .dashicons:before {
|
||||
vertical-align: middle;
|
||||
line-height: 20px;
|
||||
}
|
||||
.wio-datas-more-action .the-text {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ul.wio-datas-details.wio-datas-details {
|
||||
margin: .7em auto;
|
||||
}
|
||||
.wio-datas-details strong {
|
||||
color: #40B1D0;
|
||||
}
|
||||
.wio-datas-details .original {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.wio-datas-actions-links {
|
||||
overflow: hidden;
|
||||
border-top: 2px solid transparent;
|
||||
padding-top: 5px;
|
||||
font-size: 8px;
|
||||
}
|
||||
.nggform .wio-datas-actions-links {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.nggform .row-actions {
|
||||
z-index: 1;
|
||||
}
|
||||
.wio-datas-actions-links a {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding-left: 17px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.compat-field-wio .wio-datas-actions-links {
|
||||
max-width: 300px;
|
||||
}
|
||||
.misc-pub-wio .wio-datas-actions-links {
|
||||
border-top: 2px solid #f2f2f2;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
/* Library */
|
||||
.column-wio_optimized_file .wio-datas-actions-links a {
|
||||
margin: 0 .7em;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
/* Media edition */
|
||||
.compat-field-wio .wio-datas-actions-links a,
|
||||
.misc-pub-wio .wio-datas-actions-links a {
|
||||
font-size: 10px;
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
.media-sidebar .compat-field-wio .wio-datas-actions-links a,
|
||||
.submitbox .misc-pub-wio .wio-datas-actions-links a {
|
||||
display: block;
|
||||
width: auto;
|
||||
float: none;
|
||||
}
|
||||
.wio-datas-actions-links a:only-child {
|
||||
float: none;
|
||||
width: auto;
|
||||
}
|
||||
.wio-datas-details.is-open + .wio-datas-actions-links {
|
||||
border-top-color: rgba(0,0,0,.075);
|
||||
}
|
||||
.wio-datas-actions-links .dashicons {
|
||||
position: absolute;
|
||||
left: 0; top: 4px;
|
||||
width: 12px;
|
||||
margin-right: 2px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Button spacing */
|
||||
td.column-wio_optimized_file {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.column-wio_optimized_file .wio-datas-actions-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px 1em;
|
||||
}
|
||||
.column-wio_optimized_file .wio-datas-actions-links a {
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* PRO labels for dropdown buttons */
|
||||
button[data-value="googlepage"]:after,
|
||||
button[data-value="background"]:after {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
content: 'PRO';
|
||||
background: #ff5722;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
font-style: normal;
|
||||
padding: 4px 6px;
|
||||
margin-left: 4px;
|
||||
vertical-align: top;
|
||||
top: -8px;
|
||||
right: 0;
|
||||
z-index: 11;
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
|
||||
/**
|
||||
* == Columns
|
||||
*/
|
||||
.wio-columns {
|
||||
overflow: hidden;
|
||||
padding: 15px 0;
|
||||
counter-reset: cols;
|
||||
}
|
||||
|
||||
.wio-columns [class^="col-"] {
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.wio-columns .col-1-3 {
|
||||
width: 33.333%;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.wio .col-2-3 {
|
||||
width: 66.666%;
|
||||
padding-left: 28px
|
||||
}
|
||||
|
||||
.wio .col-1-2 {
|
||||
width: 50%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 830px) {
|
||||
.wio [class^="col-"] {
|
||||
float: none;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.wio .col-1-3,
|
||||
.wio .col-1-2 {
|
||||
width: auto;
|
||||
padding: 0 28px;
|
||||
clear: both;
|
||||
padding-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Col, behavior depending on parent */
|
||||
.wio-col {
|
||||
float: left;
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
-webkit-flex-basis: 50%;
|
||||
-ms-flex-preferred-size: 50%;
|
||||
flex-basis: 50%;
|
||||
}
|
||||
|
||||
.wio-col {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.wio-col + .wio-col {
|
||||
padding-right: 0;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.wio-col:target {
|
||||
animation: wiohello 1s 3 linear backwards;
|
||||
}
|
||||
|
||||
@keyframes wiohello {
|
||||
0%, 100% {
|
||||
background: #FFF;
|
||||
}
|
||||
50% {
|
||||
background: #F4F7F9;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-columns .col-statistics.col-statistics {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
@media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
|
||||
.wio-columns .col-statistics.col-statistics,
|
||||
.wio-columns .col-chart.col-chart {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 808px) {
|
||||
.wio-columns .col-statistics.col-statistics,
|
||||
.wio-columns .col-chart.col-chart {
|
||||
width: auto;
|
||||
float: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wio-columns .col-chart.col-chart {
|
||||
margin-top: 3em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Number display */
|
||||
.wio-number-you-optimized {
|
||||
margin-bottom: 1.35em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wio-number-you-optimized .number {
|
||||
display: table-cell;
|
||||
padding-right: 15px;
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.wio-number-you-optimized [id="wio-total-optimized-attachments-pct"] {
|
||||
color: #40B1D0;
|
||||
}
|
||||
|
||||
.wio-number-you-optimized .text {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
color: #626E7B;
|
||||
}
|
||||
|
||||
.wio-number-you-optimized > p {
|
||||
display: table;
|
||||
}
|
||||
|
||||
/* Number and bars */
|
||||
.wio-bars {
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.wio-bars p {
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.wio-bars + .wio-number-you-optimized {
|
||||
border-bottom: 0;
|
||||
padding-top: 0.85em;
|
||||
}
|
||||
|
||||
.wio-bars + .wio-number-you-optimized p {
|
||||
color: #46b1ce;
|
||||
}
|
||||
|
||||
.wio-bar-negative .wio-progress {
|
||||
background: #D2D3D6;
|
||||
}
|
||||
|
||||
.wio-bar-negative .wio-barnb {
|
||||
color: #7A8996;
|
||||
}
|
||||
|
||||
.wio-bar-neutral .wio-progress {
|
||||
background: #F5A623;
|
||||
}
|
||||
|
||||
.wio-space-left .wio-bar-negative .wio-progress {
|
||||
background: #C51162;
|
||||
}
|
||||
|
||||
.wio-progress {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.wio-progress {
|
||||
transition: width .3s;
|
||||
}
|
||||
|
||||
.wio-bar-positive .wio-progress {
|
||||
background: #8CC152;
|
||||
}
|
||||
|
||||
.wio-bar-positive .wio-barnb {
|
||||
color: #8CC152;
|
||||
}
|
||||
|
||||
.wio-bar-primary .wio-progress {
|
||||
background: #8bc34a;
|
||||
}
|
||||
|
||||
.wio-bar-primary .wio-barnb {
|
||||
color: #8bc34a;
|
||||
}
|
||||
|
||||
.right-outside-number .wio-barnb {
|
||||
display: block;
|
||||
margin-right: -5.25em;
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
line-height: .8;
|
||||
}
|
||||
|
||||
|
||||
/* Doughnut */
|
||||
.wio-chart {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.wio-chart-container {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.wio-chart-container canvas {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wio-overview-chart-container {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
|
||||
.wio-overview-chart-container {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-chart-percent {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
margin-top: -.5em;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
font-size: 55px;
|
||||
font-weight: bold;
|
||||
color: #afafaf;
|
||||
}
|
||||
|
||||
.wio-chart-percent span {
|
||||
font-size: 20px;
|
||||
vertical-align: super;
|
||||
}
|
||||
|
||||
#wio-overview-chart-legend {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.imagify-doughnut-legend {
|
||||
margin-top: 38px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend li {
|
||||
display: inline-block;
|
||||
padding-left: 30px;
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
border-radius: 5px;
|
||||
padding: 3px 8px 2px 31px;
|
||||
font-size: 13px;
|
||||
cursor: default;
|
||||
-webkit-transition: background-color 200ms ease-in-out;
|
||||
-moz-transition: background-color 200ms ease-in-out;
|
||||
-o-transition: background-color 200ms ease-in-out;
|
||||
transition: background-color 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend li span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
|
||||
.wio-overview-chart-container {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.wio-doughnut-legend {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
.wio-global-optim-phrase {
|
||||
padding-top: 0;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.wio-global-optim-phrase {
|
||||
width: 180px;
|
||||
padding-top: 20px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wio-clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.wio-total-percent {
|
||||
color: #46b1ce;
|
||||
}
|
||||
|
||||
.wio-columns .col-chart.col-chart {
|
||||
width: 40%;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
background: #f1f1f1b3;
|
||||
color: #abacaf;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
text-align: left;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.wio-image-optimize-board {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.wio-optimize-button {
|
||||
width: 180px;
|
||||
padding: 12px 30px;
|
||||
background: #c9deb2;
|
||||
color: #586549;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
font-size: 16px;
|
||||
text-transform: uppercase !important;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.wio-optimize-button:active {
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.wio-optimize-button.running {
|
||||
color: #a57b3c;
|
||||
background: #fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
|
||||
}
|
||||
|
||||
.wio-widget-bottom {
|
||||
display: table;
|
||||
padding-top: 20px !important;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.wio-widget-bottom li {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.wio-widget-bottom li:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wio-imagify-widget {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.wio-imagify-widget .wio-bars {
|
||||
width: 60%;
|
||||
margin-left: 155px;
|
||||
}
|
||||
|
||||
.wio-imagify-widget .col-chart.col-chart {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wio-imagify-widget .col-controls {
|
||||
width: 45%;
|
||||
padding-left: 5px;
|
||||
padding-top: 110px
|
||||
}
|
||||
|
||||
.wio-imagify-widget .wio-doughnut-legend {
|
||||
/*padding-top:30px;*/
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#wio-start-msg-top, #wio-start-msg-right, #wio-start-msg-complete {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wio-text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
span.wio-num {
|
||||
display: inline !important;
|
||||
position: inherit !important;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/* Sub Layer */
|
||||
body[class*="_rio"] .swal2-container.swal2-shown {
|
||||
background: rgba(16, 17, 21, 0.9);
|
||||
z-index: 100000;
|
||||
}
|
||||
.wrio-modal {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.wrio-modal .swal2-close {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
.wrio-modal .swal2-modal {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.wrio-modal .swal2-icon {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.wrio-modal .swal2-title {
|
||||
margin: 0;
|
||||
padding: 28px 32px;
|
||||
font-size: 24px;
|
||||
text-align: left;
|
||||
color: #fff !important;
|
||||
background: #3e3e3e !important;
|
||||
}
|
||||
.wrio-modal .swal2-content {
|
||||
font-size: 14px;
|
||||
padding: 28px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper {
|
||||
margin-top: 0;
|
||||
padding: 22px;
|
||||
background: #F4F7F9;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper a.button svg {
|
||||
margin-right: 12px;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper button.loading {
|
||||
border-radius: 100% !important;
|
||||
height: 40px !important;
|
||||
padding: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper button.swal2-styled {
|
||||
height: auto;
|
||||
padding: 12px 32px;
|
||||
margin: 10px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
outline: none;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper button.swal2-styled.swal2-confirm {
|
||||
background-color: #fdd599 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #ceac7a !important;
|
||||
color: #a57b3c !important;
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper button.swal2-styled.swal2-cancel {
|
||||
background-color: #d2d2d2 !important;
|
||||
color: #656464 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #a9a9a9;
|
||||
/*background-color: #c9deb2 !important;
|
||||
color: #606956 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #a7b994;*/
|
||||
}
|
||||
.wrio-modal .swal2-buttonswrapper button.swal2-styled:focus,
|
||||
.wrio-modal .swal2-buttonswrapper button.swal2-styled:hover {
|
||||
outline: none;
|
||||
text-shadow: none;
|
||||
color: #FFF;
|
||||
}
|
||||
.wrio-modal-warning {
|
||||
background: #FF5722 !important;
|
||||
}
|
||||
.wrio-modal-warning .swal2-title {
|
||||
text-align: center;
|
||||
color: #222 !important;
|
||||
background: #efefef !important;
|
||||
}
|
||||
.wrio-modal-warning .swal2-content {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal-warning .swal2-buttonswrapper {
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal-error {
|
||||
background: #dec2c0 !important;
|
||||
}
|
||||
.wrio-modal-error .swal2-title {
|
||||
text-align: center;
|
||||
color: #222 !important;
|
||||
background: #efefef !important;
|
||||
}
|
||||
.wrio-modal-error .swal2-content {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal-error .swal2-buttonswrapper {
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal-optimization-way {
|
||||
background: #1F2332 !important;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-swal-subtitle {
|
||||
padding: 0 0 28px;
|
||||
margin-top: 0px;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
text-align: left;
|
||||
color: #8c8888;
|
||||
background: #efefef;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 5px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #787575;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos li:first-child {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos li:last-child {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos li + li {
|
||||
border-top: 1px solid #E9EFF2;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-list-infos a:before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-info-icon {
|
||||
flex-grow: 0;
|
||||
flex-basis: 50px;
|
||||
}
|
||||
.wrio-modal-optimization-way .wrio-info-icon + span {
|
||||
padding-left: 20px;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["sweetalert-custom.less"],"names":[],"mappings":";AACA,IAAI,eAAgB,iBAAgB;EAClC,iCAAA;EACA,eAAA;;AAGF;EACE,qBAAA;;AADF,WAGE;EACE,yBAAA;;AAJJ,WAOE;EACE,kBAAA;;AARJ,WAWE;EACE,mBAAA;;AAZJ,WAeE;EACE,SAAA;EACA,kBAAA;EACA,eAAA;EACA,gBAAA;EACA,WAAA;EACA,mBAAA;;AArBJ,WAwBE;EACE,eAAA;EACA,kBAAA;EACA,mBAAA;;AA3BJ,WA8BE;EACE,aAAA;EACA,aAAA;EACA,mBAAA;;AAjCJ,WA8BE,sBAKE,EAAC,OAAQ;EACP,kBAAA;EACA,oBAAA;;AArCN,WA8BE,sBAUE,OAAM;EACJ,8BAAA;EACA,uBAAA;EACA,qBAAA;EACA,2BAAA;;AA5CN,WA8BE,sBAiBE,OAAM;EACJ,YAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;EACA,kBAAA;EACA,iBAAA;EACA,aAAA;;AAEA,WA5BJ,sBAiBE,OAAM,aAWH;EACC,yBAAA;EACA,4BAAA;EACA,2BAAA;EACA,cAAA;;AAGF,WAnCJ,sBAiBE,OAAM,aAkBH;EACC,yBAAA;EACA,cAAA;EACA,4BAAA;EACA,2BAAA;;;;;;AAOF,WA9CJ,sBAiBE,OAAM,aA6BH;AAAQ,WA9Cb,sBAiBE,OAAM,aA6BM;EACR,aAAA;EACA,iBAAA;EACA,WAAA;;AAMR;EACE,mBAAA;;AADF,mBAGE;EACE,kBAAA;EACA,WAAA;EACA,mBAAA;;AANJ,mBASE;EACE,eAAA;EACA,uBAAA;EACA,mBAAA;;AAZJ,mBAeE;EACE,mBAAA;;AAIJ;EACE,mBAAA;;AADF,iBAGE;EACE,kBAAA;EACA,WAAA;EACA,mBAAA;;AANJ,iBASE;EACE,eAAA;EACA,uBAAA;EACA,mBAAA;;AAZJ,iBAeE;EACE,mBAAA;;AAIJ;EACE,mBAAA;;AADF,4BAGE;EACE,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;EACA,mBAAA;;AAVJ,4BAaE;EACE,SAAA;EACA,UAAA;;AAfJ,4BAaE,iBAIE;EACE,aAAA;EACA,mBAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;EACA,gBAAA;EACA,cAAA;;AAxBN,4BAaE,iBAcE,GAAE;EACA,gBAAA;;AA5BN,4BAaE,iBAkBE,GAAE;EACA,mBAAA;;AAhCN,4BAaE,iBAsBE,GAAG;EACD,6BAAA;;AApCN,4BAaE,iBA0BE,EAAC;EACC,SAAS,EAAT;EACA,cAAA;;AAzCN,4BA6CE;EACE,YAAA;EACA,gBAAA;;AA/CJ,4BAkDE,gBAAgB;EACd,kBAAA","file":"sweetalert-custom.css"}
|
||||
@@ -0,0 +1,185 @@
|
||||
/* Sub Layer */
|
||||
body[class*="_rio"] .swal2-container.swal2-shown {
|
||||
background: rgba(16, 17, 21, 0.9);
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
.wrio-modal {
|
||||
padding: 0 !important;
|
||||
|
||||
.swal2-close {
|
||||
color: rgba(0, 0, 0, .8);
|
||||
}
|
||||
|
||||
.swal2-modal {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.swal2-icon {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.swal2-title {
|
||||
margin: 0;
|
||||
padding: 28px 32px;
|
||||
font-size: 24px;
|
||||
text-align: left;
|
||||
color: #fff !important;
|
||||
background: #3e3e3e !important;
|
||||
}
|
||||
|
||||
.swal2-content {
|
||||
font-size: 14px;
|
||||
padding: 28px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.swal2-buttonswrapper {
|
||||
margin-top: 0;
|
||||
padding: 22px;
|
||||
background: #F4F7F9;
|
||||
|
||||
a.button svg {
|
||||
margin-right: 12px;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
|
||||
button.loading {
|
||||
border-radius: 100% !important;
|
||||
height: 40px !important;
|
||||
padding: 0 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
button.swal2-styled {
|
||||
height: auto;
|
||||
padding: 12px 32px;
|
||||
margin: 10px;
|
||||
font-size: 14px;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
outline: none;
|
||||
|
||||
&.swal2-confirm {
|
||||
background-color: #fdd599 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #ceac7a !important;
|
||||
color: #a57b3c !important;
|
||||
}
|
||||
|
||||
&.swal2-cancel {
|
||||
background-color: #d2d2d2 !important;
|
||||
color: #656464 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #a9a9a9;
|
||||
/*background-color: #c9deb2 !important;
|
||||
color: #606956 !important;
|
||||
text-shadow: none !important;
|
||||
box-shadow: 0 3px 0 #a7b994;*/
|
||||
}
|
||||
|
||||
&:focus, &:hover {
|
||||
outline: none;
|
||||
text-shadow: none;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-modal-warning {
|
||||
background: #FF5722 !important;
|
||||
|
||||
.swal2-title {
|
||||
text-align: center;
|
||||
color: #222 !important;
|
||||
background: #efefef !important;
|
||||
}
|
||||
|
||||
.swal2-content {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.swal2-buttonswrapper {
|
||||
background: #efefef;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-modal-error {
|
||||
background: #dec2c0 !important;
|
||||
|
||||
.swal2-title {
|
||||
text-align: center;
|
||||
color: #222 !important;
|
||||
background: #efefef !important;
|
||||
}
|
||||
|
||||
.swal2-content {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px 32px;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.swal2-buttonswrapper {
|
||||
background: #efefef;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-modal-optimization-way {
|
||||
background: #1F2332 !important;
|
||||
|
||||
.wrio-swal-subtitle {
|
||||
padding: 0 0 28px;
|
||||
margin-top: 0px;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
text-align: left;
|
||||
color: #8c8888;
|
||||
background: #efefef;
|
||||
}
|
||||
|
||||
.wrio-list-infos {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 5px;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #8c8888;
|
||||
}
|
||||
|
||||
li:first-child {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
li + li {
|
||||
border-top: 1px solid #E9EFF2;
|
||||
}
|
||||
|
||||
a:before {
|
||||
content: '';
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.wrio-info-icon {
|
||||
flex-grow: 0;
|
||||
flex-basis: 50px;
|
||||
}
|
||||
|
||||
.wrio-info-icon + span {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
body.swal2-shown {
|
||||
overflow-y: hidden; }
|
||||
|
||||
body.swal2-iosfix {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0; }
|
||||
|
||||
.swal2-container {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 10px;
|
||||
background-color: transparent;
|
||||
z-index: 1060; }
|
||||
.swal2-container.swal2-fade {
|
||||
-webkit-transition: background-color .1s;
|
||||
transition: background-color .1s; }
|
||||
.swal2-container.swal2-shown {
|
||||
background-color: rgba(0, 0, 0, 0.4); }
|
||||
|
||||
.swal2-modal {
|
||||
background-color: #fff;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
border-radius: 5px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
display: none;
|
||||
position: relative;
|
||||
max-width: 100%; }
|
||||
.swal2-modal:focus {
|
||||
outline: none; }
|
||||
.swal2-modal.swal2-loading {
|
||||
overflow-y: hidden; }
|
||||
.swal2-modal .swal2-title {
|
||||
color: #595959;
|
||||
font-size: 30px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
text-transform: none;
|
||||
position: relative;
|
||||
margin: 0 0 .4em;
|
||||
padding: 0;
|
||||
display: block;
|
||||
word-wrap: break-word; }
|
||||
.swal2-modal .swal2-buttonswrapper {
|
||||
margin-top: 15px; }
|
||||
.swal2-modal .swal2-buttonswrapper:not(.swal2-loading) .swal2-styled[disabled] {
|
||||
opacity: .4;
|
||||
cursor: no-drop; }
|
||||
.swal2-modal .swal2-buttonswrapper.swal2-loading .swal2-styled.swal2-confirm {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border: 4px solid transparent;
|
||||
border-color: transparent;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
margin: 7.5px;
|
||||
vertical-align: top;
|
||||
background-color: transparent !important;
|
||||
color: transparent;
|
||||
cursor: default;
|
||||
border-radius: 100%;
|
||||
-webkit-animation: rotate-loading 1.5s linear 0s infinite normal;
|
||||
animation: rotate-loading 1.5s linear 0s infinite normal;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none; }
|
||||
.swal2-modal .swal2-buttonswrapper.swal2-loading .swal2-styled.swal2-cancel {
|
||||
margin-left: 30px;
|
||||
margin-right: 30px; }
|
||||
.swal2-modal .swal2-buttonswrapper.swal2-loading :not(.swal2-styled).swal2-confirm::after {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
margin-left: 5px 0 15px;
|
||||
vertical-align: -1px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
border: 3px solid #999999;
|
||||
-webkit-box-shadow: 1px 1px 1px #fff;
|
||||
box-shadow: 1px 1px 1px #fff;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
-webkit-animation: rotate-loading 1.5s linear 0s infinite normal;
|
||||
animation: rotate-loading 1.5s linear 0s infinite normal; }
|
||||
.swal2-modal .swal2-styled {
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
margin: 15px 5px 0;
|
||||
padding: 10px 32px; }
|
||||
.swal2-modal .swal2-image {
|
||||
margin: 20px auto;
|
||||
max-width: 100%; }
|
||||
.swal2-modal .swal2-close {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 38px;
|
||||
height: 40px;
|
||||
font-size: 36px;
|
||||
line-height: 40px;
|
||||
font-family: serif;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 8px;
|
||||
cursor: pointer;
|
||||
color: #cccccc;
|
||||
-webkit-transition: color .1s ease;
|
||||
transition: color .1s ease; }
|
||||
.swal2-modal .swal2-close:hover {
|
||||
color: #d55; }
|
||||
.swal2-modal > .swal2-input,
|
||||
.swal2-modal > .swal2-file,
|
||||
.swal2-modal > .swal2-textarea,
|
||||
.swal2-modal > .swal2-select,
|
||||
.swal2-modal > .swal2-radio,
|
||||
.swal2-modal > .swal2-checkbox {
|
||||
display: none; }
|
||||
.swal2-modal .swal2-content {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
position: relative;
|
||||
float: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
color: #545454;
|
||||
word-wrap: break-word; }
|
||||
.swal2-modal .swal2-input,
|
||||
.swal2-modal .swal2-file,
|
||||
.swal2-modal .swal2-textarea,
|
||||
.swal2-modal .swal2-select,
|
||||
.swal2-modal .swal2-radio,
|
||||
.swal2-modal .swal2-checkbox {
|
||||
margin: 20px auto; }
|
||||
.swal2-modal .swal2-input,
|
||||
.swal2-modal .swal2-file,
|
||||
.swal2-modal .swal2-textarea {
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-size: 18px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d9d9d9;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06);
|
||||
-webkit-transition: border-color box-shadow .3s;
|
||||
transition: border-color box-shadow .3s; }
|
||||
.swal2-modal .swal2-input.swal2-inputerror,
|
||||
.swal2-modal .swal2-file.swal2-inputerror,
|
||||
.swal2-modal .swal2-textarea.swal2-inputerror {
|
||||
border-color: #f27474 !important;
|
||||
-webkit-box-shadow: 0 0 2px #f27474 !important;
|
||||
box-shadow: 0 0 2px #f27474 !important; }
|
||||
.swal2-modal .swal2-input:focus,
|
||||
.swal2-modal .swal2-file:focus,
|
||||
.swal2-modal .swal2-textarea:focus {
|
||||
outline: none;
|
||||
border: 1px solid #b4dbed;
|
||||
-webkit-box-shadow: 0 0 3px #c4e6f5;
|
||||
box-shadow: 0 0 3px #c4e6f5; }
|
||||
.swal2-modal .swal2-input:focus::-webkit-input-placeholder,
|
||||
.swal2-modal .swal2-file:focus::-webkit-input-placeholder,
|
||||
.swal2-modal .swal2-textarea:focus::-webkit-input-placeholder {
|
||||
-webkit-transition: opacity .3s .03s ease;
|
||||
transition: opacity .3s .03s ease;
|
||||
opacity: .8; }
|
||||
.swal2-modal .swal2-input:focus:-ms-input-placeholder,
|
||||
.swal2-modal .swal2-file:focus:-ms-input-placeholder,
|
||||
.swal2-modal .swal2-textarea:focus:-ms-input-placeholder {
|
||||
-webkit-transition: opacity .3s .03s ease;
|
||||
transition: opacity .3s .03s ease;
|
||||
opacity: .8; }
|
||||
.swal2-modal .swal2-input:focus::placeholder,
|
||||
.swal2-modal .swal2-file:focus::placeholder,
|
||||
.swal2-modal .swal2-textarea:focus::placeholder {
|
||||
-webkit-transition: opacity .3s .03s ease;
|
||||
transition: opacity .3s .03s ease;
|
||||
opacity: .8; }
|
||||
.swal2-modal .swal2-input::-webkit-input-placeholder,
|
||||
.swal2-modal .swal2-file::-webkit-input-placeholder,
|
||||
.swal2-modal .swal2-textarea::-webkit-input-placeholder {
|
||||
color: #e6e6e6; }
|
||||
.swal2-modal .swal2-input:-ms-input-placeholder,
|
||||
.swal2-modal .swal2-file:-ms-input-placeholder,
|
||||
.swal2-modal .swal2-textarea:-ms-input-placeholder {
|
||||
color: #e6e6e6; }
|
||||
.swal2-modal .swal2-input::placeholder,
|
||||
.swal2-modal .swal2-file::placeholder,
|
||||
.swal2-modal .swal2-textarea::placeholder {
|
||||
color: #e6e6e6; }
|
||||
.swal2-modal .swal2-range input {
|
||||
float: left;
|
||||
width: 80%; }
|
||||
.swal2-modal .swal2-range output {
|
||||
float: right;
|
||||
width: 20%;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center; }
|
||||
.swal2-modal .swal2-range input,
|
||||
.swal2-modal .swal2-range output {
|
||||
height: 43px;
|
||||
line-height: 43px;
|
||||
vertical-align: middle;
|
||||
margin: 20px auto;
|
||||
padding: 0; }
|
||||
.swal2-modal .swal2-input {
|
||||
height: 43px;
|
||||
padding: 0 12px; }
|
||||
.swal2-modal .swal2-input[type='number'] {
|
||||
max-width: 150px; }
|
||||
.swal2-modal .swal2-file {
|
||||
font-size: 20px; }
|
||||
.swal2-modal .swal2-textarea {
|
||||
height: 108px;
|
||||
padding: 12px; }
|
||||
.swal2-modal .swal2-select {
|
||||
color: #545454;
|
||||
font-size: inherit;
|
||||
padding: 5px 10px;
|
||||
min-width: 40%;
|
||||
max-width: 100%; }
|
||||
.swal2-modal .swal2-radio {
|
||||
border: 0; }
|
||||
.swal2-modal .swal2-radio label:not(:first-child) {
|
||||
margin-left: 20px; }
|
||||
.swal2-modal .swal2-radio input,
|
||||
.swal2-modal .swal2-radio span {
|
||||
vertical-align: middle; }
|
||||
.swal2-modal .swal2-radio input {
|
||||
margin: 0 3px 0 0; }
|
||||
.swal2-modal .swal2-checkbox {
|
||||
color: #545454; }
|
||||
.swal2-modal .swal2-checkbox input,
|
||||
.swal2-modal .swal2-checkbox span {
|
||||
vertical-align: middle; }
|
||||
.swal2-modal .swal2-validationerror {
|
||||
background-color: #f0f0f0;
|
||||
margin: 0 -20px;
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
color: gray;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
display: none; }
|
||||
.swal2-modal .swal2-validationerror::before {
|
||||
content: '!';
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: #ea7d7d;
|
||||
color: #fff;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
margin-right: 10px; }
|
||||
|
||||
@supports (-ms-accelerator: true) {
|
||||
.swal2-range input {
|
||||
width: 100% !important; }
|
||||
.swal2-range output {
|
||||
display: none; } }
|
||||
|
||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||
.swal2-range input {
|
||||
width: 100% !important; }
|
||||
.swal2-range output {
|
||||
display: none; } }
|
||||
|
||||
.swal2-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid transparent;
|
||||
border-radius: 50%;
|
||||
margin: 20px auto 30px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
cursor: default;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none; }
|
||||
.swal2-icon.swal2-error {
|
||||
border-color: #f27474; }
|
||||
.swal2-icon.swal2-error .swal2-x-mark {
|
||||
position: relative;
|
||||
display: block; }
|
||||
.swal2-icon.swal2-error [class^='swal2-x-mark-line'] {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
width: 47px;
|
||||
background-color: #f27474;
|
||||
display: block;
|
||||
top: 37px;
|
||||
border-radius: 2px; }
|
||||
.swal2-icon.swal2-error [class^='swal2-x-mark-line'][class$='left'] {
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
left: 17px; }
|
||||
.swal2-icon.swal2-error [class^='swal2-x-mark-line'][class$='right'] {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
right: 16px; }
|
||||
.swal2-icon.swal2-warning {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
color: #f8bb86;
|
||||
border-color: #facea8;
|
||||
font-size: 60px;
|
||||
line-height: 80px;
|
||||
text-align: center; }
|
||||
.swal2-icon.swal2-info {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
color: #3fc3ee;
|
||||
border-color: #9de0f6;
|
||||
font-size: 60px;
|
||||
line-height: 80px;
|
||||
text-align: center; }
|
||||
.swal2-icon.swal2-question {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
color: #87adbd;
|
||||
border-color: #c9dae1;
|
||||
font-size: 60px;
|
||||
line-height: 80px;
|
||||
text-align: center; }
|
||||
.swal2-icon.swal2-success {
|
||||
border-color: #a5dc86; }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-circular-line'] {
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 120px;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg); }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-circular-line'][class$='left'] {
|
||||
border-radius: 120px 0 0 120px;
|
||||
top: -7px;
|
||||
left: -33px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform-origin: 60px 60px;
|
||||
transform-origin: 60px 60px; }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-circular-line'][class$='right'] {
|
||||
border-radius: 0 120px 120px 0;
|
||||
top: -11px;
|
||||
left: 30px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform-origin: 0 60px;
|
||||
transform-origin: 0 60px; }
|
||||
.swal2-icon.swal2-success .swal2-success-ring {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 4px solid rgba(165, 220, 134, 0.2);
|
||||
border-radius: 50%;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
position: absolute;
|
||||
left: -4px;
|
||||
top: -4px;
|
||||
z-index: 2; }
|
||||
.swal2-icon.swal2-success .swal2-success-fix {
|
||||
width: 7px;
|
||||
height: 90px;
|
||||
position: absolute;
|
||||
left: 28px;
|
||||
top: 8px;
|
||||
z-index: 1;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-line'] {
|
||||
height: 5px;
|
||||
background-color: #a5dc86;
|
||||
display: block;
|
||||
border-radius: 2px;
|
||||
position: absolute;
|
||||
z-index: 2; }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-line'][class$='tip'] {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 46px;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg); }
|
||||
.swal2-icon.swal2-success [class^='swal2-success-line'][class$='long'] {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
|
||||
.swal2-progresssteps {
|
||||
font-weight: 600;
|
||||
margin: 0 0 20px;
|
||||
padding: 0; }
|
||||
.swal2-progresssteps li {
|
||||
display: inline-block;
|
||||
position: relative; }
|
||||
.swal2-progresssteps .swal2-progresscircle {
|
||||
background: #3085d6;
|
||||
border-radius: 2em;
|
||||
color: #fff;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
text-align: center;
|
||||
width: 2em;
|
||||
z-index: 20; }
|
||||
.swal2-progresssteps .swal2-progresscircle:first-child {
|
||||
margin-left: 0; }
|
||||
.swal2-progresssteps .swal2-progresscircle:last-child {
|
||||
margin-right: 0; }
|
||||
.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep {
|
||||
background: #3085d6; }
|
||||
.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep ~ .swal2-progresscircle {
|
||||
background: #add8e6; }
|
||||
.swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep ~ .swal2-progressline {
|
||||
background: #add8e6; }
|
||||
.swal2-progresssteps .swal2-progressline {
|
||||
background: #3085d6;
|
||||
height: .4em;
|
||||
margin: 0 -1px;
|
||||
z-index: 10; }
|
||||
|
||||
[class^='swal2'] {
|
||||
-webkit-tap-highlight-color: transparent; }
|
||||
|
||||
@-webkit-keyframes showSweetAlert {
|
||||
0% {
|
||||
-webkit-transform: scale(0.7);
|
||||
transform: scale(0.7); }
|
||||
45% {
|
||||
-webkit-transform: scale(1.05);
|
||||
transform: scale(1.05); }
|
||||
80% {
|
||||
-webkit-transform: scale(0.95);
|
||||
transform: scale(0.95); }
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
|
||||
@keyframes showSweetAlert {
|
||||
0% {
|
||||
-webkit-transform: scale(0.7);
|
||||
transform: scale(0.7); }
|
||||
45% {
|
||||
-webkit-transform: scale(1.05);
|
||||
transform: scale(1.05); }
|
||||
80% {
|
||||
-webkit-transform: scale(0.95);
|
||||
transform: scale(0.95); }
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1); } }
|
||||
|
||||
@-webkit-keyframes hideSweetAlert {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
opacity: 1; }
|
||||
100% {
|
||||
-webkit-transform: scale(0.5);
|
||||
transform: scale(0.5);
|
||||
opacity: 0; } }
|
||||
|
||||
@keyframes hideSweetAlert {
|
||||
0% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
opacity: 1; }
|
||||
100% {
|
||||
-webkit-transform: scale(0.5);
|
||||
transform: scale(0.5);
|
||||
opacity: 0; } }
|
||||
|
||||
.swal2-show {
|
||||
-webkit-animation: showSweetAlert 0.3s;
|
||||
animation: showSweetAlert 0.3s; }
|
||||
.swal2-show.swal2-noanimation {
|
||||
-webkit-animation: none;
|
||||
animation: none; }
|
||||
|
||||
.swal2-hide {
|
||||
-webkit-animation: hideSweetAlert 0.15s forwards;
|
||||
animation: hideSweetAlert 0.15s forwards; }
|
||||
.swal2-hide.swal2-noanimation {
|
||||
-webkit-animation: none;
|
||||
animation: none; }
|
||||
|
||||
@-webkit-keyframes animate-success-tip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px; }
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px; }
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px; } }
|
||||
|
||||
@keyframes animate-success-tip {
|
||||
0% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
54% {
|
||||
width: 0;
|
||||
left: 1px;
|
||||
top: 19px; }
|
||||
70% {
|
||||
width: 50px;
|
||||
left: -8px;
|
||||
top: 37px; }
|
||||
84% {
|
||||
width: 17px;
|
||||
left: 21px;
|
||||
top: 48px; }
|
||||
100% {
|
||||
width: 25px;
|
||||
left: 14px;
|
||||
top: 45px; } }
|
||||
|
||||
@-webkit-keyframes animate-success-long {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0;
|
||||
top: 35px; }
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px; } }
|
||||
|
||||
@keyframes animate-success-long {
|
||||
0% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
65% {
|
||||
width: 0;
|
||||
right: 46px;
|
||||
top: 54px; }
|
||||
84% {
|
||||
width: 55px;
|
||||
right: 0;
|
||||
top: 35px; }
|
||||
100% {
|
||||
width: 47px;
|
||||
right: 8px;
|
||||
top: 38px; } }
|
||||
|
||||
@-webkit-keyframes rotatePlaceholder {
|
||||
0% {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
5% {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
12% {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg); } }
|
||||
|
||||
@keyframes rotatePlaceholder {
|
||||
0% {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
5% {
|
||||
-webkit-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg); }
|
||||
12% {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(-405deg);
|
||||
transform: rotate(-405deg); } }
|
||||
|
||||
.swal2-animate-success-line-tip {
|
||||
-webkit-animation: animate-success-tip 0.75s;
|
||||
animation: animate-success-tip 0.75s; }
|
||||
|
||||
.swal2-animate-success-line-long {
|
||||
-webkit-animation: animate-success-long 0.75s;
|
||||
animation: animate-success-long 0.75s; }
|
||||
|
||||
.swal2-success.swal2-animate-success-icon .swal2-success-circular-line-right {
|
||||
-webkit-animation: rotatePlaceholder 4.25s ease-in;
|
||||
animation: rotatePlaceholder 4.25s ease-in; }
|
||||
|
||||
@-webkit-keyframes animate-error-icon {
|
||||
0% {
|
||||
-webkit-transform: rotateX(100deg);
|
||||
transform: rotateX(100deg);
|
||||
opacity: 0; }
|
||||
100% {
|
||||
-webkit-transform: rotateX(0deg);
|
||||
transform: rotateX(0deg);
|
||||
opacity: 1; } }
|
||||
|
||||
@keyframes animate-error-icon {
|
||||
0% {
|
||||
-webkit-transform: rotateX(100deg);
|
||||
transform: rotateX(100deg);
|
||||
opacity: 0; }
|
||||
100% {
|
||||
-webkit-transform: rotateX(0deg);
|
||||
transform: rotateX(0deg);
|
||||
opacity: 1; } }
|
||||
|
||||
.swal2-animate-error-icon {
|
||||
-webkit-animation: animate-error-icon 0.5s;
|
||||
animation: animate-error-icon 0.5s; }
|
||||
|
||||
@-webkit-keyframes animate-x-mark {
|
||||
0% {
|
||||
-webkit-transform: scale(0.4);
|
||||
transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
50% {
|
||||
-webkit-transform: scale(0.4);
|
||||
transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
80% {
|
||||
-webkit-transform: scale(1.15);
|
||||
transform: scale(1.15);
|
||||
margin-top: -6px; }
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1; } }
|
||||
|
||||
@keyframes animate-x-mark {
|
||||
0% {
|
||||
-webkit-transform: scale(0.4);
|
||||
transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
50% {
|
||||
-webkit-transform: scale(0.4);
|
||||
transform: scale(0.4);
|
||||
margin-top: 26px;
|
||||
opacity: 0; }
|
||||
80% {
|
||||
-webkit-transform: scale(1.15);
|
||||
transform: scale(1.15);
|
||||
margin-top: -6px; }
|
||||
100% {
|
||||
-webkit-transform: scale(1);
|
||||
transform: scale(1);
|
||||
margin-top: 0;
|
||||
opacity: 1; } }
|
||||
|
||||
.swal2-animate-x-mark {
|
||||
-webkit-animation: animate-x-mark 0.5s;
|
||||
animation: animate-x-mark 0.5s; }
|
||||
|
||||
@-webkit-keyframes rotate-loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
|
||||
@keyframes rotate-loading {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
transform: rotate(360deg); } }
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Subscribe Widget Styles
|
||||
* Matches the onp-container / license-manager styling
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
*/
|
||||
|
||||
.wrio-subscribe-widget {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
border-radius: 5px;
|
||||
background: rgb(255, 255, 255);
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 1) 63%, rgba(246, 246, 246, 1) 100%);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
box-sizing: border-box;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__header {
|
||||
padding: 20px;
|
||||
padding-bottom: 0;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__header h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__body {
|
||||
padding: 20px;
|
||||
padding-top: 15px;
|
||||
border-bottom-right-radius: 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__messages {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__success {
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
color: #16a34a;
|
||||
padding: 12px 15px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 170%;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__error {
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
color: #dc2626;
|
||||
padding: 12px 15px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 170%;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__form {
|
||||
/* Form container */
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__form p {
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 170%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__field-wrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__email {
|
||||
flex: 1;
|
||||
font-size: 18px;
|
||||
line-height: 20px;
|
||||
height: 36px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #7e8993;
|
||||
border-radius: 4px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__email:focus {
|
||||
border-color: #007cba;
|
||||
box-shadow: 0 0 0 1px #007cba;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__button {
|
||||
white-space: nowrap;
|
||||
padding: 7px 14px 6px 14px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget .wrio-subscribe-widget__checkbox-label {
|
||||
display: inline-block;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
line-height: 170%;
|
||||
margin-top: 5px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__checkbox-label input[type="checkbox"] {
|
||||
flex-shrink: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__checkbox-label a {
|
||||
color: #0073aa;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wrio-subscribe-widget__checkbox-label a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
10
wp-content/plugins/robin-image-optimizer/admin/assets/js/Chart.min.js
vendored
Normal file
10
wp-content/plugins/robin-image-optimizer/admin/assets/js/Chart.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,470 @@
|
||||
jQuery(function ($) {
|
||||
/**
|
||||
* Factory function to create a bulk conversion handler for a specific format.
|
||||
*
|
||||
* @param {string} format - The conversion format ('webp' or 'avif')
|
||||
* @param {string} buttonSelector - jQuery selector for the start button
|
||||
* @returns {Object} - Bulk conversion handler object
|
||||
*/
|
||||
function createBulkConversion(format, buttonSelector) {
|
||||
return {
|
||||
format: format,
|
||||
inprogress: false,
|
||||
serverDown: false,
|
||||
i18n: {},
|
||||
settings: {},
|
||||
startConvertButton: $(buttonSelector),
|
||||
startOptButton: $('#wrio-start-optimization'),
|
||||
startWebpButton: $('#wrio-start-conversion'),
|
||||
startAvifButton: $('#wrio-start-avif-conversion'),
|
||||
|
||||
init: function () {
|
||||
this.i18n = wrio_l18n_bulk_page;
|
||||
this.settings = wrio_settings_bulk_page;
|
||||
|
||||
if (this.startConvertButton.length) {
|
||||
this.registerEvents();
|
||||
this.checkInitialRunningState();
|
||||
}
|
||||
},
|
||||
|
||||
checkInitialRunningState: function () {
|
||||
// If this conversion button is already running on page load, disable other buttons
|
||||
if (this.startConvertButton.hasClass('wio-running')) {
|
||||
this.startOptButton.prop('disabled', true);
|
||||
// Disable the other conversion button (not this one)
|
||||
if (this.format === 'webp') {
|
||||
this.startAvifButton.prop('disabled', true);
|
||||
} else {
|
||||
this.startWebpButton.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
registerEvents: function () {
|
||||
var self = this;
|
||||
this.startConvertButton.on('click', function () {
|
||||
|
||||
if ($(this).hasClass('wio-running')) {
|
||||
self.startOptButton.prop('disabled', false);
|
||||
self.startWebpButton.prop('disabled', false);
|
||||
self.startAvifButton.prop('disabled', false);
|
||||
self.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
self.showModal();
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
showModal: function () {
|
||||
var self = this;
|
||||
var infosModal = $('#wrio-tmpl-' + this.format + '-conversion');
|
||||
|
||||
// Fall back to webp template if format-specific one doesn't exist
|
||||
if (!infosModal.length) {
|
||||
infosModal = $('#wrio-tmpl-webp-conversion');
|
||||
}
|
||||
|
||||
if (!infosModal.length) {
|
||||
console.log('[Error]: Html template for modal not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
var modalTitle = this.format === 'avif'
|
||||
? (this.i18n.modal_avif_conversion_title || this.i18n.modal_conversion_title)
|
||||
: this.i18n.modal_conversion_title;
|
||||
|
||||
// Swal Information before loading the optimize process.
|
||||
swal({
|
||||
title: modalTitle,
|
||||
html: infosModal.html(),
|
||||
type: '',
|
||||
customClass: 'wrio-modal wrio-modal-optimization-way',
|
||||
showCancelButton: true,
|
||||
showCloseButton: true,
|
||||
padding: 0,
|
||||
width: 740,
|
||||
confirmButtonText: this.i18n.modal_conversion_manual_button,
|
||||
cancelButtonText: this.i18n.modal_conversion_cron_button,
|
||||
reverseButtons: true,
|
||||
}).then(function (result) {
|
||||
|
||||
self.startOptButton.prop('disabled', true);
|
||||
self.startWebpButton.prop('disabled', true);
|
||||
self.startAvifButton.prop('disabled', true);
|
||||
self.startConvertButton.prop('disabled', false); // Re-enable current button for stop
|
||||
self.process();
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
return self.i18n.leave_page_warning;
|
||||
}
|
||||
|
||||
}, function (dismiss) {
|
||||
if (dismiss === 'cancel') { // you might also handle 'close' or 'timer' if you used those
|
||||
self.startOptButton.prop('disabled', true);
|
||||
self.startWebpButton.prop('disabled', true);
|
||||
self.startAvifButton.prop('disabled', true);
|
||||
self.startConvertButton.prop('disabled', false); // Re-enable current button for stop
|
||||
self.process('cron');
|
||||
} else {
|
||||
throw dismiss;
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Start conversion
|
||||
* @param {string} type - 'cron' or undefined for manual
|
||||
*/
|
||||
process: function (type) {
|
||||
var self = this;
|
||||
this.inprogress = true;
|
||||
|
||||
var sendData = {
|
||||
'action': 'wrio-bulk-conversion-process',
|
||||
'scope': this.settings.scope,
|
||||
'format': this.format,
|
||||
'multisite': 0,
|
||||
'_wpnonce': this.settings.conversion_nonce,
|
||||
};
|
||||
|
||||
this.setButtonStyleRun(type);
|
||||
|
||||
if ('cron' === type) {
|
||||
this.startConvertButton.addClass('wrio-cron-mode');
|
||||
|
||||
sendData['action'] = 'wrio-' + this.format + '-cron-start';
|
||||
|
||||
$.post(ajaxurl, sendData, function (response) {
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Start cron).');
|
||||
console.log(sendData);
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
} else {
|
||||
if (response.data && response.data.stop) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.showMessage(this.i18n.conversion_inprogress.replace("%s", parseInt($('#wio-unoptimized-num').text())));
|
||||
|
||||
sendData['reset_current_errors'] = 1;
|
||||
|
||||
this.sendRequest(sendData);
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
var self = this;
|
||||
this.inprogress = false;
|
||||
|
||||
window.onbeforeunload = null;
|
||||
self.setButtonStyleStop();
|
||||
self.destroyMessages();
|
||||
|
||||
if (this.startConvertButton.hasClass('wrio-cron-mode')) {
|
||||
this.startConvertButton.removeClass('wrio-cron-mode');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
'action': 'wrio-' + this.format + '-cron-stop',
|
||||
'_wpnonce': self.settings.conversion_nonce,
|
||||
'scope': self.settings.scope
|
||||
}, function (response) {
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Stop cron).');
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
} else {
|
||||
self.startOptButton.prop('disabled', false);
|
||||
self.startWebpButton.prop('disabled', false);
|
||||
self.startAvifButton.prop('disabled', false);
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
complete: function () {
|
||||
this.inprogress = false;
|
||||
window.onbeforeunload = null;
|
||||
this.setButtonStyleComplete();
|
||||
},
|
||||
|
||||
setButtonStyleRun: function (mode) {
|
||||
|
||||
this.startConvertButton.addClass('wio-running');
|
||||
|
||||
if ("cron" === mode) {
|
||||
this.startConvertButton.text(this.i18n.modal_conversion_cron_button_stop);
|
||||
return;
|
||||
}
|
||||
|
||||
this.startConvertButton.text(this.i18n.button_stop);
|
||||
},
|
||||
|
||||
setButtonStyleComplete: function () {
|
||||
this.showMessage(this.i18n.conversion_complete);
|
||||
this.startConvertButton.text(this.i18n.button_completed);
|
||||
this.startConvertButton.removeClass('wio-running');
|
||||
this.startConvertButton.prop('disabled', true);
|
||||
this.startOptButton.prop('disabled', false);
|
||||
this.startWebpButton.prop('disabled', false);
|
||||
this.startAvifButton.prop('disabled', false);
|
||||
},
|
||||
|
||||
setButtonStyleStop: function () {
|
||||
this.startConvertButton.removeClass('wio-running');
|
||||
var buttonText = this.format === 'avif'
|
||||
? (this.i18n.avif_button_start || 'Convert to AVIF')
|
||||
: this.i18n.webp_button_start;
|
||||
this.startConvertButton.text(buttonText);
|
||||
},
|
||||
|
||||
showMessage: function (text) {
|
||||
var contanier = $('.wio-page-statistic'),
|
||||
message;
|
||||
|
||||
if (contanier.find('.wrio-statistic-message').length) {
|
||||
message = contanier.find('.wrio-statistic-message');
|
||||
} else {
|
||||
message = $('<div>');
|
||||
message.addClass('wrio-statistic-message');
|
||||
contanier.append(message);
|
||||
}
|
||||
|
||||
message.html(text);
|
||||
},
|
||||
|
||||
throwError: function (error_message) {
|
||||
this.stop();
|
||||
|
||||
var noticeId = $.wbcr_factory_templates_759.app.showNotice(error_message, 'danger');
|
||||
|
||||
setTimeout(function () {
|
||||
$.wbcr_factory_templates_759.app.hideNotice(noticeId);
|
||||
}, 10000);
|
||||
},
|
||||
|
||||
destroyMessages: function () {
|
||||
$('.wio-page-statistic').find('.wrio-statistic-message').empty();
|
||||
},
|
||||
|
||||
sendRequest: function (data) {
|
||||
var self = this;
|
||||
|
||||
if (!this.inprogress) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(ajaxurl, data, function (response) {
|
||||
if (!self.inprogress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Try to optimize images).');
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data.reset_current_errors = 0;
|
||||
|
||||
if (!response.data.end) {
|
||||
$('#wio-total-unoptimized').text(parseInt(response.data.remain));
|
||||
self.showMessage(self.i18n.conversion_inprogress.replace("%s", parseInt(response.data.remain)));
|
||||
self.sendRequest(data);
|
||||
} else {
|
||||
$('#wio-total-unoptimized').text(response.data.remain);
|
||||
self.complete();
|
||||
}
|
||||
|
||||
redraw_statistics(response.data.statistic);
|
||||
|
||||
if (response.data.last_optimized) {
|
||||
self.updateLog(response.data.last_optimized);
|
||||
}
|
||||
if (response.data.last_converted) {
|
||||
self.updateLog(response.data.last_converted);
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
},
|
||||
|
||||
updateLog: function (new_item_data) {
|
||||
var self = this;
|
||||
|
||||
var limit = 100,
|
||||
tableEl = $('.wrio-optimization-progress .wrio-table');
|
||||
|
||||
if (!tableEl.length || !new_item_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear empty table state
|
||||
if ($('.wrio-table-container-empty').length) {
|
||||
$('.wrio-table-container-empty').addClass('wrio-table-container').removeClass('wrio-table-container-empty');
|
||||
if (tableEl.find('tbody').length) {
|
||||
tableEl.find('tbody').empty();
|
||||
}
|
||||
}
|
||||
|
||||
$.each(new_item_data, function (index, value) {
|
||||
var trEl = $('<tr>'),
|
||||
tdEl = $('<td>'),
|
||||
webpSize = value.webp_size ? value.webp_size : '-',
|
||||
avifSize = value.avif_size ? value.avif_size : '-';
|
||||
|
||||
if (tableEl.find('.wrio-row-id-' + value.id).length) {
|
||||
tableEl.find('.wrio-row-id-' + value.id).remove();
|
||||
}
|
||||
|
||||
trEl.addClass('flash').addClass('wrio-table-item').addClass('wrio-row-id-' + value.id);
|
||||
|
||||
if ('error' === value.type) {
|
||||
trEl.addClass('wrio-error');
|
||||
}
|
||||
|
||||
var preview = $('<img width="40" height="40" src="' + value.thumbnail_url + '" alt="">'),
|
||||
previewUrl = $('<a href="' + value.url + '" target="_blank">' + value.file_name + '</a>');
|
||||
|
||||
tableEl.prepend(trEl);
|
||||
|
||||
trEl.append(tdEl.clone().append(preview));
|
||||
trEl.append(tdEl.clone().append(previewUrl));
|
||||
|
||||
if ('error' === value.type) {
|
||||
var colspan = value.scope !== 'custom-folders' ? '7' : '6';
|
||||
trEl.append(tdEl.clone().attr('colspan', colspan).text("Error: " + value.error_msg));
|
||||
} else {
|
||||
trEl.append(tdEl.clone().text(value.original_size));
|
||||
trEl.append(tdEl.clone().text(value.optimized_size));
|
||||
trEl.append(tdEl.clone().text(webpSize));
|
||||
trEl.append(tdEl.clone().text(avifSize));
|
||||
trEl.append(tdEl.clone().text(value.original_saving));
|
||||
|
||||
if ("custom-folders" !== self.settings.scope) {
|
||||
trEl.append(tdEl.clone().text(value.thumbnails_count));
|
||||
}
|
||||
|
||||
trEl.append(tdEl.clone().text(value.total_saving));
|
||||
}
|
||||
});
|
||||
|
||||
if (tableEl.find('tr').length > limit) {
|
||||
var diff = tableEl.find('tr').length - limit;
|
||||
|
||||
for (var i = 0; i < diff; i++) {
|
||||
tableEl.find('tr:last').remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create WebP conversion handler
|
||||
var bulkConversionWebp = createBulkConversion('webp', '#wrio-start-conversion');
|
||||
|
||||
// Create AVIF conversion handler
|
||||
var bulkConversionAvif = createBulkConversion('avif', '#wrio-start-avif-conversion');
|
||||
|
||||
$(document).ready(function () {
|
||||
bulkConversionWebp.init();
|
||||
bulkConversionAvif.init();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
||||
var ajaxUrl = ajaxurl;
|
||||
var ai_data;
|
||||
|
||||
function redraw_statistics(statistic) {
|
||||
// Update WebP stats - chart data attributes
|
||||
$('#wio-webp-chart').attr('data-unoptimized', statistic.unconverted)
|
||||
.attr('data-optimized', statistic.converted)
|
||||
.attr('data-errors', statistic.webp_error);
|
||||
|
||||
// Update WebP percent display
|
||||
$('#wio-overview-chart-percent-webp').text(statistic.webp_percent_line);
|
||||
|
||||
// Update WebP legend
|
||||
$('#wio-webp-pending').text(statistic.unconverted);
|
||||
$('#wio-webp-converted').text(statistic.converted);
|
||||
$('#wio-webp-error').text(statistic.webp_error);
|
||||
$('#wio-webp-done').text(statistic.converted);
|
||||
|
||||
// Update AVIF stats (if available)
|
||||
if (statistic.avif_unconverted !== undefined) {
|
||||
$('#wio-avif-chart').attr('data-unoptimized', statistic.avif_unconverted)
|
||||
.attr('data-optimized', statistic.avif_converted)
|
||||
.attr('data-errors', statistic.avif_error);
|
||||
|
||||
// Update AVIF percent display
|
||||
$('#wio-overview-chart-percent-avif').text(statistic.avif_percent_line);
|
||||
|
||||
// Update AVIF legend
|
||||
$('#wio-avif-pending').text(statistic.avif_unconverted);
|
||||
$('#wio-avif-converted').text(statistic.avif_converted);
|
||||
$('#wio-avif-error').text(statistic.avif_error);
|
||||
$('#wio-avif-done').text(statistic.avif_converted);
|
||||
}
|
||||
|
||||
var credits = $('.wrio-premium-user-balance');
|
||||
if (credits.attr('data-server') !== "server_5") {
|
||||
credits.text(statistic.credits);
|
||||
}
|
||||
|
||||
if (window.wio_chart_webp) {
|
||||
window.wio_chart_webp.data.datasets[0].data[0] = statistic.webp_error; // errors
|
||||
window.wio_chart_webp.data.datasets[0].data[1] = statistic.converted; // optimized
|
||||
window.wio_chart_webp.data.datasets[0].data[2] = statistic.unconverted; // unoptimized
|
||||
window.wio_chart_webp.update();
|
||||
}
|
||||
|
||||
if (window.wio_chart_avif && statistic.avif_unconverted !== undefined) {
|
||||
window.wio_chart_avif.data.datasets[0].data[0] = statistic.avif_error; // errors
|
||||
window.wio_chart_avif.data.datasets[0].data[1] = statistic.avif_converted; // optimized
|
||||
window.wio_chart_avif.data.datasets[0].data[2] = statistic.avif_unconverted; // unoptimized
|
||||
window.wio_chart_avif.update();
|
||||
}
|
||||
|
||||
if ($('#wio-overview-chart-percent-webp').text() == '100') {
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,646 @@
|
||||
function bytesToSize(bytes) {
|
||||
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes === 0) {
|
||||
return '0 Byte';
|
||||
}
|
||||
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
if (i === 0) {
|
||||
return bytes + ' ' + sizes[i];
|
||||
}
|
||||
return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
jQuery(function ($) {
|
||||
var bulkOptimization = {
|
||||
inprogress: false,
|
||||
serverDown: false,
|
||||
i18n: {},
|
||||
settings: {},
|
||||
|
||||
init: function () {
|
||||
if (wrio_l18n_bulk_page === undefined || wrio_settings_bulk_page === undefined) {
|
||||
console.log('[Error]: Required global variables are not declared.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.i18n = wrio_l18n_bulk_page;
|
||||
this.settings = wrio_settings_bulk_page;
|
||||
this.startOptButton = $('#wrio-start-optimization');
|
||||
|
||||
this.registerEvents();
|
||||
this.checkServerStatus();
|
||||
//this.calculateTotalImages();
|
||||
this.checkPremiumUserBalance();
|
||||
},
|
||||
|
||||
registerEvents: function () {
|
||||
var self = this;
|
||||
|
||||
this.startOptButton.on('click', function () {
|
||||
self.startOptButton = $(this);
|
||||
|
||||
if ($(this).hasClass('wio-running')) {
|
||||
self.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.serverDown) {
|
||||
$.wrio_modal.showErrorModal(self.i18n.server_down_warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("1" === self.settings.need_migration) {
|
||||
$.wrio_modal.showErrorModal(self.i18n.need_migrations);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("0" === self.settings.images_backup) {
|
||||
$.wrio_modal.showWarningModal(self.i18n.process_without_backup, function () {
|
||||
self.showModal();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
self.showModal();
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
checkPremiumUserBalance: function () {
|
||||
var self = this,
|
||||
userBalance = $('.wrio-premium-user-balance'),
|
||||
balanceResetAt = $('.wrio-premium-user-update'),
|
||||
data = {
|
||||
'action': 'wbcr-rio-check-user-balance',
|
||||
'_wpnonce': self.settings.optimization_nonce
|
||||
};
|
||||
|
||||
userBalance.addClass('wrio-premium-user-balance-check-proccess');
|
||||
userBalance.text('');
|
||||
|
||||
balanceResetAt.addClass('wrio-premium-user-update-check-proccess');
|
||||
balanceResetAt.text('');
|
||||
|
||||
$.post(ajaxurl, data, function (response) {
|
||||
|
||||
userBalance.removeClass('wrio-premium-user-balance-check-proccess');
|
||||
balanceResetAt.removeClass('wrio-premium-user-update-check-proccess');
|
||||
|
||||
if (!response || !response.data || !response.success) {
|
||||
console.log('[Error]: Response error');
|
||||
response.data && response.data.error && console.log(response.data.error);
|
||||
|
||||
if (!response || !response.data) {
|
||||
console.log(response);
|
||||
}
|
||||
|
||||
userBalance.text('error');
|
||||
balanceResetAt.text('error');
|
||||
} else {
|
||||
userBalance.text(response.data?.balance);
|
||||
balanceResetAt.text(response.data?.reset_at);
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
},
|
||||
|
||||
checkServerStatus: function () {
|
||||
var self = this,
|
||||
serverStatus = $('.wrio-server-status'),
|
||||
data = {
|
||||
'action': 'wbcr-rio-check-servers-status',
|
||||
'_wpnonce': self.settings.optimization_nonce
|
||||
};
|
||||
|
||||
self.serverDown = false;
|
||||
|
||||
serverStatus.addClass('wrio-server-check-proccess');
|
||||
serverStatus.text('');
|
||||
serverStatus.removeClass('wrio-down').removeClass('wrio-stable');
|
||||
|
||||
self.startOptButton.prop('disabled', true);
|
||||
|
||||
$.post(ajaxurl, data, function (response) {
|
||||
serverStatus.removeClass('wrio-server-check-proccess');
|
||||
|
||||
if (!response || !response.data || !response.success) {
|
||||
console.log('[Error]: Response error');
|
||||
response.data && response.data.error && console.log(response.data.error);
|
||||
|
||||
if (!response || !response.data) {
|
||||
console.log(response);
|
||||
}
|
||||
|
||||
serverStatus.addClass('wrio-down');
|
||||
serverStatus.text(self.i18n.server_status_down);
|
||||
self.serverDown = true;
|
||||
|
||||
return;
|
||||
} else {
|
||||
serverStatus.addClass('wrio-stable');
|
||||
serverStatus.text(self.i18n.server_status_stable);
|
||||
}
|
||||
|
||||
self.startOptButton.prop('disabled', false);
|
||||
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
},
|
||||
|
||||
calculateTotalImages: function () {
|
||||
var self = this,
|
||||
total_num = $('#wio-total-num'),
|
||||
data = {
|
||||
'action': 'wbcr-rio-calculate-total-images',
|
||||
'_wpnonce': self.settings.optimization_nonce
|
||||
};
|
||||
|
||||
total_num.addClass('wrio-calculate-process');
|
||||
total_num.text('');
|
||||
|
||||
$.post(ajaxurl, data, function (response) {
|
||||
total_num.removeClass('wrio-calculate-process');
|
||||
|
||||
if (!response || !response.data || !response.success) {
|
||||
console.log('[Error]: Response error');
|
||||
response.data && response.data.error && console.log(response.data.error);
|
||||
|
||||
if (!response || !response.data) {
|
||||
console.log(response);
|
||||
}
|
||||
|
||||
total_num.text('');
|
||||
|
||||
return;
|
||||
} else {
|
||||
if (typeof (response.data.total) !== "undefined") {
|
||||
total_num.addClass('wrio-total-images');
|
||||
total_num.text(response.data.total);
|
||||
}
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
},
|
||||
|
||||
showModal: function () {
|
||||
var self = this;
|
||||
var infosModal = $('#wrio-tmpl-bulk-optimization');
|
||||
|
||||
if (!infosModal.length) {
|
||||
console.log('[Error]: Html template for modal not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Swal Information before loading the optimize process.
|
||||
swal({
|
||||
title: this.i18n.modal_optimization_title,
|
||||
html: infosModal.html(),
|
||||
type: '',
|
||||
customClass: 'wrio-modal wrio-modal-optimization-way',
|
||||
showCancelButton: true,
|
||||
showCloseButton: true,
|
||||
padding: 0,
|
||||
width: 740,
|
||||
confirmButtonText: this.i18n.modal_optimization_manual_button,
|
||||
cancelButtonText: this.i18n.modal_optimization_cron_button,
|
||||
reverseButtons: true,
|
||||
}).then(function (result) {
|
||||
self.process();
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
return self.i18n.leave_page_warning;
|
||||
}
|
||||
|
||||
}, function (dismiss) {
|
||||
if (dismiss === 'cancel') { // you might also handle 'close' or 'timer' if you used those
|
||||
self.process('cron');
|
||||
} else {
|
||||
throw dismiss;
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Start optimization
|
||||
* @param {string} type
|
||||
*/
|
||||
process: function (type) {
|
||||
var self = this;
|
||||
|
||||
this.inprogress = true;
|
||||
|
||||
var sendData = {
|
||||
'action': 'wrio-bulk-optimization-process',
|
||||
'scope': this.settings.scope,
|
||||
'multisite': 0,
|
||||
'_wpnonce': this.settings.optimization_nonce,
|
||||
};
|
||||
|
||||
this.setButtonStyleRun(type);
|
||||
|
||||
if ('cron' === type) {
|
||||
this.startOptButton.addClass('wrio-cron-mode');
|
||||
|
||||
sendData['action'] = 'wrio-cron-start';
|
||||
|
||||
$.post(ajaxurl, sendData, function (response) {
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Start cron).');
|
||||
console.log(sendData);
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
} else {
|
||||
if (response.data && response.data.stop) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.showMessage(this.i18n.optimization_inprogress.replace("%s", parseInt($('#wio-unoptimized-num').text())));
|
||||
|
||||
// show message: Optimization remined
|
||||
/*if( "1" === this.settings.is_network_admin ) {
|
||||
sendData['multisite'] = 1;
|
||||
}*/
|
||||
|
||||
sendData['reset_current_errors'] = 1;
|
||||
|
||||
this.sendRequest(sendData);
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
var self = this;
|
||||
|
||||
this.inprogress = false;
|
||||
|
||||
window.onbeforeunload = null;
|
||||
self.setButtonStyleStop();
|
||||
self.destroyMessages();
|
||||
|
||||
if (this.startOptButton.hasClass('wrio-cron-mode')) {
|
||||
this.startOptButton.removeClass('wrio-cron-mode');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
'action': 'wrio-cron-stop',
|
||||
'_wpnonce': self.settings.optimization_nonce,
|
||||
'scope': self.settings.scope
|
||||
}, function (response) {
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Stop cron).');
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
}
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
complete: function () {
|
||||
this.inprogress = false;
|
||||
window.onbeforeunload = null;
|
||||
this.setButtonStyleComplete();
|
||||
},
|
||||
|
||||
setButtonStyleRun: function (mode) {
|
||||
|
||||
this.startOptButton.addClass('wio-running');
|
||||
|
||||
if ("cron" === mode) {
|
||||
this.startOptButton.text(this.i18n.modal_optimization_cron_button_stop);
|
||||
return;
|
||||
}
|
||||
|
||||
this.startOptButton.text(this.i18n.button_stop);
|
||||
},
|
||||
|
||||
setButtonStyleComplete: function () {
|
||||
this.showMessage(this.i18n.optimization_complete);
|
||||
this.startOptButton.text(this.i18n.button_completed);
|
||||
this.startOptButton.removeClass('wio-running');
|
||||
this.startOptButton.prop('disabled', true);
|
||||
},
|
||||
|
||||
setButtonStyleStop: function () {
|
||||
this.startOptButton.removeClass('wio-running');
|
||||
this.startOptButton.text(this.i18n.button_start);
|
||||
},
|
||||
|
||||
showMessage: function (text) {
|
||||
var contanier = $('.wio-page-statistic'),
|
||||
message;
|
||||
|
||||
if (contanier.find('.wrio-statistic-message').length) {
|
||||
message = contanier.find('.wrio-statistic-message');
|
||||
} else {
|
||||
message = $('<div>');
|
||||
message.addClass('wrio-statistic-message');
|
||||
contanier.append(message);
|
||||
}
|
||||
|
||||
message.html(text);
|
||||
},
|
||||
|
||||
throwError: function (error_message) {
|
||||
this.stop();
|
||||
|
||||
var noticeId = $.wbcr_factory_templates_759.app.showNotice(error_message, 'danger');
|
||||
|
||||
setTimeout(function () {
|
||||
$.wbcr_factory_templates_759.app.hideNotice(noticeId);
|
||||
}, 10000);
|
||||
},
|
||||
|
||||
destroyMessages: function () {
|
||||
$('.wio-page-statistic').find('.wrio-statistic-message').empty();
|
||||
},
|
||||
|
||||
sendRequest: function (data) {
|
||||
var self = this;
|
||||
|
||||
if (!this.inprogress) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(ajaxurl, data, function (response) {
|
||||
if (!self.inprogress) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(response);
|
||||
|
||||
if (!response || !response.success) {
|
||||
console.log('[Error]: Failed ajax request (Try to optimize images).');
|
||||
console.log(response);
|
||||
|
||||
if (response.data && response.data.error_message) {
|
||||
self.throwError(response.data.error_message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data.reset_current_errors = 0;
|
||||
|
||||
if (!response.data.end) {
|
||||
$('#wio-total-unoptimized').text(parseInt(response.data.remain));
|
||||
self.showMessage(self.i18n.optimization_inprogress.replace("%s", parseInt(response.data.remain)));
|
||||
self.sendRequest(data);
|
||||
} else {
|
||||
$('#wio-total-unoptimized').text(response.data.remain);
|
||||
self.complete();
|
||||
|
||||
// если мультисайт режим, то не скрываем кнопку запуска оптимизации
|
||||
/*if( $('#wbcr-rio-current-blog').length ) {
|
||||
$('#wio-start-optimization').toggleClass('wio-running');
|
||||
} else {
|
||||
$('#wio-start-optimization').hide();
|
||||
}*/
|
||||
}
|
||||
|
||||
redraw_statistics(response.data.statistic);
|
||||
|
||||
self.updateLog(response.data.last_optimized);
|
||||
}).fail(function (xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
self.throwError(error);
|
||||
});
|
||||
},
|
||||
|
||||
updateLog: function (new_item_data) {
|
||||
const self = this;
|
||||
const limit = 100;
|
||||
const tableEl = $('.wrio-optimization-progress .wrio-table');
|
||||
|
||||
if (!tableEl.length || !new_item_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle empty table state
|
||||
if ($('.wrio-table-container-empty').length) {
|
||||
$('.wrio-table-container-empty').addClass('wrio-table-container').removeClass('wrio-table-container-empty');
|
||||
if (tableEl.find('tbody').length) {
|
||||
tableEl.find('tbody').empty();
|
||||
}
|
||||
}
|
||||
|
||||
$.each(new_item_data, function (index, value) {
|
||||
const attachmentId = value.attachment_id || value.id;
|
||||
const existingRow = tableEl.find('[data-attachment-id="' + attachmentId + '"]');
|
||||
|
||||
if (existingRow.length) {
|
||||
// Update existing row data
|
||||
self.updateRowData(existingRow, value);
|
||||
// Move existing row to top of the table
|
||||
existingRow.detach();
|
||||
tableEl.find('tbody').prepend(existingRow);
|
||||
// Re-trigger flash animation
|
||||
existingRow.removeClass('flash');
|
||||
// Force reflow to restart animation
|
||||
existingRow[0].offsetWidth;
|
||||
existingRow.addClass('flash');
|
||||
} else {
|
||||
// Create new row and add to top
|
||||
const trEl = self.buildLogRow(value);
|
||||
tableEl.find('tbody').prepend(trEl);
|
||||
}
|
||||
});
|
||||
|
||||
// Enforce row limit
|
||||
self.enforceRowLimit(tableEl, limit);
|
||||
},
|
||||
|
||||
updateRowData: function (row, data) {
|
||||
// Update optimized size cell
|
||||
row.find('.wrio-optimized-size').text(data.optimized_size);
|
||||
|
||||
// Update WebP size if present
|
||||
if (data.webp_size) {
|
||||
const webpCell = row.find('.wrio-webp-size');
|
||||
if (webpCell.length) {
|
||||
webpCell.text(data.webp_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Update AVIF size if present
|
||||
if (data.avif_size) {
|
||||
const avifCell = row.find('.wrio-avif-size');
|
||||
if (avifCell.length) {
|
||||
avifCell.text(data.avif_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Update total saving
|
||||
row.find('.wrio-total-saving').text(data.total_saving);
|
||||
|
||||
// Update thumbnails count
|
||||
if (data.thumbnails_count !== undefined) {
|
||||
row.find('.wrio-thumbnails-count').text(data.thumbnails_count);
|
||||
}
|
||||
|
||||
// Update error state if needed
|
||||
if (data.type === 'error') {
|
||||
row.addClass('wrio-error');
|
||||
} else {
|
||||
row.removeClass('wrio-error');
|
||||
}
|
||||
},
|
||||
|
||||
buildLogRow: function (value) {
|
||||
const attachmentId = value.attachment_id || value.id;
|
||||
const trEl = $('<tr>')
|
||||
.addClass('flash wrio-table-item')
|
||||
.addClass('wrio-row-id-' + value.id)
|
||||
.attr('data-attachment-id', attachmentId);
|
||||
|
||||
if (value.type === 'error') {
|
||||
trEl.addClass('wrio-error');
|
||||
}
|
||||
|
||||
// Build cells with classes for easy updates
|
||||
const preview = $('<img width="40" height="40" src="' + value.thumbnail_url + '" alt="">');
|
||||
const previewUrl = $('<a href="' + value.url + '" target="_blank">' + value.file_name + '</a>');
|
||||
|
||||
trEl.append($('<td>').append(preview));
|
||||
trEl.append($('<td>').append(previewUrl));
|
||||
|
||||
if (value.type === 'error') {
|
||||
const colspan = this.settings.scope !== 'custom-folders' ? '4' : '3';
|
||||
trEl.append($('<td>').attr('colspan', colspan).text("Error: " + value.error_msg));
|
||||
} else {
|
||||
trEl.append($('<td class="wrio-original-size">').text(value.original_size));
|
||||
trEl.append($('<td class="wrio-optimized-size">').text(value.optimized_size));
|
||||
|
||||
if ("custom-folders" !== this.settings.scope) {
|
||||
trEl.append($('<td class="wrio-thumbnails-count">').text(value.thumbnails_count));
|
||||
}
|
||||
|
||||
trEl.append($('<td class="wrio-total-saving">').text(value.total_saving));
|
||||
}
|
||||
|
||||
return trEl;
|
||||
},
|
||||
|
||||
enforceRowLimit: function (tableEl, limit) {
|
||||
const rows = tableEl.find('tbody tr');
|
||||
if (rows.length > limit) {
|
||||
rows.slice(limit).remove();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
bulkOptimization.init();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
||||
var ajaxUrl = ajaxurl;
|
||||
var ai_data;
|
||||
|
||||
function redraw_statistics(statistic) {
|
||||
$('#wio-main-chart').attr('data-unoptimized', statistic.unoptimized)
|
||||
.attr('data-optimized', statistic.optimized)
|
||||
.attr('data-errors', statistic.error);
|
||||
$('#wio-total-optimized-attachments').text(statistic.optimized); // optimized
|
||||
$('#wio-original-size').text(bytesToSize(statistic.original_size));
|
||||
$('#wio-optimized-size').text(bytesToSize(statistic.optimized_size));
|
||||
$('#wio-total-saved').text(statistic.save_size_percent + '%');
|
||||
$('#wio-overview-chart-percent').text(statistic.optimized_percent);
|
||||
$('.wio-total-percent').text(statistic.optimized_percent + '%');
|
||||
$('#wio-optimized-bar').css('width', statistic.percent_line + '%');
|
||||
|
||||
$('#wio-unoptimized-num').text(statistic.unoptimized);
|
||||
$('#wio-optimized-num').text(statistic.optimized);
|
||||
$('#wio-error-num').text(statistic.error);
|
||||
|
||||
if(statistic.quota_limit) {
|
||||
$('.wrio-premium-user-balance').text(statistic.quota_limit);
|
||||
}
|
||||
|
||||
if ($('.wrio-statistic-nav li.active').length) {
|
||||
$('.wrio-statistic-nav li.active').find('span.wio-statistic-tab-percent').text(statistic.optimized_percent + '%');
|
||||
}
|
||||
|
||||
window.wio_chart.data.datasets[0].data[0] = statistic.error; // errors
|
||||
window.wio_chart.data.datasets[0].data[1] = statistic.optimized; // optimized
|
||||
window.wio_chart.data.datasets[0].data[2] = statistic.unoptimized; // unoptimized
|
||||
window.wio_chart.update();
|
||||
if ($('#wio-overview-chart-percent').text() == '100%') {
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*$('#wbcr-rio-current-blog').on('change', function() {
|
||||
var self = $(this);
|
||||
$('#wio-start-msg-complete').hide();
|
||||
$(this).attr('disabled', true);
|
||||
$('#wio-start-optimization').attr('disabled', true);
|
||||
var ai_data = {
|
||||
'action': 'wbcr_rio_update_current_blog',
|
||||
'wpnonce': $(this).data('nonce'),
|
||||
'current_blog_id': $(this).find('option:selected').val(),
|
||||
'context': $(this).attr('data-context')
|
||||
};
|
||||
$.post(ajaxUrl, ai_data, function(response) {
|
||||
self.removeAttr('disabled');
|
||||
$('#wio-start-optimization').removeAttr('disabled');
|
||||
redraw_statistics(response.data.statistic);
|
||||
});
|
||||
});*/
|
||||
|
||||
// AVIF upsell banner dismiss handler
|
||||
$(document).on('click', '.wrio-avif-banner-dismiss', function () {
|
||||
var $banner = $(this).closest('.wrio-avif-upsell-banner');
|
||||
|
||||
$.post(ajaxurl, {
|
||||
action: 'wrio_dismiss_avif_banner',
|
||||
nonce: $banner.data('nonce')
|
||||
}, function () {
|
||||
$banner.slideUp(300, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,169 @@
|
||||
(function ($) {
|
||||
class BulkOptimization {
|
||||
constructor(ajaxUrl, i18n, settings) {
|
||||
if (!i18n || !settings) {
|
||||
console.error('[Error]: Required global variables are missing.');
|
||||
return;
|
||||
}
|
||||
this.ajaxUrl = ajaxUrl;
|
||||
this.i18n = i18n;
|
||||
this.settings = settings;
|
||||
|
||||
this.totalImages = 0;
|
||||
this.countAttachments = 0;
|
||||
this.countThumbs = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the bulk optimization process by chaining multiple calculations.
|
||||
* If any error occurs during the process, it will be caught and handled.
|
||||
*/
|
||||
init() {
|
||||
this.calculateTotalAttachments()
|
||||
.then(() => this.calculateTotalThumbs())
|
||||
.then(() => this.calculateTotalImages())
|
||||
.catch((error) => this.throwError(error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an AJAX POST request to the server.
|
||||
*
|
||||
* @param {string} action - The AJAX action to trigger on the server.
|
||||
* @param {Object} additionalData - Additional data to send with the request.
|
||||
* @returns {Promise<Object>} - A promise that resolves with the response data from the server.
|
||||
* @throws Will throw an error if the response is invalid or the request fails.
|
||||
*/
|
||||
async postAjax(action, additionalData = {}) {
|
||||
const data = {
|
||||
action: action,
|
||||
_wpnonce: this.settings.optimization_nonce,
|
||||
...additionalData,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await $.post(this.ajaxUrl, data);
|
||||
|
||||
if (!response || !response.success || !response.data) {
|
||||
console.error('[Error]: Invalid AJAX response.', response);
|
||||
if (response?.data?.error) {
|
||||
console.error(response.data.error);
|
||||
}
|
||||
|
||||
throw new Error(this.i18n.ajaxError || 'AJAX Error Occurred');
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (xhr) {
|
||||
console.error('[Error]: AJAX Request Failed.', xhr);
|
||||
throw xhr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of attachments.
|
||||
*
|
||||
* This method sends an AJAX request to the server to fetch the total
|
||||
* number of media attachments and updates the corresponding UI element with the result.
|
||||
*
|
||||
* @returns {Promise<void>} - A promise that resolves when the calculation is complete.
|
||||
*/
|
||||
async calculateTotalAttachments() {
|
||||
try {
|
||||
const data = await this.postAjax('wbcr-rio-calculate-total-attachments');
|
||||
|
||||
this.countAttachments = data.found_attachments;
|
||||
|
||||
$('#wio-stat-totals__originals')
|
||||
.removeClass('wio-stat-totals__loading')
|
||||
.text(data.found_attachments);
|
||||
} catch (error) {
|
||||
this.throwError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the total count of images by summing attachments and thumbnails.
|
||||
*
|
||||
* This method does not send an AJAX request. Instead, it calculates the total
|
||||
* number of found images and updates the corresponding UI element.
|
||||
*/
|
||||
async calculateTotalImages() {
|
||||
this.totalImages = this.countAttachments + this.countThumbs;
|
||||
$('#wio-stat-totals__totals')
|
||||
.removeClass('wio-stat-totals__loading')
|
||||
.text(this.totalImages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of thumbnails in a paginated manner.
|
||||
*
|
||||
* This method sends multiple AJAX requests based on the `offset` returned
|
||||
* from the server. It stops once the `done` parameter is `true` and accumulates
|
||||
* the total number of thumbnails found during the process.
|
||||
*
|
||||
* @returns {Promise<void>} - A promise that resolves when all requests are complete.
|
||||
* @throws Will throw an error if `next_offset` is missing or undefined in the response.
|
||||
*/
|
||||
async calculateTotalThumbs() {
|
||||
try {
|
||||
let offset = 0;
|
||||
let totalThumbs = 0;
|
||||
|
||||
// Sequentially fetch thumbnail counts in batches
|
||||
while (true) {
|
||||
const data = await this.postAjax('wbcr-rio-calculate-total-thumbs', { offset });
|
||||
|
||||
// Update the total thumbnail counter
|
||||
totalThumbs = data.found_thumbs;
|
||||
|
||||
// Update the thumbnails count in the UI
|
||||
$('#wio-stat-totals__thumbnails')
|
||||
.removeClass('wio-stat-totals__loading')
|
||||
.text(totalThumbs);
|
||||
|
||||
// Break the loop if the server indicates the process is complete
|
||||
if (data.done) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the offset for the next request
|
||||
offset = data.next_offset;
|
||||
|
||||
// Validate the offset to avoid infinite loops
|
||||
if (offset === undefined || offset === null) {
|
||||
console.error('[Error]: Missing offset in server response.');
|
||||
throw new Error('Invalid server response: offset is undefined.');
|
||||
}
|
||||
}
|
||||
|
||||
this.countThumbs = totalThumbs;
|
||||
|
||||
} catch (error) {
|
||||
this.throwError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors by logging them to the console and displaying an alert.
|
||||
*
|
||||
* This method provides a standardized way to handle any unexpected errors
|
||||
* that occur during the execution of the bulk optimization process.
|
||||
*
|
||||
* @param {Error|string} error - The error message or object to handle.
|
||||
*/
|
||||
throwError(error) {
|
||||
console.error('[Error]:', error);
|
||||
alert(this.i18n.generalError || 'An error occurred. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the bulk optimization process on document ready
|
||||
$(document).ready(() => {
|
||||
const bulkOptimization = new BulkOptimization(
|
||||
ajaxurl, // The URL for WordPress AJAX requests
|
||||
window.wrio_l18n_bulk_page, // Localization data for the UI
|
||||
window.wrio_settings_bulk_page // Settings data for the optimization
|
||||
);
|
||||
bulkOptimization.init();
|
||||
});
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,52 @@
|
||||
jQuery(function($) {
|
||||
$('#wbcr-wio-meta-migration-action').on('click', function() {
|
||||
|
||||
var data = {
|
||||
'action': 'wrio_meta_migrations',
|
||||
'_wpnonce': $(this).data('nonce'),
|
||||
};
|
||||
|
||||
$(this).addClass('disabled').text('Please wait...');
|
||||
|
||||
send_request($(this), data);
|
||||
});
|
||||
|
||||
function send_request(button, data) {
|
||||
$.post(window.ajaxurl, data, function(response) {
|
||||
|
||||
console.log(response);
|
||||
|
||||
if( !response || !response.data ) {
|
||||
console.log('An unknown server error has occurred.');
|
||||
console.log(response);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !response.data.need_more_time ) {
|
||||
if( button.closest('.notice').length ) {
|
||||
button.closest('.notice').remove();
|
||||
}
|
||||
if( button.closest('.alert').length ) {
|
||||
button.closest('.alert').remove();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
button.text(response.data.message);
|
||||
|
||||
send_request(button, data);
|
||||
}).fail(function(xhr, status, error) {
|
||||
console.log(xhr);
|
||||
console.log(status);
|
||||
console.log(error);
|
||||
|
||||
data.limit = 5;
|
||||
data.error = 1;
|
||||
|
||||
setTimeout(function() {
|
||||
send_request(button, data);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* A set of tools for creating pop-ups. You can create a popup
|
||||
* using a global method call.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
if( !$.wrio_modal ) {
|
||||
$.wrio_modal = {};
|
||||
}
|
||||
|
||||
$.wrio_modal = $.wrio_popup || {
|
||||
|
||||
showErrorModal: function(text) {
|
||||
if( !text ) {
|
||||
console.log('[Error]: Text required.');
|
||||
return;
|
||||
}
|
||||
|
||||
swal({
|
||||
title: 'Error',
|
||||
text: text,
|
||||
type: 'error',
|
||||
customClass: 'wrio-modal wrio-modal-error',
|
||||
width: 500,
|
||||
confirmButtonText: 'OK',
|
||||
});
|
||||
},
|
||||
|
||||
showWarningModal: function(text, callback) {
|
||||
if( !text ) {
|
||||
console.log('[Error]: Text required.');
|
||||
return;
|
||||
}
|
||||
|
||||
swal({
|
||||
title: 'Warning',
|
||||
text: text,
|
||||
type: 'warning',
|
||||
customClass: 'wrio-modal wrio-modal-warning',
|
||||
width: 500,
|
||||
showCancelButton: true,
|
||||
showCloseButton: true,
|
||||
confirmButtonText: 'OK',
|
||||
}).then(function(result) {
|
||||
if( callback ) {
|
||||
callback();
|
||||
}
|
||||
}).catch(swal.noop);
|
||||
},
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
@@ -0,0 +1,209 @@
|
||||
jQuery(function($){
|
||||
var ajaxUrl = ajaxurl;
|
||||
|
||||
// Delivery Mode Toggle Handler
|
||||
// Handles the visibility of the delivery mode options based on WebP and AVIF conversion toggles
|
||||
var $webpToggle = $('input[name="wbcr_io_convert_webp_format"]');
|
||||
var $avifToggle = $('input[name="wbcr_io_convert_avif_format"]');
|
||||
var $deliveryModeSection = $('.wrio-conversion-delivery-options');
|
||||
|
||||
// Function to check if at least one toggle is enabled
|
||||
function updateDeliveryModeVisibility() {
|
||||
var isWebpEnabled = $webpToggle.val() === '1';
|
||||
var isAvifEnabled = $avifToggle.val() === '1';
|
||||
|
||||
// Show delivery mode section only if at least one format is enabled
|
||||
if (isWebpEnabled || isAvifEnabled) {
|
||||
$deliveryModeSection.slideDown(300);
|
||||
} else {
|
||||
$deliveryModeSection.slideUp(300);
|
||||
}
|
||||
}
|
||||
|
||||
// Attach event listeners to both toggles
|
||||
if ($webpToggle.length && $avifToggle.length && $deliveryModeSection.length) {
|
||||
// Get the button containers for both toggles
|
||||
var $webpButtons = $webpToggle.closest('.factory-checkbox').find('button');
|
||||
var $avifButtons = $avifToggle.closest('.factory-checkbox').find('button');
|
||||
|
||||
// Initial check on page load
|
||||
updateDeliveryModeVisibility();
|
||||
|
||||
// Listen for clicks on the factory buttons
|
||||
$webpButtons.on('click', function() {
|
||||
setTimeout(updateDeliveryModeVisibility, 50);
|
||||
});
|
||||
|
||||
$avifButtons.on('click', function() {
|
||||
setTimeout(updateDeliveryModeVisibility, 50);
|
||||
});
|
||||
|
||||
// Also listen for direct changes on the hidden inputs (fallback)
|
||||
$webpToggle.on('change', function() {
|
||||
updateDeliveryModeVisibility();
|
||||
});
|
||||
|
||||
$avifToggle.on('change', function() {
|
||||
updateDeliveryModeVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
$('#wio-restore-backup-btn').on('click', function() {
|
||||
if ( $('#wio-multisite-mode').length ) {
|
||||
$('#wio-multisite-mode').toggle();
|
||||
$('#wio-multisite-confirm').attr('data-action', 'restore');
|
||||
$('#wio-multisite-restore-progress').empty();
|
||||
return false;
|
||||
}
|
||||
result = confirm( $(this).attr('data-confirm') );
|
||||
if ( ! result ) {
|
||||
return false;
|
||||
}
|
||||
$(this).hide();
|
||||
$('#wio-restore-backup-progress').show();
|
||||
var ai_data = {
|
||||
'total' : '?',
|
||||
'action': 'wio_restore_backup',
|
||||
'_wpnonce': $('#wio-iph-nonce').val()
|
||||
};
|
||||
send_post_data(ai_data);
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#wio-clear-backup-btn').on('click', function() {
|
||||
$('#wio-restore-backup-msg').hide();
|
||||
if ( $('#wio-multisite-mode').length ) {
|
||||
$('#wio-multisite-mode').toggle();
|
||||
$('#wio-multisite-confirm').attr('data-action', 'clear');
|
||||
$('#wio-multisite-restore-progress').empty();
|
||||
return false;
|
||||
}
|
||||
result = confirm( $(this).attr('data-confirm') );
|
||||
if ( ! result ) {
|
||||
return false;
|
||||
}
|
||||
var data = {
|
||||
'action': 'wio_clear_backup',
|
||||
'_wpnonce': $('#wio-iph-nonce').val()
|
||||
};
|
||||
$.post(ajaxUrl, data, function(response) {
|
||||
$('#wio-clear-backup-msg').show();
|
||||
});
|
||||
});
|
||||
|
||||
$('#wio-multisite-confirm').on('click', function() {
|
||||
var action = $(this).attr('data-action');
|
||||
// если запущена очистка резервных копий
|
||||
if ( action == 'clear' ) {
|
||||
result = confirm( $('#wio-clear-backup-btn').attr('data-confirm') ); // берём сообщение из основной кнопки
|
||||
if ( ! result ) {
|
||||
return false;
|
||||
}
|
||||
var blogs = [];
|
||||
$('.wbcr_io_multisite_blogs:checked').each(function() {
|
||||
blogs.push( $(this).val() );
|
||||
});
|
||||
var data = {
|
||||
'action': 'wio_clear_backup',
|
||||
'_wpnonce': $('#wio-iph-nonce').val(),
|
||||
'blogs': blogs
|
||||
};
|
||||
|
||||
$.post(ajaxUrl, data, function(response) {
|
||||
$('#wio-clear-backup-msg').show();
|
||||
$('#wio-multisite-mode').toggle();
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// если запущено восстановление из резервных копий
|
||||
if ( action == 'restore' ) {
|
||||
result = confirm( $('#wio-restore-backup-btn').attr('data-confirm') ); // берём сообщение из основной кнопки
|
||||
if ( ! result ) {
|
||||
return false;
|
||||
}
|
||||
$('#wio-multisite-mode').toggle();
|
||||
$('#wio-multisite-restore-progress').empty();
|
||||
$('.wbcr_io_multisite_blogs:checked').each(function() {
|
||||
$('#wio-multisite-restore-progress').append('\
|
||||
<label>'+$(this).attr('data-name')+'</label>\
|
||||
<div class="progress">\
|
||||
<div id="wio-restore-backup-progress-'+$(this).val()+'" class="wio-restore-backup-progressbar progress-bar progress-bar-success" data-id="'+$(this).val()+'" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%">\
|
||||
</div>\
|
||||
</div>\
|
||||
');
|
||||
});
|
||||
$('#wio-multisite-restore-progress').show();
|
||||
if ( ! $('.wio-restore-backup-progressbar').length ) {
|
||||
$('#wio-restore-backup-msg').show();
|
||||
return false;
|
||||
}
|
||||
var ai_data = {
|
||||
'total' : '?',
|
||||
'action': 'wio_restore_backup',
|
||||
'_wpnonce': $('#wio-iph-nonce').val(),
|
||||
'blog_id': $('.wio-restore-backup-progressbar:eq(0)').attr('data-id')
|
||||
};
|
||||
send_multisite_post_data(ai_data);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#wbcr_io_multisite_blog_all').on('change', function() {
|
||||
if ( $(this).attr('checked') == 'checked' ) {
|
||||
$('.wbcr_io_multisite_blogs').attr('checked', true);
|
||||
} else {
|
||||
$('.wbcr_io_multisite_blogs').removeAttr('checked');
|
||||
}
|
||||
});
|
||||
|
||||
$('.wbcr_io_multisite_blogs').on('change', function() {
|
||||
var all_checked = true;
|
||||
$('.wbcr_io_multisite_blogs').each(function() {
|
||||
if ( $(this).attr('checked') != 'checked' ) {
|
||||
all_checked = false;
|
||||
}
|
||||
});
|
||||
if ( all_checked ) {
|
||||
$('#wbcr_io_multisite_blog_all').attr('checked', true);
|
||||
} else {
|
||||
$('#wbcr_io_multisite_blog_all').removeAttr('checked');
|
||||
}
|
||||
});
|
||||
|
||||
function send_post_data(data){
|
||||
$.post(ajaxUrl, data, function(response) {
|
||||
if ( ! response.end ) {
|
||||
data.total = response.total;
|
||||
send_post_data(data);
|
||||
$('#wio-restore-backup-progress').find('.progress-bar').css( 'width', response.percent + '%' );
|
||||
} else {
|
||||
$('#wio-restore-backup-progress').find('.progress-bar').css( 'width', '100%' );
|
||||
$('#wio-restore-backup-msg').show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function send_multisite_post_data(data){
|
||||
$.post(ajaxUrl, data, function(response) {
|
||||
if ( ! response.end ) {
|
||||
data.total = response.total;
|
||||
send_multisite_post_data(data);
|
||||
$('#wio-restore-backup-progress-' + data.blog_id).css( 'width', response.percent + '%' );
|
||||
} else {
|
||||
$('#wio-restore-backup-progress-' + data.blog_id).css( 'width', '100%' ).removeClass('wio-restore-backup-progressbar');
|
||||
if ( $('.wio-restore-backup-progressbar').length ) {
|
||||
var ai_data = {
|
||||
'total' : '?',
|
||||
'action': 'wio_restore_backup',
|
||||
'_wpnonce': $('#wio-iph-nonce').val(),
|
||||
'blog_id': $('.wio-restore-backup-progressbar:eq(0)').attr('data-id')
|
||||
};
|
||||
send_multisite_post_data(ai_data);
|
||||
} else {
|
||||
$('#wio-restore-backup-msg').show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
jQuery(document).ready(function($) {
|
||||
// Disable premium dropdown buttons for non-licensed users
|
||||
$('button[data-value="googlepage"]').attr('disabled', 'disabled');
|
||||
$('button[data-value="background"]').attr('disabled', 'disabled');
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
jQuery(function($) {
|
||||
var ajaxUrl = ajaxurl;
|
||||
|
||||
$(document).on('click', '.wio-reoptimize', function() {
|
||||
var ai_data = {
|
||||
'action' : $(this).attr('data-action'),
|
||||
'id' : $(this).attr('data-id'),
|
||||
'level' : $(this).attr('data-level'),
|
||||
'_wpnonce' : $(this).attr('data-nonce')
|
||||
};
|
||||
var td = $(this).closest('td');
|
||||
var msg = $(this).attr( 'data-waiting-label' );
|
||||
td.html('<p>'+msg+'</p>');
|
||||
wio_reoptimize( ai_data, td );
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('click', '.wio-convert', function() {
|
||||
var ai_data = {
|
||||
'action' : $(this).attr('data-action'),
|
||||
'id' : $(this).attr('data-id'),
|
||||
'format' : $(this).attr('data-format'),
|
||||
'_wpnonce' : $(this).attr('data-nonce')
|
||||
};
|
||||
var td = $(this).closest('td');
|
||||
var msg = $(this).attr( 'data-waiting-label' );
|
||||
td.html('<p>'+msg+'</p>');
|
||||
wio_convert( ai_data, td );
|
||||
return false;
|
||||
});
|
||||
|
||||
function wio_reoptimize( ai_data, td ) {
|
||||
$.post(ajaxUrl, ai_data, function(response) {
|
||||
if ( response === 'processing' ) {
|
||||
wio_reoptimize( ai_data, td );
|
||||
return false;
|
||||
}
|
||||
td.html(response);
|
||||
var btn = $('.wio-reoptimize').first();
|
||||
|
||||
if ( btn.closest('.media-frame-content').length ) {
|
||||
if ( btn.closest('table').find('.wio-datas-list').length ) {
|
||||
var diminsionName = $('.dimensions').find('strong').clone();
|
||||
var fileSizeName = $('.file-size').find('strong').clone();
|
||||
var diminsionSize = btn.closest('table').find('.wio-datas-list').data('dimensions');
|
||||
var fileSize = btn.closest('table').find('.wio-datas-list').data('size');
|
||||
$('.dimensions').html(diminsionName.get(0).outerHTML + ' ' + diminsionSize);
|
||||
$('.file-size').html(fileSizeName.get(0).outerHTML + ' ' + fileSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function wio_convert( ai_data, td ) {
|
||||
$.post(ajaxUrl, ai_data, function(response) {
|
||||
if ( response === 'processing' ) {
|
||||
wio_convert( ai_data, td );
|
||||
return false;
|
||||
}
|
||||
td.html(response);
|
||||
var btn = $('.wio-convert').first();
|
||||
|
||||
if ( btn.closest('.media-frame-content').length ) {
|
||||
if ( btn.closest('table').find('.wio-datas-list').length ) {
|
||||
var diminsionName = $('.dimensions').find('strong').clone();
|
||||
var fileSizeName = $('.file-size').find('strong').clone();
|
||||
var diminsionSize = btn.closest('table').find('.wio-datas-list').data('dimensions');
|
||||
var fileSize = btn.closest('table').find('.wio-datas-list').data('size');
|
||||
$('.dimensions').html(diminsionName.get(0).outerHTML + ' ' + diminsionSize);
|
||||
$('.file-size').html(fileSizeName.get(0).outerHTML + ' ' + fileSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '.button-wio-restore', function() {
|
||||
var ai_data = {
|
||||
'action' : $(this).attr('data-action'),
|
||||
'id' : $(this).attr('data-id'),
|
||||
'_wpnonce' : $(this).attr('data-nonce')
|
||||
};
|
||||
var td = $(this).closest('td');
|
||||
var msg = $(this).attr( 'data-waiting-label' );
|
||||
td.html('<p>'+msg+'</p>');
|
||||
$.post(ajaxUrl, ai_data, function(response) {
|
||||
td.html(response);
|
||||
var btn = $('.wio-reoptimize');
|
||||
if ( btn.closest('.media-frame-content').length ) {
|
||||
if ( btn.length ) {
|
||||
btn = btn.first();
|
||||
var diminsionName = $('.dimensions').find('strong').clone();
|
||||
var fileSizeName = $('.file-size').find('strong').clone();
|
||||
var diminsionSize = btn.data('dimensions');
|
||||
var fileSize = btn.data('size');
|
||||
$('.dimensions').html(diminsionName.get(0).outerHTML + ' ' + diminsionSize);
|
||||
$('.file-size').html(fileSizeName.get(0).outerHTML + ' ' + fileSize);
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
jQuery(function ($) {
|
||||
|
||||
var chart_html_id = 'wio-main-chart';
|
||||
var webp_chart_html_id = 'wio-webp-chart';
|
||||
var avif_chart_html_id = 'wio-avif-chart';
|
||||
|
||||
var ctx = document.getElementById(chart_html_id);
|
||||
var ctx_webp = document.getElementById(webp_chart_html_id);
|
||||
var ctx_avif = document.getElementById(avif_chart_html_id);
|
||||
|
||||
window.wio_chart = new window.robin.Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
$('#' + chart_html_id).attr('data-errors'),
|
||||
$('#' + chart_html_id).attr('data-optimized'),
|
||||
$('#' + chart_html_id).attr('data-unoptimized'),
|
||||
],
|
||||
backgroundColor: [
|
||||
'#f1b1b6',
|
||||
'#8bc34a',
|
||||
'#d6d6d6',
|
||||
],
|
||||
borderWidth: 0,
|
||||
label: 'Dataset 1'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
events: [],
|
||||
animation: {
|
||||
easing: 'easeOutBounce'
|
||||
},
|
||||
responsive: false,
|
||||
cutoutPercentage: 80
|
||||
}
|
||||
});
|
||||
|
||||
if (ctx_webp) {
|
||||
window.wio_chart_webp = new window.robin.Chart(ctx_webp, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
$('#' + webp_chart_html_id).attr('data-errors'),
|
||||
$('#' + webp_chart_html_id).attr('data-optimized'),
|
||||
$('#' + webp_chart_html_id).attr('data-unoptimized'),
|
||||
],
|
||||
backgroundColor: [
|
||||
'#f1b1b6',
|
||||
'#8bc34a',
|
||||
'#d6d6d6',
|
||||
],
|
||||
borderWidth: 0,
|
||||
label: 'Dataset 1'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
events: [],
|
||||
animation: {
|
||||
easing: 'easeOutBounce'
|
||||
},
|
||||
responsive: false,
|
||||
cutoutPercentage: 80
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (ctx_avif) {
|
||||
window.wio_chart_avif = new window.robin.Chart(ctx_avif, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
data: [
|
||||
$('#' + avif_chart_html_id).attr('data-errors'),
|
||||
$('#' + avif_chart_html_id).attr('data-optimized'),
|
||||
$('#' + avif_chart_html_id).attr('data-unoptimized'),
|
||||
],
|
||||
backgroundColor: [
|
||||
'#f1b1b6',
|
||||
'#8bc34a',
|
||||
'#d6d6d6',
|
||||
],
|
||||
borderWidth: 0,
|
||||
label: 'Dataset 1'
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
events: [],
|
||||
animation: {
|
||||
easing: 'easeOutBounce'
|
||||
},
|
||||
responsive: false,
|
||||
cutoutPercentage: 80
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* License Manager JavaScript
|
||||
*
|
||||
* Handles license activation, deactivation, sync, and unsubscribe actions.
|
||||
* This is a lightweight replacement for clearfy-license-manager.js
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
*/
|
||||
|
||||
jQuery(function($) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Handle license action button clicks
|
||||
*/
|
||||
$(document).on('click', '.wrio-license-btn', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Remove any existing notices when user tries again
|
||||
$('.wrio-license-notice').remove();
|
||||
|
||||
var $button = $(this),
|
||||
$wrapper = $('#wrio-license-wrapper'),
|
||||
action = $button.data('action'),
|
||||
nonce = $wrapper.data('nonce'),
|
||||
loaderUrl = $wrapper.data('loader');
|
||||
|
||||
// Disable all buttons and show loader inside the clicked button
|
||||
$('.wrio-license-btn').prop('disabled', true);
|
||||
$button.prepend('<img class="wrio-loader" src="' + loaderUrl + '" alt="Loading..." style="height: 16px; vertical-align: middle; margin-right: 8px;">');
|
||||
|
||||
// Build request data
|
||||
var data = {
|
||||
action: 'wrio_license_action',
|
||||
_wpnonce: nonce,
|
||||
license_action: action,
|
||||
licensekey: ''
|
||||
};
|
||||
|
||||
// Include license key for activation
|
||||
if (action === 'activate') {
|
||||
data.licensekey = $('#license-key').val().trim();
|
||||
|
||||
if (!data.licensekey) {
|
||||
showNotice('Please enter a license key.', 'error');
|
||||
resetButtons();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Send AJAX request
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: data,
|
||||
success: function(response) {
|
||||
if (response && response.success) {
|
||||
showNotice(response.data.message, 'success');
|
||||
// Reload page to show updated license state
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
var errorMsg = response && response.data && response.data.message
|
||||
? response.data.message
|
||||
: 'An error occurred. Please try again.';
|
||||
showNotice(errorMsg, 'error');
|
||||
resetButtons();
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('WRIO License AJAX Error:', {
|
||||
status: xhr.status,
|
||||
statusText: xhr.statusText,
|
||||
responseText: xhr.responseText,
|
||||
error: error
|
||||
});
|
||||
|
||||
var errorMsg = 'Connection error. Please check your internet connection and try again.';
|
||||
if (xhr.responseText) {
|
||||
try {
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
if (response.data && response.data.message) {
|
||||
errorMsg = response.data.message;
|
||||
}
|
||||
} catch (e) {
|
||||
// Response wasn't JSON
|
||||
}
|
||||
}
|
||||
|
||||
showNotice(errorMsg, 'error');
|
||||
resetButtons();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Reset buttons to their original state
|
||||
*/
|
||||
function resetButtons() {
|
||||
$('.wrio-loader').remove();
|
||||
$('.wrio-license-btn').prop('disabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a notice message
|
||||
*
|
||||
* @param {string} message The message to display
|
||||
* @param {string} type Notice type: 'success' or 'error'
|
||||
*/
|
||||
function showNotice(message, type) {
|
||||
// Remove any existing notices
|
||||
$('.wrio-license-notice').remove();
|
||||
|
||||
var typeClass = type === 'success' ? 'wrio-license-notice--success' : 'wrio-license-notice--error';
|
||||
var $notice = $('<div class="wrio-license-notice ' + typeClass + '"><p>' + escapeHtml(message) + '</p></div>');
|
||||
|
||||
// Insert into the error container
|
||||
$('#license-form-error-container').html($notice);
|
||||
|
||||
// Auto-dismiss after 5 seconds for success messages
|
||||
if (type === 'success') {
|
||||
setTimeout(function() {
|
||||
$notice.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML entities
|
||||
*
|
||||
* @param {string} text Text to escape
|
||||
* @return {string} Escaped text
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return String(text).replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
});
|
||||
355
wp-content/plugins/robin-image-optimizer/admin/boot.php
Normal file
355
wp-content/plugins/robin-image-optimizer/admin/boot.php
Normal file
@@ -0,0 +1,355 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin boot
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяем таблицу в базе данных
|
||||
*
|
||||
* Если таблица не существует или её структура устарела, то обновляем.
|
||||
* Проверка проводится при каждой инициализации плагина т.к. структура может измениться
|
||||
* после очередного обновления плагина.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
add_action(
|
||||
'admin_init',
|
||||
function () {
|
||||
RIO_Process_Queue::try_create_plugin_tables();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
add_filter(
|
||||
'wbcr/clearfy/components/items_list',
|
||||
function ( $components ) {
|
||||
if ( wrio_is_clearfy_license_activate() ) {
|
||||
return $components;
|
||||
}
|
||||
if ( ! empty( $components ) ) {
|
||||
foreach ( $components as $key => $component ) {
|
||||
if ( 'robin_image_optimizer' == $component['name'] ) {
|
||||
unset( $components[ $key ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $components;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Добавляет карточку компонента на страницу компонентов
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
add_action(
|
||||
'wbcr/clearfy/components/custom_plugins_card',
|
||||
function () {
|
||||
if ( ! wrio_is_clearfy_license_activate() ) {
|
||||
$view = WRIO_Views::get_instance( WRIO_PLUGIN_DIR );
|
||||
$view->print_template( 'clearfy-component-card' );
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* We asset migration scripts to all admin panel pages
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
function () {
|
||||
if ( ! current_user_can( 'update_plugins' ) || ! wbcr_rio_has_meta_to_migrate() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'wrio-meta-migrations',
|
||||
WRIO_PLUGIN_URL . '/admin/assets/js/meta-migrations.js',
|
||||
[
|
||||
'jquery',
|
||||
'wbcr-factory-clearfy-000-global',
|
||||
],
|
||||
WRIO_Plugin::app()->getPluginVersion()
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Plugin was heavy migrated into new architecture. Specifically, post meta was moved to separate table and
|
||||
* therefore it is required to migrate all of them to new table.
|
||||
*
|
||||
* This action prints a notice, which contains clickable link with JS onclick event, which invokes AJAX request
|
||||
* to migrate these post metas to new table.
|
||||
*
|
||||
* Once all post meta migrated, notice would not be shown anymore.
|
||||
*
|
||||
* @param $notices
|
||||
*
|
||||
* @return array
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @see wbcr_rio_migrate_postmeta_to_process_queue() for further information about AJAX processing function.
|
||||
* @see wbcr_rio_has_meta_to_migrate() used to check whether to show notice or not.
|
||||
*
|
||||
* @see RIO_Process_Queue for further information about new table.
|
||||
*/
|
||||
add_action(
|
||||
'wbcr/factory/admin_notices',
|
||||
function ( $notices ) {
|
||||
|
||||
if ( ! current_user_can( 'update_plugins' ) || ! wbcr_rio_has_meta_to_migrate() ) {
|
||||
return $notices;
|
||||
}
|
||||
|
||||
$notices[] = [
|
||||
'id' => WRIO_Plugin::app()->getPrefix() . 'meta_to_migration',
|
||||
'type' => 'warning',
|
||||
'dismissible' => false,
|
||||
'dismiss_expires' => 0,
|
||||
'text' => '<p><b>' . WRIO_Plugin::app()->getPluginTitle() . ':</b> ' . wrio_get_meta_migration_notice_text() . '</p>',
|
||||
];
|
||||
|
||||
return $notices;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Plugin was heavy migrated into new architecture. Specifically, post meta was moved to separate table and
|
||||
* therefore it is required to migrate all of them to new table.
|
||||
*
|
||||
* This action prints a notice, which contains clickable link with JS onclick event, which invokes AJAX request
|
||||
* to migrate these post metas to new table.
|
||||
*
|
||||
* Once all post meta migrated, notice would not be shown anymore.
|
||||
*
|
||||
* @param Wbcr_Factory480_Plugin $plugin
|
||||
* @param Wbcr_FactoryPages480_ImpressiveThemplate $obj
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @see wbcr_rio_migrate_postmeta_to_process_queue() for further information about AJAX processing function.
|
||||
* @see wbcr_rio_has_meta_to_migrate() used to check whether to show notice or not.
|
||||
*
|
||||
* @see RIO_Process_Queue for further information about new table.
|
||||
*/
|
||||
add_action(
|
||||
'wbcr/factory/pages/impressive/print_all_notices',
|
||||
function ( $plugin, $obj ) {
|
||||
if ( ( $plugin->getPluginName() != WRIO_Plugin::app()->getPluginName() ) || ! wbcr_rio_has_meta_to_migrate() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$obj->printWarningNotice( wrio_get_meta_migration_notice_text() );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
|
||||
/***
|
||||
* Flush configuration after saving the settings
|
||||
*
|
||||
* @param WRIO_Plugin $plugin
|
||||
* @param Wbcr_FactoryPages480_ImpressiveThemplate $obj
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
/*
|
||||
add_action('wbcr_factory_480_imppage_after_form_save', function ($plugin, $obj) {
|
||||
$is_rio = WRIO_Plugin::app()->getPluginName() == $plugin->getPluginName();
|
||||
|
||||
if( $is_rio ) {
|
||||
WRIO_Cron::check();
|
||||
}
|
||||
}, 10, 2);*/
|
||||
|
||||
/**
|
||||
* Виджет отзывов
|
||||
*
|
||||
* @param string $page_url
|
||||
* @param string $plugin_name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wio_rating_widget_url( $page_url, $plugin_name ) {
|
||||
if ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) {
|
||||
return 'https://wordpress.org/support/plugin/robin-image-optimizer/reviews/#new-post';
|
||||
}
|
||||
|
||||
return $page_url;
|
||||
}
|
||||
|
||||
add_filter( 'wbcr_factory_pages_480_imppage_rating_widget_url', 'wio_rating_widget_url', 10, 2 );
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $widgets
|
||||
* @param string $position
|
||||
* @param Wbcr_Factory480_Plugin $plugin
|
||||
*/
|
||||
add_filter(
|
||||
'wbcr/factory/pages/impressive/widgets',
|
||||
function ( $widgets, $position, $plugin ) {
|
||||
if ( $plugin->getPluginName() == WRIO_Plugin::app()->getPluginName() ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/includes/sidebar-widgets.php';
|
||||
|
||||
if ( wrio_is_license_activate() ) {
|
||||
unset( $widgets['donate_widget'] );
|
||||
|
||||
if ( $position == 'right' ) {
|
||||
unset( $widgets['adverts_widget'] );
|
||||
unset( $widgets['business_suggetion'] );
|
||||
unset( $widgets['rating_widget'] );
|
||||
unset( $widgets['info_widget'] );
|
||||
}
|
||||
|
||||
/*
|
||||
if ( $position == 'bottom' ) {
|
||||
$widgets['support'] = wrio_get_sidebar_support_widget();
|
||||
}*/
|
||||
|
||||
return $widgets;
|
||||
} elseif ( $position == 'right' ) {
|
||||
unset( $widgets['info_widget'] );
|
||||
unset( $widgets['rating_widget'] );
|
||||
// $widgets['support'] = wrio_get_sidebar_support_widget();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
},
|
||||
20,
|
||||
3
|
||||
);
|
||||
|
||||
/**
|
||||
* Заменяет заголовок в рекламном виджете
|
||||
*
|
||||
* @param array $features
|
||||
* @param string $plugin_name
|
||||
* @param string $page_id
|
||||
*/
|
||||
add_filter(
|
||||
'wbcr/clearfy/pages/suggetion_title',
|
||||
function ( $features, $plugin_name, $page_id ) {
|
||||
if ( ! empty( $plugin_name ) && ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) ) {
|
||||
return __( 'ROBIN IMAGE OPTIMIZER PRO', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
return $features;
|
||||
},
|
||||
20,
|
||||
3
|
||||
);
|
||||
|
||||
/**
|
||||
* Заменяем премиум возможности в рекламном виджете
|
||||
*
|
||||
* @param array $features
|
||||
* @param string $plugin_name
|
||||
* @param string $page_id
|
||||
*/
|
||||
add_filter(
|
||||
'wbcr/clearfy/pages/suggetion_features',
|
||||
function ( $features, $plugin_name, $page_id ) {
|
||||
if ( ! empty( $plugin_name ) && ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) ) {
|
||||
$upgrade_feature = [];
|
||||
$upgrade_feature[] = __( 'Automatic conversion to WebP', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'You can optimize custom folders', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'Supports NextGen gallery', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'Multisite support', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'Fast optimization servers', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'Ad-free experience', 'robin-image-optimizer' );
|
||||
$upgrade_feature[] = __( 'Priority support', 'robin-image-optimizer' );
|
||||
|
||||
return $upgrade_feature;
|
||||
}
|
||||
|
||||
return $features;
|
||||
},
|
||||
20,
|
||||
3
|
||||
);
|
||||
|
||||
/**
|
||||
* Заменяем премиум возможности в рекламном виджете
|
||||
*
|
||||
* @param array $messages
|
||||
* @param string $type
|
||||
* @param string $plugin_name
|
||||
*/
|
||||
add_filter(
|
||||
'wbcr/factory/premium/notice_text',
|
||||
function ( $text, $type, $plugin_name ) {
|
||||
if ( WRIO_Plugin::app()->getPluginName() != $plugin_name ) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
$license_page_url = WRIO_Plugin::app()->getPluginPageUrl( 'rio_license' );
|
||||
|
||||
if ( null === $license_page_url ) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
if ( 'need_activate_license' == $type ) {
|
||||
return sprintf(
|
||||
// translators: %1$s is opening <a> tag, %2$s is closing </a> tag.
|
||||
__( '%1$sLicense activation%2$s required. A license is required to get premium plugin updates, as well as to get additional services.', 'robin-image-optimizer' ),
|
||||
'<a href="' . esc_url( $license_page_url ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
} elseif ( 'need_renew_license' == $type ) {
|
||||
return sprintf(
|
||||
// translators: %1$s is opening <a> tag, %2$s is closing </a> tag.
|
||||
__( 'Your %1$slicense%2$s has expired. You can no longer get premium plugin updates, premium support and your access to services has been suspended.', 'robin-image-optimizer' ),
|
||||
'<a href="' . esc_url( $license_page_url ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
return $text;
|
||||
},
|
||||
10,
|
||||
3
|
||||
);
|
||||
|
||||
/**
|
||||
* Check if the AVIF upsell banner has been dismissed by the current user.
|
||||
*
|
||||
* @return bool True if dismissed, false otherwise.
|
||||
*/
|
||||
function wrio_is_avif_banner_dismissed() {
|
||||
return (bool) get_user_meta( get_current_user_id(), 'wrio_avif_banner_dismissed', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for dismissing the AVIF upsell banner.
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wrio_dismiss_avif_banner',
|
||||
function () {
|
||||
check_ajax_referer( 'wrio_dismiss_avif_banner', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Permission denied.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
// Dismiss permanently.
|
||||
update_user_meta( get_current_user_id(), 'wrio_avif_banner_dismissed', 1 );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
/**
|
||||
* Subscribe Widget Class
|
||||
*
|
||||
* Self-contained newsletter subscription widget that can be used on any admin page.
|
||||
* No dependencies on Factory libs.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
* @subpackage Admin\Includes
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_Subscribe_Widget
|
||||
*/
|
||||
class WRIO_Subscribe_Widget {
|
||||
|
||||
/**
|
||||
* ThemeIsle subscription API endpoint
|
||||
*/
|
||||
const API_URL = 'https://api.themeisle.com/tracking/subscribe';
|
||||
|
||||
/**
|
||||
* Option name to track subscription status
|
||||
*/
|
||||
const OPTION_SUBSCRIBED = 'wrio_user_subscribed';
|
||||
|
||||
/**
|
||||
* Plugin slug for API
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $plugin_name = 'wbcr_image_optimizer';
|
||||
|
||||
/**
|
||||
* Privacy policy URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $privacy_url = 'https://themeisle.com/privacy-policy/';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_wrio_subscribe', [ $this, 'ajax_handler' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has already subscribed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_subscribed() {
|
||||
return (bool) get_option( self::OPTION_SUBSCRIBED, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the subscribe widget
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render() {
|
||||
if ( $this->is_subscribed() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nonce = wp_create_nonce( 'wrio_subscribe' );
|
||||
?>
|
||||
<div class="wrio-subscribe-widget">
|
||||
<div class="wrio-subscribe-widget__header">
|
||||
<h3><?php esc_html_e( 'Subscribe to plugin\'s newsletter', 'robin-image-optimizer' ); ?></h3>
|
||||
</div>
|
||||
<div class="wrio-subscribe-widget__body">
|
||||
<p><?php esc_html_e( 'Get the latest news and updates about the plugin:', 'robin-image-optimizer' ); ?></p>
|
||||
<div class="wrio-subscribe-widget__messages">
|
||||
<div class="wrio-subscribe-widget__success" style="display:none;">
|
||||
<?php esc_html_e( 'Thank you for subscribing.', 'robin-image-optimizer' ); ?>
|
||||
</div>
|
||||
<div class="wrio-subscribe-widget__error" style="display:none;"></div>
|
||||
</div>
|
||||
<form class="wrio-subscribe-widget__form">
|
||||
<div class="wrio-subscribe-widget__field-wrap">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
class="wrio-subscribe-widget__email"
|
||||
placeholder="<?php esc_attr_e( 'Enter your email address', 'robin-image-optimizer' ); ?>"
|
||||
required
|
||||
>
|
||||
<button type="submit" class="button button-primary wrio-subscribe-widget__button">
|
||||
<?php esc_html_e( 'Subscribe', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
<label class="wrio-subscribe-widget__checkbox-label">
|
||||
<input type="checkbox" name="agree_terms" checked required>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %1$s: opening link tag, %2$s: closing link tag */
|
||||
esc_html__( 'I agree to receive the Themeisle newsletter. See our %1$sPrivacy Policy%2$s for details.', 'robin-image-optimizer' ),
|
||||
'<a href="' . esc_url( $this->privacy_url ) . '" target="_blank" rel="noopener">',
|
||||
'</a>'
|
||||
);
|
||||
?>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
const form = document.querySelector('.wrio-subscribe-widget__form');
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const button = form.querySelector('.wrio-subscribe-widget__button');
|
||||
const successEl = document.querySelector('.wrio-subscribe-widget__success');
|
||||
const errorEl = document.querySelector('.wrio-subscribe-widget__error');
|
||||
const email = form.querySelector('[name="email"]').value;
|
||||
const agreed = form.querySelector('[name="agree_terms"]').checked;
|
||||
|
||||
if (!agreed) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.disabled = true;
|
||||
button.textContent = '<?php echo esc_js( __( 'Subscribing...', 'robin-image-optimizer' ) ); ?>';
|
||||
errorEl.style.display = 'none';
|
||||
|
||||
const data = new FormData();
|
||||
data.append('action', 'wrio_subscribe');
|
||||
data.append('email', email);
|
||||
data.append('_wpnonce', '<?php echo esc_js( $nonce ); ?>');
|
||||
|
||||
fetch(ajaxurl, {
|
||||
method: 'POST',
|
||||
body: data
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(res => {
|
||||
if (res.success) {
|
||||
form.style.display = 'none';
|
||||
successEl.style.display = 'block';
|
||||
} else {
|
||||
errorEl.textContent = res.data.message || '<?php echo esc_js( __( 'An error occurred. Please try again.', 'robin-image-optimizer' ) ); ?>';
|
||||
errorEl.style.display = 'block';
|
||||
button.disabled = false;
|
||||
button.textContent = '<?php echo esc_js( __( 'Subscribe', 'robin-image-optimizer' ) ); ?>';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
errorEl.textContent = '<?php echo esc_js( __( 'An error occurred. Please try again.', 'robin-image-optimizer' ) ); ?>';
|
||||
errorEl.style.display = 'block';
|
||||
button.disabled = false;
|
||||
button.textContent = '<?php echo esc_js( __( 'Subscribe', 'robin-image-optimizer' ) ); ?>';
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX subscription request
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_handler() {
|
||||
check_ajax_referer( 'wrio_subscribe', '_wpnonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Permission denied.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
|
||||
|
||||
if ( ! is_email( $email ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Please enter a valid email address.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
$body = wp_json_encode(
|
||||
[
|
||||
'slug' => $this->plugin_name,
|
||||
'site' => home_url(),
|
||||
'email' => $email,
|
||||
]
|
||||
);
|
||||
|
||||
// Ensure body is a string, not false.
|
||||
if ( false === $body ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Failed to encode request body.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
$response = wp_remote_post(
|
||||
self::API_URL,
|
||||
[
|
||||
'timeout' => 10,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Accept' => 'application/json, */*;q=0.1',
|
||||
],
|
||||
'body' => $body,
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
wp_send_json_error( [ 'message' => $response->get_error_message() ] );
|
||||
}
|
||||
|
||||
$body = wp_remote_retrieve_body( $response );
|
||||
$data = json_decode( $body, true );
|
||||
|
||||
if ( isset( $data['code'] ) ) {
|
||||
update_option( self::OPTION_SUBSCRIBED, 1 );
|
||||
wp_send_json_success( [ 'message' => __( 'Subscribed successfully!', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
wp_send_json_error( [ 'message' => __( 'Subscription failed. Please try again.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс используется для вывода страницы лендинга
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WIO_NextgenLanding {
|
||||
|
||||
/**
|
||||
* Инициализация лендинга
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_menu', [ $this, 'removeSubMenu' ], 99999 );
|
||||
add_action( 'admin_menu', [ $this, 'addSubMenu' ], 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет лендинг nextgen
|
||||
*/
|
||||
public function removeSubMenu() {
|
||||
remove_submenu_page( 'nextgen-gallery', 'ngg_imagify' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляем свою страницу в меню Тextgen
|
||||
*/
|
||||
public function addSubMenu() {
|
||||
add_submenu_page(
|
||||
'nextgen-gallery',
|
||||
__( 'Image optimizer', 'robin-image-optimizer' ),
|
||||
__( 'Image optimizer', 'robin-image-optimizer' ),
|
||||
'manage_options',
|
||||
'ngg_robin', // если взять старый слаг ngg_imagify, то на странице выведет оба лендинга
|
||||
[ $this, 'nngLandingPage' ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Контент лендинга
|
||||
*/
|
||||
public function nngLandingPage() {
|
||||
// если активна премиум версия - делаем редирект на страницу статистики nextgen
|
||||
if ( defined( 'WRIOP_PLUGIN_ACTIVE' ) && WRIOP_PLUGIN_ACTIVE ) {
|
||||
wp_redirect( admin_url( 'admin.php?page=io_nextgen_gallery_statistic-wbcr_image_optimizer' ) );
|
||||
die();
|
||||
}
|
||||
?>
|
||||
рекламма установки премиум аддона
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
new WIO_NextgenLanding();
|
||||
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс реализует шаблон блока статистики
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WIO_OptimizePageTemplate {
|
||||
|
||||
/**
|
||||
* Тип страницы
|
||||
*/
|
||||
protected $page_type = 'media-library';
|
||||
|
||||
|
||||
public function __construct( $type = 'media-library' ) {
|
||||
$this->page_type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выводит контент страницы с учётом мультисайта
|
||||
*
|
||||
* @param WRIO_Page $page
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
|
||||
/*
|
||||
public function showPageContent( WRIO_Page $page ) {
|
||||
do_action( 'wbcr/rio/multisite_current_blog' );
|
||||
$this->pageContent( $page );
|
||||
do_action( 'wbcr/rio/multisite_restore_blog' );
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Выбор сайта для мультисайт режима
|
||||
*/
|
||||
/*
|
||||
public function selectSite() {
|
||||
if ( ! WRIO_Plugin::app()->isNetworkAdmin() ) {
|
||||
return;
|
||||
}
|
||||
$blogs = WIO_Multisite::getBlogs( $this->page_type );
|
||||
$current_blog = WRIO_Plugin::app()->getPopulateOption( 'current_blog', 1 );
|
||||
?>
|
||||
<select style="width:200px;display:inline-block; height: 45px; margin-left:40px;" id="wbcr-rio-current-blog"
|
||||
class="factory-dropdown factory-from-control-dropdown form-control"
|
||||
data-context="<?php echo esc_attr( $this->page_type ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'update_blog_id' ); ?>">
|
||||
<?php foreach ( $blogs as $blog ) : ?>
|
||||
<?php
|
||||
$blog_name = $blog->domain . $blog->path;
|
||||
if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
|
||||
$blog_name = $blog->domain;
|
||||
}
|
||||
?>
|
||||
<option <?php selected( $current_blog, $blog->blog_id ); ?>
|
||||
value="<?php echo esc_attr( $blog->blog_id ); ?>"><?php echo esc_attr( $blog_name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Возвращает html код блока ручной оптимизации
|
||||
*
|
||||
* @param array $params {
|
||||
* Параметры
|
||||
*
|
||||
* @type int $attachment_id Attachment post ID
|
||||
* @type bool $is_optimized Оптимизировано ли изображение
|
||||
* @type string $attach_dimensions Размеры изображения. Например 200x150
|
||||
* @type int $attachment_file_size Размер оригинального основного файла в байтах
|
||||
* @type bool $is_skipped Пропущено ли изображение. Изображения с таким флагом больше не участвуют в
|
||||
* оптимизации
|
||||
* @type int $optimized_size Оптимизированный размер основного файла + превьюшек в байтах
|
||||
* @type int $original_size Оригинальный размер основного файла + превьюшек в байтах
|
||||
* @type int $original_main_size Оригинальный размер основного файла в байтах
|
||||
* @type int $thumbnails_optimized Кол-во оптимизированных превьюшек
|
||||
* @type string $optimization_level Уровень оптимизации
|
||||
* @type string $error_msg Текст ошибки
|
||||
* @type bool $backuped Сделана ли резервная копия
|
||||
* @type float $diff_percent Разница между оригиналом и оптимизацией в процентах
|
||||
* @type float $diff_percent_all Общая оптимизация в процентах
|
||||
* }
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMediaColumnTemplate( $params ) {
|
||||
ob_start();
|
||||
|
||||
$ajaxActionOptimize = apply_filters( 'wbcr/rio/optimize_template/reoptimize_ajax_action', 'wio_reoptimize_image', $this->page_type );
|
||||
$ajaxActionConvert = apply_filters( 'wbcr/rio/optimize_template/convert_ajax_action', 'wio_convert_image', $this->page_type );
|
||||
$ajaxActionRestore = apply_filters( 'wbcr/rio/optimize_template/restore_ajax_action', 'wio_restore_image', $this->page_type );
|
||||
|
||||
$attachment_id = $params['attachment_id'];
|
||||
$is_optimized = $params['is_optimized'];
|
||||
$attach_dimensions = $params['attach_dimensions'];
|
||||
$attachment_file_size = $params['attachment_file_size'];
|
||||
$is_skipped = $params['is_skipped'];
|
||||
$is_supported_format = isset( $params['is_supported_format'] ) ? $params['is_supported_format'] : true;
|
||||
$webp_size = $params['webp_size'];
|
||||
$avif_size = isset( $params['avif_size'] ) ? $params['avif_size'] : 0;
|
||||
$webp_percent = isset( $params['webp_percent'] ) ? $params['webp_percent'] : 0;
|
||||
$avif_percent = isset( $params['avif_percent'] ) ? $params['avif_percent'] : 0;
|
||||
$backuped = isset( $params['backuped'] ) ? $params['backuped'] : false;
|
||||
|
||||
if ( $is_skipped ) {
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
// Don't show buttons for unsupported formats
|
||||
if ( ! $is_supported_format ) {
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
if ( $is_optimized ) {
|
||||
$original_main_size = $params['original_main_size'];
|
||||
$thumbnails_optimized = $params['thumbnails_optimized'];
|
||||
$optimization_level = $params['optimization_level'];
|
||||
$error_msg = $params['error_msg'];
|
||||
$diff_percent = $params['diff_percent'];
|
||||
$diff_percent_all = $params['diff_percent_all'];
|
||||
|
||||
?>
|
||||
<ul class="wio-datas-list" data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
|
||||
data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>">
|
||||
<li class="wio-data-item"><span
|
||||
class="data"><?php _e( 'New Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $attachment_file_size, 1 ) ); ?></strong>
|
||||
<span class="wio-chart-value">(<?php echo esc_attr( $diff_percent ); ?>%)</span></li>
|
||||
<?php if ( $webp_size ) : ?>
|
||||
<li class="wio-data-item"><span
|
||||
class="data"><?php _e( 'WebP Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $webp_size, 1 ) ); ?></strong>
|
||||
<span class="wio-chart-value">(<?php echo esc_attr( $webp_percent ); ?>%)</span></li>
|
||||
<?php endif; ?>
|
||||
<?php if ( $avif_size ) : ?>
|
||||
<li class="wio-data-item"><span
|
||||
class="data"><?php _e( 'AVIF Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $avif_size, 1 ) ); ?></strong>
|
||||
<span class="wio-chart-value">(<?php echo esc_attr( $avif_percent ); ?>%)</span></li>
|
||||
<?php endif; ?>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php _e( 'Original Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="original"><?php echo esc_attr( size_format( $original_main_size, 1 ) ); ?></strong>
|
||||
</li>
|
||||
<li class="wio-data-item"><span class="data"><?php _e( 'Level:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong>
|
||||
<?php
|
||||
if ( ! $error_msg ) {
|
||||
// если уровень кастомный от 1 до 100
|
||||
if ( is_numeric( $optimization_level ) ) {
|
||||
echo __( 'Custom', 'robin-image-optimizer' ) . ' ' . intval( $optimization_level ) . '%';
|
||||
} else {
|
||||
// если уровень один из настроек
|
||||
if ( $optimization_level == 'normal' ) {
|
||||
echo __( 'Lossless', 'robin-image-optimizer' );
|
||||
} elseif ( $optimization_level == 'aggresive' ) {
|
||||
echo __( 'Lossy', 'robin-image-optimizer' );
|
||||
} else {
|
||||
echo __( 'High', 'robin-image-optimizer' );
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
</strong>
|
||||
</li>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php _e( 'Thumbnails Optimized:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="original"><?php echo intval( $thumbnails_optimized ); ?></strong>
|
||||
</li>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php _e( 'Overall Saving:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="original"><?php echo esc_attr( $diff_percent_all ); ?>%</strong>
|
||||
</li>
|
||||
<?php if ( $error_msg ) : ?>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php _e( 'Error Message:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong><?php echo esc_attr( $error_msg ); ?></strong></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<div class="wio-datas-actions-links" style="display:inline;">
|
||||
<?php if ( $optimization_level != 'normal' ) : ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-level="normal"
|
||||
data-nonce="<?php echo wp_create_nonce( 'reoptimize' ); ?>"
|
||||
href="#"
|
||||
class="wio-reoptimize button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<span
|
||||
class="wio-hide-if-small"
|
||||
>
|
||||
<?php _e( 'Re-Optimize to:', 'robin-image-optimizer' ); ?>
|
||||
</span><?php _e( 'Lossless', 'robin-image-optimizer' ); ?>
|
||||
<span class="wio-hide-if-small"></span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ( $optimization_level != 'aggresive' ) : ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-level="aggresive"
|
||||
data-nonce="<?php echo wp_create_nonce( 'reoptimize' ); ?>"
|
||||
href="#"
|
||||
class="wio-reoptimize button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<span
|
||||
class="wio-hide-if-small"
|
||||
>
|
||||
<?php _e( 'Re-Optimize to:', 'robin-image-optimizer' ); ?>
|
||||
</span>
|
||||
<?php _e( 'Lossy', 'robin-image-optimizer' ); ?>
|
||||
<span class="wio-hide-if-small"></span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ( $optimization_level != 'ultra' ) : ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-level="ultra"
|
||||
data-nonce="<?php echo wp_create_nonce( 'reoptimize' ); ?>"
|
||||
href="#"
|
||||
class="wio-reoptimize button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<span
|
||||
class="wio-hide-if-small"
|
||||
>
|
||||
<?php _e( 'Re-Optimize to:', 'robin-image-optimizer' ); ?>
|
||||
</span>
|
||||
<?php _e( 'High', 'robin-image-optimizer' ); ?>
|
||||
<span class="wio-hide-if-small"></span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php // WebP conversion is available to all users ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="webp"
|
||||
data-nonce="<?php echo wp_create_nonce( 'convert' ); ?>"
|
||||
href="#"
|
||||
class="wio-convert button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php esc_html_e( 'Convert in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-images-alt2"></span>
|
||||
<?php echo esc_html( $webp_size ? __( 'Re-convert to WebP', 'robin-image-optimizer' ) : __( 'Convert to WebP', 'robin-image-optimizer' ) ); ?>
|
||||
</a>
|
||||
<?php // AVIF conversion is a premium-only feature ?>
|
||||
<?php if ( wrio_is_license_activate() ) : ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="avif"
|
||||
data-nonce="<?php echo wp_create_nonce( 'convert' ); ?>"
|
||||
href="#"
|
||||
class="wio-convert button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php esc_html_e( 'Convert in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-images-alt2"></span>
|
||||
<?php echo esc_html( $avif_size ? __( 'Re-convert to AVIF', 'robin-image-optimizer' ) : __( 'Convert to AVIF', 'robin-image-optimizer' ) ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ( $backuped ) : ?>
|
||||
<a
|
||||
href="#" data-action="<?php echo esc_attr( $ajaxActionRestore ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'restore' ); ?>"
|
||||
class="button-wio-restore attachment-has-backup"
|
||||
data-waiting-label="<?php esc_html_e( 'Recovery in progress', 'robin-image-optimizer' ); ?>"
|
||||
>
|
||||
<span class="dashicons dashicons-image-rotate"></span>
|
||||
<?php esc_html_e( 'Restore original', 'robin-image-optimizer' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<!-- .wio-datas-actions-links -->
|
||||
<?php
|
||||
} elseif ( $attach_dimensions !== '0 x 0' ) {
|
||||
// Show full stats view if any conversion exists
|
||||
if ( $webp_size || $avif_size ) {
|
||||
?>
|
||||
<ul class="wio-datas-list" data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
|
||||
data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>">
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php esc_html_e( 'Original Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $attachment_file_size, 1 ) ); ?></strong>
|
||||
</li>
|
||||
<?php if ( $webp_size ) : ?>
|
||||
<?php $webp_saving = $attachment_file_size ? round( ( $attachment_file_size - $webp_size ) * 100 / $attachment_file_size ) : 0; ?>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php esc_html_e( 'WebP Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $webp_size, 1 ) ); ?></strong>
|
||||
<span class="wio-chart-value">(<?php echo esc_attr( $webp_saving ); ?>%)</span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<?php if ( $avif_size ) : ?>
|
||||
<?php $avif_saving = $attachment_file_size ? round( ( $attachment_file_size - $avif_size ) * 100 / $attachment_file_size ) : 0; ?>
|
||||
<li class="wio-data-item">
|
||||
<span class="data"><?php esc_html_e( 'AVIF Filesize:', 'robin-image-optimizer' ); ?></span>
|
||||
<strong class="big"><?php echo esc_attr( size_format( $avif_size, 1 ) ); ?></strong>
|
||||
<span class="wio-chart-value">(<?php echo esc_attr( $avif_saving ); ?>%)</span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<div class="wio-datas-actions-links" style="display:inline;">
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-level=""
|
||||
data-nonce="<?php echo wp_create_nonce( 'reoptimize' ); ?>"
|
||||
href="#"
|
||||
class="wio-reoptimize button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php esc_attr_e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-admin-generic"></span>
|
||||
<?php esc_html_e( 'Optimize', 'robin-image-optimizer' ); ?>
|
||||
</a>
|
||||
<?php // WebP conversion is available to all users ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="webp"
|
||||
data-nonce="<?php echo esc_attr( wp_create_nonce( 'convert' ) ); ?>"
|
||||
href="#"
|
||||
class="wio-convert button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php esc_attr_e( 'Convert in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-images-alt2"></span>
|
||||
<?php echo esc_html( $webp_size ? __( 'Re-convert to WebP', 'robin-image-optimizer' ) : __( 'Convert to WebP', 'robin-image-optimizer' ) ); ?>
|
||||
</a>
|
||||
<?php // AVIF conversion is a premium-only feature ?>
|
||||
<?php if ( wrio_is_license_activate() ) : ?>
|
||||
<a data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="avif"
|
||||
data-nonce="<?php echo esc_attr( wp_create_nonce( 'convert' ) ); ?>"
|
||||
href="#"
|
||||
class="wio-convert button-wio-manual-override-upload"
|
||||
data-waiting-label="<?php esc_attr_e( 'Convert in progress', 'robin-image-optimizer' ); ?>">
|
||||
<span class="dashicons dashicons-images-alt2"></span>
|
||||
<?php echo esc_html( $avif_size ? __( 'Re-convert to AVIF', 'robin-image-optimizer' ) : __( 'Convert to AVIF', 'robin-image-optimizer' ) ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ( $backuped ) : ?>
|
||||
<a
|
||||
href="#" data-action="<?php echo esc_attr( $ajaxActionRestore ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-nonce="<?php echo esc_attr( wp_create_nonce( 'restore' ) ); ?>"
|
||||
class="button-wio-restore attachment-has-backup"
|
||||
data-waiting-label="<?php esc_attr_e( 'Recovery in progress', 'robin-image-optimizer' ); ?>"
|
||||
>
|
||||
<span class="dashicons dashicons-image-rotate"></span>
|
||||
<?php esc_html_e( 'Restore original', 'robin-image-optimizer' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<!-- .wio-datas-actions-links -->
|
||||
<?php
|
||||
} else {
|
||||
// No conversions yet - show buttons
|
||||
?>
|
||||
<button
|
||||
type="button" data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'reoptimize' ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>" data-level=""
|
||||
class="wio-reoptimize button button-primary button-large"
|
||||
data-waiting-label="<?php esc_attr_e( 'Optimization in progress', 'robin-image-optimizer' ); ?>"
|
||||
data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
|
||||
data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Optimize', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
<?php // WebP conversion is available to all users ?>
|
||||
<button
|
||||
type="button" data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'convert' ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="webp"
|
||||
class="wio-convert button button-primary button-large"
|
||||
data-waiting-label="<?php esc_attr_e( 'Convert in progress', 'robin-image-optimizer' ); ?>"
|
||||
data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
|
||||
data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Convert to WebP', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
<?php // AVIF conversion is a premium-only feature ?>
|
||||
<?php if ( wrio_is_license_activate() ) : ?>
|
||||
<button
|
||||
type="button" data-action="<?php echo esc_attr( $ajaxActionConvert ); ?>"
|
||||
data-nonce="<?php echo wp_create_nonce( 'convert' ); ?>"
|
||||
data-id="<?php echo esc_attr( $attachment_id ); ?>"
|
||||
data-format="avif"
|
||||
class="wio-convert button button-primary button-large"
|
||||
data-waiting-label="<?php esc_attr_e( 'Convert in progress', 'robin-image-optimizer' ); ?>"
|
||||
data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
|
||||
data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>"
|
||||
>
|
||||
<?php esc_html_e( 'Convert to AVIF', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* Sidebar widgets
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Return support widget markup
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wrio_get_sidebar_support_widget() {
|
||||
$free_support_url = 'https://wordpress.org/support/plugin/robin-image-optimizer/';
|
||||
$support_url = tsdk_utmify( tsdk_translate_link( 'https://themeisle.com/contact/' ), 'bug-security-ticket' );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div id="wbcr-clr-support-widget" class="wbcr-factory-sidebar-widget">
|
||||
<p><strong><?php _e( 'Having Issues?', 'robin-image-optimizer' ); ?></strong></p>
|
||||
<div class="wbcr-clr-support-widget-body">
|
||||
<p>
|
||||
<?php _e( 'Need help? Create a support ticket and we\'ll assist you.', 'robin-image-optimizer' ); ?>
|
||||
</p>
|
||||
<ul>
|
||||
<li><span class="dashicons dashicons-sos"></span>
|
||||
<a href="<?php echo esc_url( $free_support_url ); ?>" target="_blank" rel="noopener">
|
||||
<?php esc_html_e( 'Get free support', 'robin-image-optimizer' ); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li style="margin-top: 15px;background: #fff4f1;padding: 10px;color: #a58074;">
|
||||
<span class="dashicons dashicons-warning"></span>
|
||||
<?php
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
// translators: %1$s is opening <a> tag, %2$s is closing </a> tag
|
||||
__( 'Found a bug or security issue? %1$sCreate a ticket%2$s for a faster response.', 'robin-image-optimizer' ),
|
||||
'<a href="' . esc_url( $support_url ) . '" target="_blank" rel="noopener">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
?>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
$output = ob_get_contents();
|
||||
|
||||
ob_end_clean();
|
||||
|
||||
return $output;
|
||||
}
|
||||
2
wp-content/plugins/robin-image-optimizer/admin/index.php
Normal file
2
wp-content/plugins/robin-image-optimizer/admin/index.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,690 @@
|
||||
<?php
|
||||
/**
|
||||
* License Page class
|
||||
*
|
||||
* Self-contained license management page without Factory Templates dependency.
|
||||
* Reuses existing factory-bootstrap-500 CSS framework.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
* @subpackage Admin\Pages
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_License_Page_View
|
||||
*
|
||||
* Handles the license management admin page.
|
||||
*/
|
||||
class WRIO_License_Page_View {
|
||||
|
||||
/**
|
||||
* Page ID
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $id = 'wrio_license';
|
||||
|
||||
/**
|
||||
* Page hook suffix
|
||||
*
|
||||
* @var string|false
|
||||
*/
|
||||
private $page_hook;
|
||||
|
||||
/**
|
||||
* Premium provider instance
|
||||
*
|
||||
* @var WRIO_Premium_Provider
|
||||
* @phpstan-ignore-next-line property.onlyRead
|
||||
*/
|
||||
private $premium;
|
||||
|
||||
/**
|
||||
* License instance
|
||||
*
|
||||
* @var WRIO_License
|
||||
* @phpstan-ignore-next-line property.onlyRead
|
||||
*/
|
||||
private $license;
|
||||
|
||||
/**
|
||||
* Whether premium is activated
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
private $is_premium;
|
||||
|
||||
/**
|
||||
* Plan name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $plan_name;
|
||||
|
||||
/**
|
||||
* Subscribe widget instance
|
||||
*
|
||||
* @var WRIO_Subscribe_Widget
|
||||
*/
|
||||
private $subscribe_widget;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->is_premium = null;
|
||||
$this->plan_name = __( 'Robin image optimizer Premium', 'robin-image-optimizer' );
|
||||
|
||||
// Initialize subscribe widget.
|
||||
$this->subscribe_widget = new WRIO_Subscribe_Widget();
|
||||
|
||||
add_action( 'admin_menu', [ $this, 'register_page' ], 9 );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
|
||||
add_action( 'wp_ajax_wrio_license_action', [ $this, 'ajax_handler' ] );
|
||||
|
||||
// Register this page in the Factory navigation menu
|
||||
$this->register_in_factory_menu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this page in the Factory impressive menu system
|
||||
*
|
||||
* This allows the license page to appear in the left navigation bar
|
||||
* alongside other Factory-registered pages.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function register_in_factory_menu() {
|
||||
global $factory_impressive_page_menu;
|
||||
|
||||
$page_url = admin_url( 'admin.php?page=' . $this->id );
|
||||
|
||||
$factory_impressive_page_menu['robin-image-optimizer'][ $this->id ] = [
|
||||
'type' => 'page',
|
||||
'url' => $page_url,
|
||||
'title' => __( 'License', 'robin-image-optimizer' ) . ' <span class="dashicons dashicons-admin-network"></span>',
|
||||
'short_description' => __( 'Product activation', 'robin-image-optimizer' ),
|
||||
'position' => 10,
|
||||
'parent' => 'rio_general',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize premium data
|
||||
*
|
||||
* Called late to ensure WRIO_Plugin is available.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_premium() {
|
||||
if ( null !== $this->premium ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the premium provider instance.
|
||||
*
|
||||
* @var WRIO_Premium_Provider $premium
|
||||
*/
|
||||
$premium = WRIO_Plugin::app()->premium;
|
||||
$this->premium = $premium;
|
||||
/**
|
||||
* Gets the license instance from premium provider.
|
||||
*
|
||||
* @var WRIO_License $license
|
||||
*/
|
||||
$license = $premium->get_license();
|
||||
$this->license = $license;
|
||||
$this->is_premium = $premium->is_activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the admin page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register_page() {
|
||||
// Parent slug follows Factory pattern: {page_id}-{plugin_name}
|
||||
$parent_slug = 'rio_general-robin-image-optimizer';
|
||||
|
||||
$this->page_hook = add_submenu_page(
|
||||
$parent_slug,
|
||||
__( 'License', 'robin-image-optimizer' ),
|
||||
__( 'License', 'robin-image-optimizer' ),
|
||||
'manage_options',
|
||||
$this->id,
|
||||
[ $this, 'render_page' ],
|
||||
90 // Position before About Us (which uses 100)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue page assets
|
||||
*
|
||||
* @param string $hook Current admin page hook.
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_assets( $hook ) {
|
||||
if ( $this->page_hook !== $hook ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_script(
|
||||
'wrio-license-manager',
|
||||
WRIO_PLUGIN_URL . '/admin/assets/js/wrio-license-manager.js',
|
||||
[ 'jquery' ],
|
||||
WRIO_Plugin::app()->getPluginVersion(),
|
||||
true
|
||||
);
|
||||
|
||||
// Enqueue the Factory bootstrap core CSS (provides .btn, .form-control, etc.)
|
||||
wp_enqueue_style(
|
||||
'wrio-bootstrap-core',
|
||||
WRIO_PLUGIN_URL . '/libs/factory/bootstrap/assets/css-min/bootstrap.core.min.css',
|
||||
[],
|
||||
WRIO_Plugin::app()->getPluginVersion()
|
||||
);
|
||||
|
||||
// Enqueue the license manager CSS from Factory templates
|
||||
wp_enqueue_style(
|
||||
'wrio-license-manager',
|
||||
WRIO_PLUGIN_URL . '/libs/factory/templates/assets/css/license-manager.css',
|
||||
[ 'wrio-bootstrap-core' ],
|
||||
WRIO_Plugin::app()->getPluginVersion()
|
||||
);
|
||||
|
||||
// Enqueue the impressive page template CSS for full layout
|
||||
wp_enqueue_style(
|
||||
'wrio-impressive-template',
|
||||
WRIO_PLUGIN_URL . '/libs/factory/templates/pages/templates/impressive/assets/css/impressive.page.template.css',
|
||||
[ 'wrio-bootstrap-core' ],
|
||||
WRIO_Plugin::app()->getPluginVersion()
|
||||
);
|
||||
|
||||
// Enqueue subscribe widget CSS.
|
||||
wp_enqueue_style(
|
||||
'wrio-subscribe-widget',
|
||||
WRIO_PLUGIN_URL . '/admin/assets/css/wrio-subscribe-widget.css',
|
||||
[],
|
||||
WRIO_Plugin::app()->getPluginVersion()
|
||||
);
|
||||
|
||||
// Hide internal pages from sidebar (Custom Folders, Nextgen Gallery)
|
||||
wp_add_inline_style(
|
||||
'wrio-impressive-template',
|
||||
'#io_folders_statistic-robin-image-optimizer-tab, #io_nextgen_gallery_statistic-robin-image-optimizer-tab { display: none !important; }'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX license actions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_handler() {
|
||||
// Verify nonce
|
||||
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'wrio_license_action' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Security check failed.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
// Check capabilities
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'You do not have permission to perform this action.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
$this->init_premium();
|
||||
|
||||
$action = isset( $_POST['license_action'] ) ? sanitize_text_field( wp_unslash( $_POST['license_action'] ) ) : '';
|
||||
$license_key = isset( $_POST['licensekey'] ) ? trim( wp_unslash( $_POST['licensekey'] ) ) : '';
|
||||
|
||||
$allowed_actions = [ 'activate', 'deactivate', 'unsubscribe' ];
|
||||
|
||||
if ( empty( $action ) || ! in_array( $action, $allowed_actions, true ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Invalid action.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
try {
|
||||
switch ( $action ) {
|
||||
case 'activate':
|
||||
if ( empty( $license_key ) ) {
|
||||
wp_send_json_error( [ 'message' => __( 'Please enter a license key.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
$this->premium->activate( $license_key );
|
||||
$message = __( 'Your license has been successfully activated.', 'robin-image-optimizer' );
|
||||
break;
|
||||
|
||||
case 'deactivate':
|
||||
$this->premium->deactivate();
|
||||
$message = __( 'Your license has been deactivated.', 'robin-image-optimizer' );
|
||||
break;
|
||||
|
||||
case 'unsubscribe':
|
||||
$this->premium->cancel_paid_subscription();
|
||||
$message = __( 'Your subscription has been cancelled.', 'robin-image-optimizer' );
|
||||
break;
|
||||
|
||||
default:
|
||||
wp_send_json_error( [ 'message' => __( 'Unknown action.', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
wp_send_json_success( [ 'message' => $message ] );
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
wp_send_json_error( [ 'message' => $e->getMessage() ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the admin page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function render_page() {
|
||||
$this->init_premium();
|
||||
$min_height = $this->calculate_menu_height();
|
||||
?>
|
||||
<div id="WBCR" class="wrap">
|
||||
<div class="wbcr-factory-templates-759-impressive-page-template factory-bootstrap-500 factory-fontawesome-000">
|
||||
<div class="wbcr-factory-page wbcr-factory-page-<?php echo esc_attr( $this->id ); ?>">
|
||||
<?php $this->render_header(); ?>
|
||||
<div class="wbcr-factory-left-navigation-bar">
|
||||
<?php $this->render_page_menu(); ?>
|
||||
</div>
|
||||
<div class="wbcr-factory-page-inner-wrap">
|
||||
<div class="wbcr-factory-content-section wbcr-fullwidth">
|
||||
<div class="wbcr-factory-content" style="min-height:<?php echo esc_attr( (string) $min_height ); ?>px">
|
||||
<div id="wrio-license-wrapper"
|
||||
data-loader="<?php echo esc_url( admin_url( 'images/spinner.gif' ) ); ?>"
|
||||
data-nonce="<?php echo esc_attr( wp_create_nonce( 'wrio_license_action' ) ); ?>">
|
||||
<?php $this->render_license_form(); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate minimum height for content based on menu items
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function calculate_menu_height() {
|
||||
global $factory_impressive_page_menu;
|
||||
|
||||
$menu_scope = 'robin-image-optimizer';
|
||||
$min_height = 0;
|
||||
|
||||
if ( isset( $factory_impressive_page_menu[ $menu_scope ] ) ) {
|
||||
foreach ( $factory_impressive_page_menu[ $menu_scope ] as $page ) {
|
||||
if ( ! isset( $page['parent'] ) || empty( $page['parent'] ) ) {
|
||||
$min_height += 77;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $min_height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the page header
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_header() {
|
||||
?>
|
||||
<div class="wbcr-factory-page-header">
|
||||
<div class="wbcr-factory-header-logo">
|
||||
<?php echo esc_html( WRIO_Plugin::app()->getPluginTitle() ); ?>
|
||||
<span class="version"><?php echo esc_html( WRIO_Plugin::app()->getPluginVersion() ); ?></span>
|
||||
<span class="dash">—</span>
|
||||
</div>
|
||||
<div class="wbcr-factory-header-title">
|
||||
<h2><?php esc_html_e( 'Page', 'robin-image-optimizer' ); ?>: <?php esc_html_e( 'License', 'robin-image-optimizer' ); ?></h2>
|
||||
</div>
|
||||
<div class="wbcr-factory-control">
|
||||
<?php do_action( 'wbcr_factory_pages_impressive_header', WRIO_Plugin::app()->getPluginName() ); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the left navigation menu
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_page_menu() {
|
||||
global $factory_impressive_page_menu;
|
||||
|
||||
$menu_scope = 'robin-image-optimizer';
|
||||
$self_page_id = $this->id;
|
||||
|
||||
if ( ! isset( $factory_impressive_page_menu[ $menu_scope ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page_menu = $factory_impressive_page_menu[ $menu_scope ];
|
||||
|
||||
// Sort by position (descending - higher position = higher in menu)
|
||||
uasort(
|
||||
$page_menu,
|
||||
function ( $a, $b ) {
|
||||
return $b['position'] <=> $a['position'];
|
||||
}
|
||||
);
|
||||
|
||||
?>
|
||||
<ul>
|
||||
<?php
|
||||
// First, render parent pages
|
||||
foreach ( $page_menu as $page_screen => $page ) :
|
||||
if ( ! empty( $page['parent'] ) ) {
|
||||
continue;
|
||||
}
|
||||
$active_tab = ( $page_screen === $self_page_id ) ? ' wbcr-factory-active-tab' : '';
|
||||
?>
|
||||
<li class="wbcr-factory-nav-tab<?php echo esc_attr( $active_tab ); ?>">
|
||||
<a href="<?php echo esc_url( $page['url'] ); ?>"
|
||||
id="<?php echo esc_attr( $page_screen ); ?>-tab"
|
||||
class="wbcr-factory-tab__link js-wbcr-factory-tab__link">
|
||||
<div class="wbcr-factory-tab__title">
|
||||
<?php echo wp_kses( $page['title'], [ 'span' => [ 'class' => [] ] ] ); ?>
|
||||
</div>
|
||||
<?php if ( ! empty( $page['short_description'] ) ) : ?>
|
||||
<div class="wbcr-factory-tab__short-description">
|
||||
<?php echo esc_html( $page['short_description'] ); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php
|
||||
// Then, render child pages
|
||||
foreach ( $page_menu as $page_screen => $page ) :
|
||||
if ( empty( $page['parent'] ) ) {
|
||||
continue;
|
||||
}
|
||||
$active_tab = ( $page_screen === $self_page_id ) ? ' wbcr-factory-active-tab' : '';
|
||||
?>
|
||||
<li class="wbcr-factory-nav-tab<?php echo esc_attr( $active_tab ); ?>">
|
||||
<a href="<?php echo esc_url( $page['url'] ); ?>"
|
||||
id="<?php echo esc_attr( $page_screen ); ?>-tab"
|
||||
class="wbcr-factory-tab__link js-wbcr-factory-tab__link">
|
||||
<div class="wbcr-factory-tab__title">
|
||||
<?php echo wp_kses( $page['title'], [ 'span' => [ 'class' => [] ] ] ); ?>
|
||||
</div>
|
||||
<?php if ( ! empty( $page['short_description'] ) ) : ?>
|
||||
<div class="wbcr-factory-tab__short-description">
|
||||
<?php echo esc_html( $page['short_description'] ); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license type for CSS class
|
||||
*
|
||||
* @return string free|gift|trial|paid
|
||||
*/
|
||||
private function get_license_type() {
|
||||
if ( ! $this->is_premium || null === $this->license ) {
|
||||
return 'free';
|
||||
}
|
||||
|
||||
if ( $this->license->is_lifetime() ) {
|
||||
return 'gift';
|
||||
}
|
||||
|
||||
if ( $this->license->get_expiration_time( 'days' ) < 1 ) {
|
||||
return 'trial';
|
||||
}
|
||||
|
||||
return 'paid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hidden license key (first 8 + last 4 characters)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_hidden_license_key() {
|
||||
if ( ! $this->is_premium || null === $this->license ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->license->get_masked_key();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get unified expiration display string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_expiration_display() {
|
||||
if ( ! $this->is_premium || null === $this->license ) {
|
||||
return __( 'N/A', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
return $this->license->get_expiration_display();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the license form
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_license_form() {
|
||||
$license_type = $this->get_license_type();
|
||||
|
||||
$plan_title = __( 'Free', 'robin-image-optimizer' );
|
||||
$has_pro = null !== $this->premium && null !== $this->premium->get_plan_id();
|
||||
if ( $has_pro ) {
|
||||
$plan_title = __( 'Pro', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
$license_status_label = __( 'License', 'robin-image-optimizer' );
|
||||
|
||||
?>
|
||||
<div id="license-manager"
|
||||
class="factory-bootstrap-500 onp-page-wrap <?php echo esc_attr( $license_type ); ?>-license-manager-content">
|
||||
|
||||
<?php if ( ! $has_pro ) : ?>
|
||||
<?php $this->render_upgrade_banner(); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="onp-container">
|
||||
<div class="license-details">
|
||||
<h3>
|
||||
<?php echo esc_html( $license_status_label ); ?>
|
||||
<span class="license-info-badge <?php echo esc_attr( 'license-info-badge--plan-' . ( $has_pro ? 'pro' : 'free' ) ); ?>">
|
||||
<?php echo esc_html( $plan_title ); ?>
|
||||
</span>
|
||||
<?php if ( $this->is_premium ) : ?>
|
||||
<span class="license-info-badge license-info-badge--expiration">
|
||||
<?php echo esc_html( $this->get_expiration_display() ); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="license-input">
|
||||
<form action="" method="post">
|
||||
<?php if ( $this->is_premium ) : ?>
|
||||
<p><?php esc_html_e( 'Have a key to activate the premium version? Paste it here:', 'robin-image-optimizer' ); ?></p>
|
||||
<?php else : ?>
|
||||
<p><?php esc_html_e( 'Have a key to activate the plugin? Paste it here:', 'robin-image-optimizer' ); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
|
||||
<div class="license-key-wrap">
|
||||
<input
|
||||
type="text"
|
||||
id="license-key"
|
||||
name="licensekey"
|
||||
value=""
|
||||
class="form-control"
|
||||
placeholder="<?php echo esc_html( $this->get_hidden_license_key() ); ?>"
|
||||
/>
|
||||
|
||||
<?php if ( $this->is_premium ) : ?>
|
||||
<button data-action="deactivate" class="button button-primary wrio-license-btn" type="button" id="license-submit">
|
||||
<?php esc_html_e( 'Deactivate', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
<?php else : ?>
|
||||
<button data-action="activate" class="button button-primary wrio-license-btn" type="button" id="license-submit">
|
||||
<?php esc_html_e( 'Activate', 'robin-image-optimizer' ); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php $this->render_learnmore_section(); ?>
|
||||
|
||||
<div id="license-form-error-container"></div>
|
||||
<?php $this->render_freemius_migration_notice(); ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php $this->subscribe_widget->render(); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the upgrade banner for free users
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_upgrade_banner() {
|
||||
$support = WRIO_Plugin::app()->get_support();
|
||||
$pricing_url = $support->get_pricing_url( true, 'license_banner' );
|
||||
?>
|
||||
<div class="wrio-license-upgrade-banner">
|
||||
<div class="wrio-license-upgrade-icon">
|
||||
<span>⚡</span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-content">
|
||||
<h2 class="wrio-license-upgrade-title"><?php esc_html_e( 'Supercharge Your Image Optimization', 'robin-image-optimizer' ); ?></h2>
|
||||
<p class="wrio-license-upgrade-subtitle"><?php esc_html_e( "You're using the free version. Upgrade to unlock unlimited conversions and premium features.", 'robin-image-optimizer' ); ?></p>
|
||||
<div class="wrio-license-upgrade-features">
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'Unlimited AVIF', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'Custom Folders Optimization', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'NextGen Gallery Support', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'Priority Support', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'More Compression Modes', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
<div class="wrio-license-upgrade-feature">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
<span><?php esc_html_e( 'Super Page Cache Pro', 'robin-image-optimizer' ); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="<?php echo esc_url( $pricing_url ); ?>" class="wrio-license-upgrade-button" target="_blank" rel="noopener">
|
||||
<?php esc_html_e( 'View Pro Plans', 'robin-image-optimizer' ); ?> →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render learn more section
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_learnmore_section() {
|
||||
$support = WRIO_Plugin::app()->get_support();
|
||||
|
||||
if ( ! $this->is_premium ) :
|
||||
?>
|
||||
<p style="margin-top: 10px;">
|
||||
<?php
|
||||
printf(
|
||||
wp_kses(
|
||||
/* translators: %1$s: opening link tag, %2$s: closing link tag */
|
||||
__( 'Can\'t find your key? Go to %1$sthis page%2$s and login using the e-mail address associated with your purchase.', 'robin-image-optimizer' ),
|
||||
[
|
||||
'a' => [
|
||||
'href' => [],
|
||||
'target' => [],
|
||||
'rel' => [],
|
||||
],
|
||||
]
|
||||
),
|
||||
'<a href="' . esc_url( $support->get_contacts_url( true, 'license_page' ) ) . '" target="_blank" rel="noopener">',
|
||||
'</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render migration notice for Freemius license holders
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function render_freemius_migration_notice() {
|
||||
if ( ! $this->is_premium || null === $this->license || $this->license->get_source() !== WRIO_License::SOURCE_FREEMIUS ) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<div class="wrio-migration-notice">
|
||||
<p>
|
||||
<strong><?php esc_html_e( 'Action Required:', 'robin-image-optimizer' ); ?></strong>
|
||||
<?php esc_html_e( 'Your license was issued through our previous licensing system (Freemius). Please contact support to receive a new Themeisle license key for continued access to updates and support.', 'robin-image-optimizer' ); ?>
|
||||
</p>
|
||||
<p>
|
||||
<a href="<?php echo esc_url( WRIO_Plugin::app()->get_support()->get_contacts_url( true, 'license_migration' ) ); ?>" target="_blank" rel="noopener" class="button button-primary">
|
||||
<?php esc_html_e( 'Contact Support', 'robin-image-optimizer' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс отвечает за работу страницы логов.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_LogPage extends Wbcr_FactoryLogger359_PageBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $id = 'rio_logs'; // Уникальный идентификатор страницы
|
||||
|
||||
/**
|
||||
* Hide bottom sidebar - only show on Settings page
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $show_bottom_sidebar = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $page_parent_page = 'rio_general';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $available_for_multisite = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $clearfy_collaboration = false;
|
||||
|
||||
/**
|
||||
*
|
||||
* Whether to show the right sidebar in options.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $show_right_sidebar_in_options = true;
|
||||
|
||||
/**
|
||||
* Menu target for WordPress admin submenu.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $menu_target = 'rio_general-robin-image-optimizer';
|
||||
|
||||
/**
|
||||
* Use admin.php as base URL instead of menu_target.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $custom_target = true;
|
||||
|
||||
/**
|
||||
* The page is internal and should not be displayed in the menu.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $internal = false;
|
||||
|
||||
/**
|
||||
* View instance for rendering templates.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @var WRIO_Views
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* Подменяем пространство имен для меню плагина, если активирован плагин
|
||||
* Меню текущего плагина будет добавлено в общее меню
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMenuScope() {
|
||||
if ( $this->clearfy_collaboration ) {
|
||||
$this->page_parent_page = 'rio_general';
|
||||
|
||||
return 'wbcr_clearfy';
|
||||
}
|
||||
|
||||
return 'robin-image-optimizer';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WRIO_Plugin $plugin
|
||||
*/
|
||||
public function __construct( WRIO_Plugin $plugin ) {
|
||||
$this->menu_title = __( 'Error Log', 'robin-image-optimizer' );
|
||||
$this->page_menu_short_description = __( 'Plugin debug report', 'robin-image-optimizer' );
|
||||
|
||||
$this->view = WRIO_Views::get_instance( WRIO_PLUGIN_DIR );
|
||||
if ( is_multisite() && defined( 'WCL_PLUGIN_ACTIVE' ) ) {
|
||||
if ( WRIO_Plugin::app()->isNetworkActive() && WCL_Plugin::app()->isNetworkActive() ) {
|
||||
$this->clearfy_collaboration = true;
|
||||
}
|
||||
} elseif ( defined( 'WCL_PLUGIN_ACTIVE' ) ) {
|
||||
$this->clearfy_collaboration = true;
|
||||
}
|
||||
|
||||
parent::__construct( $plugin );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function assets( $scripts, $styles ) {
|
||||
parent::assets( $scripts, $styles );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMenuTitle() {
|
||||
return defined( 'LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON' ) ? __( 'Image optimizer', 'robin-image-optimizer' ) : __( 'Error Log', 'robin-image-optimizer' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The page Settings.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс отвечает за работу страницы настроек
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Page extends WBCR\Factory_Templates_759\Impressive {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $page_parent_page = null;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $available_for_multisite = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $clearfy_collaboration = false;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $show_right_sidebar_in_options = false;
|
||||
|
||||
/**
|
||||
* Hide bottom sidebar by default - only show on Settings page
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $show_bottom_sidebar = false;
|
||||
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
* @var WRIO_Views
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @param WRIO_Plugin $plugin
|
||||
*/
|
||||
public function __construct( WRIO_Plugin $plugin ) {
|
||||
$this->view = WRIO_Views::get_instance( WRIO_PLUGIN_DIR );
|
||||
|
||||
if ( is_multisite() && defined( 'WCL_PLUGIN_ACTIVE' ) ) {
|
||||
if ( WRIO_Plugin::app()->isNetworkActive() && WCL_Plugin::app()->isNetworkActive() ) {
|
||||
$this->clearfy_collaboration = true;
|
||||
}
|
||||
} elseif ( defined( 'WCL_PLUGIN_ACTIVE' ) ) {
|
||||
$this->clearfy_collaboration = true;
|
||||
}
|
||||
|
||||
parent::__construct( $plugin );
|
||||
}
|
||||
|
||||
/**
|
||||
* Подменяем простраинство имен для меню плагина, если активирован плагин
|
||||
* Меню текущего плагина будет добавлено в общее меню
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMenuScope() {
|
||||
if ( $this->clearfy_collaboration ) {
|
||||
$this->page_parent_page = 'rio_general';
|
||||
|
||||
return 'wbcr_clearfy';
|
||||
}
|
||||
|
||||
return 'robin-image-optimizer';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,687 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс отвечает за работу страницы настроек
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_SettingsPage extends WRIO_Page {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $id = 'rio_settings';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $page_menu_dashicon = 'dashicons-admin-generic';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $page_parent_page = 'rio_general';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $show_right_sidebar_in_options = false;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @since 1.1.3 - Added
|
||||
* @var bool
|
||||
*/
|
||||
public $show_bottom_sidebar = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $show_search_options_form = false;
|
||||
|
||||
/**
|
||||
* Menu target for WordPress admin submenu.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $menu_target = 'rio_general-robin-image-optimizer';
|
||||
|
||||
/**
|
||||
* Use admin.php as base URL instead of menu_target.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $custom_target = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $internal = false;
|
||||
|
||||
/**
|
||||
* @param WRIO_Plugin $plugin
|
||||
*/
|
||||
public function __construct( WRIO_Plugin $plugin ) {
|
||||
|
||||
$this->menu_title = __( 'Settings', 'robin-image-optimizer' );
|
||||
$this->page_menu_short_description = __( 'Plugin configuration', 'robin-image-optimizer' );
|
||||
|
||||
if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
|
||||
$this->show_search_options_form = true;
|
||||
}
|
||||
|
||||
add_filter(
|
||||
'wbcr/factory/option_image_optimization_type',
|
||||
function ( $option_value ) {
|
||||
if ( ! wrio_is_license_activate() && $option_value === 'background' ) {
|
||||
$option_value = 'schedule';
|
||||
}
|
||||
|
||||
return $option_value;
|
||||
}
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'wbcr/factory/option_convert_avif_format',
|
||||
function ( $option_value ) {
|
||||
if ( ! wrio_is_license_activate() && $option_value ) {
|
||||
$option_value = false;
|
||||
}
|
||||
|
||||
return $option_value;
|
||||
}
|
||||
);
|
||||
|
||||
parent::__construct( $plugin );
|
||||
}
|
||||
|
||||
/**
|
||||
* Подключаем скрипты и стили для страницы
|
||||
*
|
||||
* @return void
|
||||
* @since 1.0.0
|
||||
* @see Wbcr_FactoryPages600_AdminPage
|
||||
*/
|
||||
public function assets( $scripts, $styles ) {
|
||||
parent::assets( $scripts, $styles );
|
||||
|
||||
$this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css' );
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/restore-backup.js' );
|
||||
|
||||
if ( ! wrio_is_license_activate() ) {
|
||||
$this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/settings-premium.css' );
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/settings-premium.js' );
|
||||
}
|
||||
|
||||
if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
|
||||
$this->styles->add( WCL_PLUGIN_URL . '/admin/assets/css/general.css' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Выводим предупреждения
|
||||
*/
|
||||
protected function warningNotice() {
|
||||
$upload_dir = wp_upload_dir();
|
||||
|
||||
if ( ! wp_is_writable( $upload_dir['basedir'] ) ) {
|
||||
$this->printErrorNotice(
|
||||
sprintf(
|
||||
// translators: %s is the folder path.
|
||||
__( 'Folder %s is unavailable for writing', 'robin-image-optimizer' ),
|
||||
'wp-content/uploads/'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$wio_backup = $upload_dir['basedir'] . '/wio_backup/';
|
||||
if ( file_exists( $wio_backup ) && ! wp_is_writable( $wio_backup ) ) {
|
||||
$this->printErrorNotice(
|
||||
sprintf(
|
||||
// translators: %s is the folder path.
|
||||
__( 'Folder %s is unavailable for writing', 'robin-image-optimizer' ),
|
||||
'wp-content/uploads/wio-backup/'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON == true ) {
|
||||
$this->printErrorNotice(
|
||||
sprintf(
|
||||
// translators: %s is the file name (wp-config.php).
|
||||
__( 'Cron is disabled in %s', 'robin-image-optimizer' ),
|
||||
'wp-config.php'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Метод должен передать массив опций для создания формы с полями.
|
||||
* Созданием страницы и формы занимается фреймворк
|
||||
*
|
||||
* @return mixed[]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getPageOptions() {
|
||||
$options = [];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __( 'Main Settings', 'robin-image-optimizer' ) . '</strong><p>' . __( 'Configure image optimization settings.', 'robin-image-optimizer' ) . '</p></div>',
|
||||
];
|
||||
|
||||
// Радио переключатель
|
||||
$options[] = [
|
||||
'type' => 'dropdown',
|
||||
'name' => 'image_optimization_level',
|
||||
'way' => 'buttons',
|
||||
'title' => __( 'Compression mode', 'robin-image-optimizer' ),
|
||||
'data' => [
|
||||
[
|
||||
'normal',
|
||||
__( 'Lossless', 'robin-image-optimizer' ),
|
||||
__( 'This mode provides lossless compression and your images will be optimized without visible changes. If you want an ideal image quality, we recommend this mode. The size of the files will be reduced approximately 2 times. If this is not enough for you, try other modes.', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'aggresive',
|
||||
__( 'Lossy', 'robin-image-optimizer' ),
|
||||
__( 'This mode provides an ideal optimization of your images without significant quality loss. The file size will be reduced approximately 5 times with a slight decrease in image quality. In most cases that cannot be seen with the naked eye.', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'ultra',
|
||||
__( 'High', 'robin-image-optimizer' ),
|
||||
__( 'This mode will use all available optimization methods for maximum image compression. The file size will be reduced approximately 7 times. The quality of some images may deteriorate slightly. Use this mode if you need the maximum weight reduction, and you are ready to accept the loss of image quality.', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'googlepage',
|
||||
__( 'G PageSpeed', 'robin-image-optimizer' ),
|
||||
__( 'This mode uses the optimal settings for Google Page Speed', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'custom',
|
||||
__( 'Custom', 'robin-image-optimizer' ),
|
||||
__( 'This mode allows you to configure your own compression ratio.', 'robin-image-optimizer' ),
|
||||
],
|
||||
],
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Select the compression mode appropriate for your case.', 'robin-image-optimizer' ),
|
||||
'default' => 'normal',
|
||||
'events' => [
|
||||
'normal' => [
|
||||
'hide' => '.factory-control-image_optimization_level_custom',
|
||||
],
|
||||
'aggresive' => [
|
||||
'hide' => '.factory-control-image_optimization_level_custom',
|
||||
],
|
||||
'ultra' => [
|
||||
'hide' => '.factory-control-image_optimization_level_custom',
|
||||
],
|
||||
'googlepage' => [
|
||||
'hide' => '.factory-control-image_optimization_level_custom',
|
||||
],
|
||||
'custom' => [
|
||||
'show' => '.factory-control-image_optimization_level_custom',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Текстовое поле
|
||||
$options[] = [
|
||||
'type' => 'textbox',
|
||||
'name' => 'image_optimization_level_custom',
|
||||
'title' => __( 'Enter custom quality', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Custom quality 1-100', 'robin-image-optimizer' ),
|
||||
'default' => '70',
|
||||
];
|
||||
|
||||
// Переключатель
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'auto_optimize_when_upload',
|
||||
'title' => __( 'Auto optimization on upload', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Automatically compress all images that you upload directly to the WordPress media library, when editing pages and posts or using themes and plugins.', 'robin-image-optimizer' ),
|
||||
'default' => false,
|
||||
];
|
||||
|
||||
// Переключатель
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'backup_origin_images',
|
||||
'title' => __( 'Backup images', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'green',
|
||||
],
|
||||
'hint' => __( 'Before optimizing, all your images will be saved in a separate folder for future recovery.', 'robin-image-optimizer' ),
|
||||
'default' => true,
|
||||
];
|
||||
|
||||
// Переключатель
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'error_log',
|
||||
'title' => __( 'Error Log', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Enable error logging. The log will be displayed on a separate tab.', 'robin-image-optimizer' ),
|
||||
'default' => false,
|
||||
'eventsOn' => [
|
||||
'show' => '#wrio-error-log-options',
|
||||
],
|
||||
'eventsOff' => [
|
||||
'hide' => '#wrio-error-log-options',
|
||||
],
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => [ $this, 'error_log_options' ],
|
||||
];
|
||||
|
||||
// WebP conversion option (FREE)
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'convert_webp_format',
|
||||
'title' => __( 'Convert Images to WebP', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Convert JPEG & PNG images into WebP format and replace them for browsers which support it. Unsupported browsers would be skipped.', 'robin-image-optimizer' ),
|
||||
'default' => false,
|
||||
];
|
||||
|
||||
// AVIF conversion option (PRO only)
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'convert_avif_format',
|
||||
'title' => __( 'Convert Images to AVIF', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Convert JPEG & PNG images into AVIF format. AVIF provides superior compression but requires a premium license.', 'robin-image-optimizer' ),
|
||||
'default' => false,
|
||||
'cssClass' => ! wrio_is_license_activate() ? [ 'factory-checkbox-disabled', 'wrio-checkbox-premium-label' ] : [],
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => [ $this, 'conver_webp_options' ],
|
||||
];
|
||||
|
||||
// восстановление
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => [ $this, 'rollbackButton' ],
|
||||
];
|
||||
|
||||
// Переключатель
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'save_exif_data',
|
||||
'title' => __( 'Strip EXIF data', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'EXIF is information stored in photos: camera model, shutter speed, exposure compensation, ISO, GPS, etc. By default, the plugin removes EXIF extended data. If your project is dedicated to photography and you need to display this data, then enable this option.', 'robin-image-optimizer' ),
|
||||
'default' => true,
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __( 'Optimization', 'robin-image-optimizer' ) . '</strong><p>' . __( 'Here you can specify additional image optimization options.', 'robin-image-optimizer' ) . '</p></div>',
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'dropdown',
|
||||
'name' => 'image_optimization_order',
|
||||
'way' => 'buttons',
|
||||
'title' => __( 'Optimization order', 'robin-image-optimizer' ),
|
||||
'data' => [
|
||||
[
|
||||
'asc',
|
||||
__( 'Ascending', 'robin-image-optimizer' ),
|
||||
__( 'Optimization will start with old images in the media library', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'desc',
|
||||
__( 'Descending', 'robin-image-optimizer' ),
|
||||
__( 'Optimization will start with new images in the media library', 'robin-image-optimizer' ),
|
||||
],
|
||||
],
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( /** @lang text */ 'Select the optimization order from the media library.', 'robin-image-optimizer' ),
|
||||
'default' => 'asc',
|
||||
];
|
||||
|
||||
// Переключатель
|
||||
$options[] = [
|
||||
'type' => 'checkbox',
|
||||
'way' => 'buttons',
|
||||
'name' => 'resize_larger',
|
||||
'title' => __( 'Resizing large images', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'When you upload images from a camera or stock, they may be too high resolution and it is not necessary for web. The option allows you to automatically change images resolution on upload.', 'robin-image-optimizer' ),
|
||||
'default' => false,
|
||||
// когда чекбокс включен показываем поле с классом .factory-control-resize_larger_w
|
||||
'eventsOn' => [
|
||||
'show' => '.factory-control-resize_larger_w,.factory-control-resize_larger_h',
|
||||
],
|
||||
// когда чекбокс выключен, скрываем поле с классом .factory-control-resize_larger_w
|
||||
'eventsOff' => [
|
||||
'hide' => '.factory-control-resize_larger_w,.factory-control-resize_larger_h',
|
||||
],
|
||||
];
|
||||
|
||||
// Текстовое поле
|
||||
$options[] = [
|
||||
'type' => 'textbox',
|
||||
'name' => 'resize_larger_w',
|
||||
'title' => __( 'Enter the maximum width (px)', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Set the maximum images resolution on the long side. For horizontal images, this will be the width, and for vertical images - the height. The resolution of the images will be changed proportionally according to the set value.', 'robin-image-optimizer' ),
|
||||
'default' => '1600',
|
||||
];
|
||||
|
||||
// Текстовое поле
|
||||
$options[] = [
|
||||
'type' => 'textbox',
|
||||
'name' => 'resize_larger_h',
|
||||
'title' => __( 'Enter the maximum height (px)', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Set the maximum images resolution on the long side. For horizontal images, this will be the width, and for vertical images - the height. The resolution of the images will be changed proportionally according to the set value.', 'robin-image-optimizer' ),
|
||||
'default' => '1600',
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'list',
|
||||
'way' => 'checklist',
|
||||
'name' => 'allowed_formats',
|
||||
'title' => __( 'Optimize formats', 'robin-image-optimizer' ),
|
||||
'data' => [
|
||||
[ 'image/jpeg', 'JPG' ],
|
||||
[ 'image/png', 'PNG' ],
|
||||
[ 'image/gif', 'GIF' ],
|
||||
],
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Choose which formats of images should be optimized and uncheck those that do not need optimization.', 'robin-image-optimizer' ),
|
||||
'default' => 'image/jpeg,image/png,image/gif',
|
||||
];
|
||||
|
||||
// получаем зарегистрированные размеры изображений
|
||||
$wp_image_sizes = wrio_get_image_sizes();
|
||||
$wio_image_sizes = [];
|
||||
foreach ( $wp_image_sizes as $key => $value ) {
|
||||
$wio_image_sizes[] = [
|
||||
$key,
|
||||
$key . ' - ' . $value['width'] . 'x' . $value['height'],
|
||||
];
|
||||
}
|
||||
|
||||
$options[] = [
|
||||
'type' => 'list',
|
||||
'way' => 'checklist',
|
||||
'name' => 'allowed_sizes_thumbnail',
|
||||
'title' => __( 'Optimize thumbnails', 'robin-image-optimizer' ),
|
||||
'data' => $wio_image_sizes,
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Choose which sizes of thumbnails should be optimized and uncheck those that do not need optimization.', 'robin-image-optimizer' ),
|
||||
'default' => 'thumbnail,medium',
|
||||
];
|
||||
|
||||
// cron
|
||||
$options[] = [
|
||||
'type' => 'html',
|
||||
'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __( 'Scheduled and background optimization', 'robin-image-optimizer' ) . '</strong><p>' . __( 'Settings for scheduled and background image optimization.', 'robin-image-optimizer' ) . '</p></div>',
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'dropdown',
|
||||
'name' => 'image_optimization_type',
|
||||
'way' => 'buttons',
|
||||
'title' => __( 'Background optimization type', 'robin-image-optimizer' ),
|
||||
'data' => [
|
||||
[
|
||||
'schedule',
|
||||
__( 'Scheduled', 'robin-image-optimizer' ),
|
||||
__( 'Optimization will take place on a schedule', 'robin-image-optimizer' ),
|
||||
],
|
||||
[
|
||||
'background',
|
||||
__( 'Background', 'robin-image-optimizer' ),
|
||||
__( 'Optimization will occur in the background constantly', 'robin-image-optimizer' ),
|
||||
],
|
||||
],
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => sprintf(
|
||||
// translators: %1$s is the bold start tag, %2$s is the bold end tag.
|
||||
__( '%1$sScheduled optimization%2$s will occur on a scheduled basis.', 'robin-image-optimizer' ),
|
||||
'<b>',
|
||||
'</b>'
|
||||
) . '<br>' . sprintf(
|
||||
// translators: %1$s is the bold start tag, %2$s is the bold end tag.
|
||||
__( '%1$sBackground optimization%2$s will occur in the background constantly.', 'robin-image-optimizer' ),
|
||||
'<b>',
|
||||
'</b>'
|
||||
),
|
||||
'default' => 'schedule',
|
||||
'events' => [
|
||||
'schedule' => [
|
||||
'show' => '#wbcr-io-shedule-options',
|
||||
],
|
||||
'background' => [
|
||||
'hide' => '#wbcr-io-shedule-options',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$group_items[] = [
|
||||
'type' => 'dropdown',
|
||||
'way' => 'buttons',
|
||||
'name' => 'image_autooptimize_shedule_time',
|
||||
'data' => [
|
||||
[ 'wio_1_min', __( '1 minute', 'robin-image-optimizer' ) ],
|
||||
// translators: %s is the number of minutes.
|
||||
[ 'wio_2_min', sprintf( __( '%s minutes', 'robin-image-optimizer' ), '2' ) ],
|
||||
// translators: %s is the number of minutes.
|
||||
[ 'wio_5_min', sprintf( __( '%s minutes', 'robin-image-optimizer' ), '5' ) ],
|
||||
// translators: %s is the number of minutes.
|
||||
[ 'wio_10_min', sprintf( __( '%s minutes', 'robin-image-optimizer' ), '10' ) ],
|
||||
// translators: %s is the number of minutes.
|
||||
[ 'wio_30_min', sprintf( __( '%s minutes', 'robin-image-optimizer' ), '30' ) ],
|
||||
[ 'wio_hourly', __( 'Hour', 'robin-image-optimizer' ) ],
|
||||
[ 'wio_daily', __( 'Day', 'robin-image-optimizer' ) ],
|
||||
],
|
||||
'default' => 'wio_5_min',
|
||||
'title' => __( 'Run every', 'robin-image-optimizer' ),
|
||||
'hint' => __( 'Select time at which the task will be repeated.', 'robin-image-optimizer' ),
|
||||
];
|
||||
|
||||
$group_items[] = [
|
||||
'type' => 'textbox',
|
||||
'name' => 'image_autooptimize_items_number_per_interation',
|
||||
'title' => __( 'Images per iteration', 'robin-image-optimizer' ),
|
||||
'layout' => [
|
||||
'hint-type' => 'icon',
|
||||
'hint-icon-color' => 'grey',
|
||||
],
|
||||
'hint' => __( 'Specify the number of images that will be optimized during the job. For example, if you enter 5 and select 5 min, the plugin will optimize 5 images every 5 minutes.', 'robin-image-optimizer' ),
|
||||
'default' => '3',
|
||||
'htmlAttrs' => [
|
||||
'type' => 'number',
|
||||
'min' => '1',
|
||||
'step' => '1',
|
||||
],
|
||||
'filter_value' => function ( $value ) {
|
||||
$int_value = intval( $value );
|
||||
return $int_value < 1 ? 1 : $int_value;
|
||||
},
|
||||
];
|
||||
|
||||
$options[] = [
|
||||
'type' => 'div',
|
||||
'id' => 'wbcr-io-shedule-options',
|
||||
'items' => $group_items,
|
||||
];
|
||||
|
||||
$options = apply_filters( 'wbcr/rio/settings_page/options', $options );
|
||||
|
||||
$formOptions = [];
|
||||
|
||||
$formOptions[] = [
|
||||
'type' => 'form-group',
|
||||
'items' => $options,
|
||||
// 'cssClass' => 'postbox'
|
||||
];
|
||||
|
||||
return $formOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save advanced options in database
|
||||
*
|
||||
* @since 1.3.6
|
||||
*/
|
||||
public function beforeFormSave() {
|
||||
|
||||
/**
|
||||
* Used to save webp options. It can also be used to intercept
|
||||
* other unregistered fields.
|
||||
*
|
||||
* @since 1.3.6
|
||||
*/
|
||||
do_action( 'wrio/settings_page/berfore_form_save' );
|
||||
|
||||
$error_log = (int) WRIO_Plugin::app()->request->post( WRIO_Plugin::app()->getPrefix() . 'error_log', 0 );
|
||||
|
||||
if ( ! $error_log ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$keep_error_log_on_frontend = (int) WRIO_Plugin::app()->request->post( 'wrio_keep_error_log_on_frontend', 0 );
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'keep_error_log_on_frontend', $keep_error_log_on_frontend );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds advanced options for the "Convert Images to WebP" checkbox.
|
||||
*
|
||||
* @since 1.3.6
|
||||
*/
|
||||
public function conver_webp_options() {
|
||||
|
||||
/**
|
||||
* This hook prints options for delivering webp images.
|
||||
*
|
||||
* @since 1.3.6
|
||||
*/
|
||||
do_action( 'wrio/settings_page/conver_webp_options' );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds advanced options for the "Error log" checkbox.
|
||||
*
|
||||
* @since 1.3.6
|
||||
*/
|
||||
public function error_log_options() {
|
||||
$this->view->print_template(
|
||||
'part-settings-page-error-log-options',
|
||||
[
|
||||
'keep_error_log_on_frontend' => (int) WRIO_Plugin::app()->getPopulateOption( 'keep_error_log_on_frontend', 0 ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Кнопка восстановления изображений
|
||||
*/
|
||||
public function rollbackButton() {
|
||||
?>
|
||||
<div class="form-group form-group-checkbox factory-control-rollback-button">
|
||||
<label for="wio-clear-backup-btn" class="col-sm-4 control-label">
|
||||
<?php esc_html_e( 'Manage backups', 'robin-image-optimizer' ); ?>
|
||||
<span class="factory-hint-icon factory-hint-icon-red" data-toggle="factory-tooltip"
|
||||
data-placement="right" title=""
|
||||
data-original-title="<?php esc_html_e( 'You can restore the original images from a backup or clear them.', 'robin-image-optimizer' ); ?>"
|
||||
>
|
||||
?
|
||||
</span>
|
||||
</label>
|
||||
<input type="hidden" value="<?php echo wp_create_nonce( 'wio-iph' ); ?>" id="wio-iph-nonce">
|
||||
<div class="control-group col-sm-8">
|
||||
<div class="factory-buttons-way btn-group">
|
||||
<a class="btn btn-default" id="wio-restore-backup-btn"
|
||||
data-confirm="<?php esc_html_e( 'Are you sure you want to restore all images? This cannot be undone.', 'robin-image-optimizer' ); ?>"
|
||||
href="#"><?php esc_html_e( 'Restore', 'robin-image-optimizer' ); ?></a>
|
||||
<a class="btn btn-default" id="wio-clear-backup-btn"
|
||||
data-confirm="<?php esc_html_e( 'Are you sure you want to clear the backup folder? All backup images will be permanently deleted.', 'robin-image-optimizer' ); ?>"
|
||||
href="#"><?php esc_html_e( 'Clear Backup', 'robin-image-optimizer' ); ?></a>
|
||||
</div>
|
||||
<div class="progress" id="wio-restore-backup-progress" style="display:none;">
|
||||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:0%">
|
||||
</div>
|
||||
</div>
|
||||
<p id="wio-restore-backup-msg"
|
||||
style="display:none;"><?php esc_html_e( 'Restore completed.', 'robin-image-optimizer' ); ?></p>
|
||||
<p id="wio-clear-backup-msg"
|
||||
style="display:none;"><?php esc_html_e( 'The backup folder was cleared.', 'robin-image-optimizer' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_StatisticPage
|
||||
* Класс отвечает за работу страницы статистики
|
||||
*/
|
||||
class WRIO_StatisticPage extends WRIO_Page {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $id = 'rio_general';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $type = 'page';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $plugin;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $page_menu_position = 20;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public $page_menu_dashicon = 'dashicons-chart-line';
|
||||
|
||||
/**
|
||||
* Menu icon for WordPress admin sidebar.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $menu_icon = 'dashicons-images-alt';
|
||||
|
||||
/**
|
||||
* Position in WordPress admin menu (58 = between Comments and Appearance).
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $menu_position = '58';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $menu_target = null;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
public $page_parent_page = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $internal = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $add_link_to_plugin_actions = true;
|
||||
|
||||
/**
|
||||
* Page type
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $scope = 'media-library';
|
||||
|
||||
|
||||
/**
|
||||
* @param WRIO_Plugin $plugin
|
||||
*/
|
||||
public function __construct( WRIO_Plugin $plugin ) {
|
||||
$this->menu_title = __( 'Robin Image Optimizer', 'robin-image-optimizer' );
|
||||
$this->menu_sub_title = __( 'Optimize', 'robin-image-optimizer' );
|
||||
$this->page_menu_short_description = __( 'Compress bulk of images', 'robin-image-optimizer' );
|
||||
$this->plugin = $plugin;
|
||||
|
||||
parent::__construct( $plugin );
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'print_i18n' ] );
|
||||
|
||||
add_filter( 'wbcr/factory/pages/impressive/print_all_notices', [ $this, 'register_limit_notice' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $plugin
|
||||
* @param $obj
|
||||
*
|
||||
* @return void|bool
|
||||
*/
|
||||
public function register_limit_notice( $plugin, $obj ) {
|
||||
if ( ( $this->plugin->getPluginName() !== $plugin->getPluginName() ) || ( 'rio_general' !== $obj->id ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подменяем простраинство имен для меню плагина, если активирован плагин
|
||||
* Меню текущего плагина будет добавлено в общее мен
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMenuScope() {
|
||||
if ( $this->clearfy_collaboration ) {
|
||||
// $this->internal = true;
|
||||
|
||||
return 'wbcr_clearfy';
|
||||
}
|
||||
|
||||
return 'robin-image-optimizer';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMenuTitle() {
|
||||
return $this->menu_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getPageTitle() {
|
||||
return $this->clearfy_collaboration ? __( 'Image optimizer', 'robin-image-optimizer' ) : __( 'Bulk optimization', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function assets( $scripts, $styles ) {
|
||||
parent::assets( $scripts, $styles );
|
||||
|
||||
$this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css' );
|
||||
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/sweetalert2.js' );
|
||||
$this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/sweetalert2.css' );
|
||||
$this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css' );
|
||||
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/Chart.min.js' );
|
||||
// $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/statistic.js' );
|
||||
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/modals.js', [ 'jquery' ], 'wrio-modals' );
|
||||
$this->scripts->add(
|
||||
WRIO_PLUGIN_URL . '/admin/assets/js/bulk-optimization.js',
|
||||
[
|
||||
'jquery',
|
||||
'wrio-modals',
|
||||
]
|
||||
);
|
||||
|
||||
$this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/calculate-attachments.js' );
|
||||
|
||||
$this->scripts->add(
|
||||
WRIO_PLUGIN_URL . '/admin/assets/js/bulk-conversion.js',
|
||||
[
|
||||
'jquery',
|
||||
'wrio-modals',
|
||||
]
|
||||
);
|
||||
|
||||
if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
|
||||
$this->styles->add( WCL_PLUGIN_URL . '/admin/assets/css/general.css' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print localization only current page
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public function print_i18n() {
|
||||
$page = $this->plugin->request->get( 'page', null );
|
||||
|
||||
if ( $page !== $this->getResultId() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$backup = new WIO_Backup();
|
||||
|
||||
wp_enqueue_script( 'wio-statistic-page', WRIO_PLUGIN_URL . '/admin/assets/js/statistic.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion(), true );
|
||||
wp_localize_script( 'wio-statistic-page', 'wrio_l18n_bulk_page', $this->get_i18n() );
|
||||
|
||||
wp_localize_script(
|
||||
'wio-statistic-page',
|
||||
'wrio_settings_bulk_page',
|
||||
[
|
||||
'is_premium' => wrio_is_license_activate(),
|
||||
'is_network_admin' => WRIO_Plugin::app()->isNetworkAdmin() ? 1 : 0,
|
||||
'is_writable_backup_dir' => $backup->isBackupWritable() ? 1 : 0,
|
||||
'images_backup' => WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false ) ? 1 : 0,
|
||||
'need_migration' => wbcr_rio_has_meta_to_migrate() ? 1 : 0,
|
||||
'scope' => $this->scope,
|
||||
'optimization_nonce' => wp_create_nonce( 'bulk_optimization' ),
|
||||
'conversion_nonce' => wp_create_nonce( 'bulk_conversion' ),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function showPageContent() {
|
||||
$is_premium = wrio_is_license_activate();
|
||||
$statistics = $this->get_statisctic_data();
|
||||
|
||||
$template_data = [
|
||||
'is_premium' => $is_premium,
|
||||
'scope' => $this->scope,
|
||||
];
|
||||
|
||||
// do_action( 'wbcr/rio/multisite_current_blog' );
|
||||
|
||||
// Page header
|
||||
$this->view->print_template(
|
||||
'part-page-header',
|
||||
[
|
||||
'title' => __( 'Image optimization dashboard', 'robin-image-optimizer' ),
|
||||
'description' => __( 'Monitor image optimization statistics and run on demand or scheduled optimization.', 'robin-image-optimizer' ),
|
||||
],
|
||||
$this
|
||||
);
|
||||
|
||||
// Page tabs
|
||||
$this->view->print_template( 'part-bulk-optimization-tabs', $template_data, $this );
|
||||
|
||||
?>
|
||||
<div class="wbcr-factory-page-group-body" style="padding:0; border-top: 1px solid #d4d4d4;">
|
||||
<?php
|
||||
// Servers
|
||||
$this->view->print_template( 'part-bulk-optimization-servers', $template_data, $this );
|
||||
|
||||
// Total
|
||||
$this->view->print_template(
|
||||
'part-bulk-optimization-total',
|
||||
array_merge(
|
||||
$template_data,
|
||||
[
|
||||
'stats' => $statistics->get(),
|
||||
]
|
||||
),
|
||||
$this
|
||||
);
|
||||
|
||||
// Statistic
|
||||
$this->view->print_template(
|
||||
'part-bulk-optimization-statistic',
|
||||
array_merge(
|
||||
$template_data,
|
||||
[
|
||||
'stats' => $statistics->get(),
|
||||
]
|
||||
),
|
||||
$this
|
||||
);
|
||||
|
||||
// Optimization log
|
||||
$this->view->print_template(
|
||||
'part-bulk-optimization-log',
|
||||
array_merge(
|
||||
$template_data,
|
||||
[
|
||||
'process_log' => $statistics->get_last_optimized_images(),
|
||||
]
|
||||
),
|
||||
$this
|
||||
);
|
||||
?>
|
||||
</div>
|
||||
<script type="text/html" id="wrio-tmpl-bulk-optimization">
|
||||
<?php $this->view->print_template( 'modal-bulk-optimization' ); ?>
|
||||
</script>
|
||||
<script type="text/html" id="wrio-tmpl-webp-conversion">
|
||||
<?php $this->view->print_template( 'modal-webp-conversion' ); ?>
|
||||
</script>
|
||||
<?php
|
||||
// do_action( 'wbcr/rio/multisite_restore_blog' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return object|\WRIO_Image_Statistic
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected function get_statisctic_data() {
|
||||
return WRIO_Image_Statistic::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @since 1.3.0
|
||||
*/
|
||||
protected function get_i18n() {
|
||||
$modal_optimization_cron_button = __( 'Scheduled optimization', 'robin-image-optimizer' );
|
||||
$modal_conversion_cron_button = __( 'Scheduled conversion', 'robin-image-optimizer' );
|
||||
$modal_optimization_cron_button_stop = __( 'Stop schedule optimization', 'robin-image-optimizer' );
|
||||
$modal_conversion_cron_button_stop = __( 'Stop schedule conversion', 'robin-image-optimizer' );
|
||||
|
||||
$optimize_type = WRIO_Plugin::app()->getOption( 'image_optimization_type', 'schedule' );
|
||||
if ( wrio_is_license_activate() && $optimize_type === 'background' ) {
|
||||
$modal_optimization_cron_button = __( 'Background optimization', 'robin-image-optimizer' );
|
||||
$modal_conversion_cron_button = __( 'Background conversion', 'robin-image-optimizer' );
|
||||
$modal_optimization_cron_button_stop = __( 'Stop background optimization', 'robin-image-optimizer' );
|
||||
$modal_conversion_cron_button_stop = __( 'Stop background conversion', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
return [
|
||||
'server_down_warning' => __( 'Your selected optimization server is down. This means that you cannot optimize images through this server. Try selecting another optimization server.', 'robin-image-optimizer' ),
|
||||
// translators: the status of server.
|
||||
'server_status_down' => __( 'Down', 'robin-image-optimizer' ),
|
||||
// translators: the status of server.
|
||||
'server_status_stable' => __( 'Stable', 'robin-image-optimizer' ),
|
||||
'modal_error' => __( 'Error', 'robin-image-optimizer' ),
|
||||
'modal_cancel' => __( 'Cancel', 'robin-image-optimizer' ),
|
||||
'modal_confirm' => __( 'Confirm', 'robin-image-optimizer' ),
|
||||
|
||||
'modal_optimization_title' => __( 'Select optimization way', 'robin-image-optimizer' ),
|
||||
'modal_optimization_manual_button' => __( 'Optimize now', 'robin-image-optimizer' ),
|
||||
'modal_optimization_cron_button' => $modal_optimization_cron_button,
|
||||
'modal_optimization_cron_button_stop' => $modal_optimization_cron_button_stop,
|
||||
'optimization_complete' => __( 'All images from the media library are optimized.', 'robin-image-optimizer' ),
|
||||
// translators: %s is the number of remaining images
|
||||
'optimization_inprogress' => sprintf( __( 'Optimization in progress. %s images remaining.', 'robin-image-optimizer' ), '<span id="wio-total-unoptimized">%s</span>' ),
|
||||
'modal_conversion_title' => __( 'Select conversion way', 'robin-image-optimizer' ),
|
||||
'modal_conversion_manual_button' => __( 'Convert now', 'robin-image-optimizer' ),
|
||||
'modal_conversion_cron_button' => $modal_conversion_cron_button,
|
||||
'modal_conversion_cron_button_stop' => $modal_conversion_cron_button_stop,
|
||||
'conversion_complete' => __( 'All images from the media library are optimized.', 'robin-image-optimizer' ),
|
||||
// translators: %s is the number of remaining images
|
||||
'conversion_inprogress' => sprintf( __( 'Conversion in progress. %s images remaining.', 'robin-image-optimizer' ), '<span id="wio-total-unoptimized">%s</span>' ),
|
||||
'webp_button_start' => __( 'Convert to WebP', 'robin-image-optimizer' ),
|
||||
'avif_button_start' => __( 'Convert to AVIF', 'robin-image-optimizer' ),
|
||||
'modal_avif_conversion_title' => __( 'Select AVIF conversion way', 'robin-image-optimizer' ),
|
||||
|
||||
'need_migrations' => __( 'To start optimizing, you must complete migration from old plugin version.', 'robin-image-optimizer' ),
|
||||
'leave_page_warning' => __( 'Optimization is still in progress. Are you sure you want to leave?', 'robin-image-optimizer' ),
|
||||
'process_without_backup' => __( 'Do you want to start optimization without backup?', 'robin-image-optimizer' ),
|
||||
'button_resume' => __( 'Resume', 'robin-image-optimizer' ),
|
||||
'button_completed' => __( 'Completed', 'robin-image-optimizer' ),
|
||||
'button_start' => __( 'Optimize', 'robin-image-optimizer' ),
|
||||
'button_stop' => __( 'Stop', 'robin-image-optimizer' ),
|
||||
// Don't Need a Parachute?
|
||||
// If you keep this option deactivated, you won't be able to re-optimize your images to another compression level and restore your original images in case of need.
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,11 @@
|
||||
/*if ('loading' in HTMLImageElement.prototype && wbcr_robin.wpCompatibleLazy === 'yes') {
|
||||
//loading="lazy" in WP >= 5.5
|
||||
} else {
|
||||
}*/
|
||||
|
||||
const el = document.querySelectorAll('img');
|
||||
const observer = lozad(el, {
|
||||
loaded: function (el) {
|
||||
},
|
||||
});
|
||||
observer.observe();
|
||||
10
wp-content/plugins/robin-image-optimizer/assets/js/lozad.min.js
vendored
Normal file
10
wp-content/plugins/robin-image-optimizer/assets/js/lozad.min.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/*! lozad.js - v1.16.0 - 2020-09-06
|
||||
* https://github.com/ApoorvSaxena/lozad.js
|
||||
* Copyright (c) 2020 Apoorv Saxena; Licensed MIT */
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.lozad=e()}(this,function(){"use strict";
|
||||
/**
|
||||
* Detect IE browser
|
||||
* @const {boolean}
|
||||
* @private
|
||||
*/var g="undefined"!=typeof document&&document.documentMode,f={rootMargin:"0px",threshold:0,load:function(t){if("picture"===t.nodeName.toLowerCase()){var e=t.querySelector("img"),r=!1;null===e&&(e=document.createElement("img"),r=!0),g&&t.getAttribute("data-iesrc")&&(e.src=t.getAttribute("data-iesrc")),t.getAttribute("data-alt")&&(e.alt=t.getAttribute("data-alt")),r&&t.append(e)}if("video"===t.nodeName.toLowerCase()&&!t.getAttribute("data-src")&&t.children){for(var a=t.children,o=void 0,i=0;i<=a.length-1;i++)(o=a[i].getAttribute("data-src"))&&(a[i].src=o);t.load()}t.getAttribute("data-poster")&&(t.poster=t.getAttribute("data-poster")),t.getAttribute("data-src")&&(t.src=t.getAttribute("data-src")),t.getAttribute("data-srcset")&&t.setAttribute("srcset",t.getAttribute("data-srcset"));var n=",";if(t.getAttribute("data-background-delimiter")&&(n=t.getAttribute("data-background-delimiter")),t.getAttribute("data-background-image"))t.style.backgroundImage="url('"+t.getAttribute("data-background-image").split(n).join("'),url('")+"')";else if(t.getAttribute("data-background-image-set")){var d=t.getAttribute("data-background-image-set").split(n),u=d[0].substr(0,d[0].indexOf(" "))||d[0];// Substring before ... 1x
|
||||
u=-1===u.indexOf("url(")?"url("+u+")":u,1===d.length?t.style.backgroundImage=u:t.setAttribute("style",(t.getAttribute("style")||"")+"background-image: "+u+"; background-image: -webkit-image-set("+d+"); background-image: image-set("+d+")")}t.getAttribute("data-toggle-class")&&t.classList.toggle(t.getAttribute("data-toggle-class"))},loaded:function(){}};function A(t){t.setAttribute("data-loaded",!0)}var m=function(t){return"true"===t.getAttribute("data-loaded")},v=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:document;return t instanceof Element?[t]:t instanceof NodeList?t:e.querySelectorAll(t)};return function(){var r,a,o=0<arguments.length&&void 0!==arguments[0]?arguments[0]:".lozad",t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},e=Object.assign({},f,t),i=e.root,n=e.rootMargin,d=e.threshold,u=e.load,g=e.loaded,s=void 0;"undefined"!=typeof window&&window.IntersectionObserver&&(s=new IntersectionObserver((r=u,a=g,function(t,e){t.forEach(function(t){(0<t.intersectionRatio||t.isIntersecting)&&(e.unobserve(t.target),m(t.target)||(r(t.target),A(t.target),a(t.target)))})}),{root:i,rootMargin:n,threshold:d}));for(var c,l=v(o,i),b=0;b<l.length;b++)(c=l[b]).getAttribute("data-placeholder-background")&&(c.style.background=c.getAttribute("data-placeholder-background"));return{observe:function(){for(var t=v(o,i),e=0;e<t.length;e++)m(t[e])||(s?s.observe(t[e]):(u(t[e]),A(t[e]),g(t[e])))},triggerLoad:function(t){m(t)||(u(t),A(t),g(t))},observer:s}}});
|
||||
34
wp-content/plugins/robin-image-optimizer/composer.json
Normal file
34
wp-content/plugins/robin-image-optimizer/composer.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "codeinwp/robin-image-optimizer",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"vendor/codeinwp/themeisle-sdk/load.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "phpcs",
|
||||
"format": "phpcbf",
|
||||
"lint:generate:baseline": "phpcs-baseline",
|
||||
"phpstan": "phpstan analyse -c phpstan.neon --memory-limit=2G",
|
||||
"phpstan:generate:baseline": "phpstan analyse -c phpstan.neon --memory-limit=2G --generate-baseline",
|
||||
"phpunit": "phpunit"
|
||||
},
|
||||
"require": {
|
||||
"codeinwp/themeisle-sdk": "^3.3"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"digitalrevolution/php-codesniffer-baseline": true
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"wp-coding-standards/wpcs": "^3.0",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"szepeviktor/phpstan-wordpress": "^2.0",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"digitalrevolution/php-codesniffer-baseline": "^1.3",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"yoast/phpunit-polyfills": "^4.0"
|
||||
}
|
||||
}
|
||||
2619
wp-content/plugins/robin-image-optimizer/composer.lock
generated
Normal file
2619
wp-content/plugins/robin-image-optimizer/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,532 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Основной класс плагина
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Plugin extends Wbcr_Factory600_Plugin {
|
||||
|
||||
/**
|
||||
* @see self::app()
|
||||
* @var Wbcr_Factory600_Plugin
|
||||
*/
|
||||
private static $app;
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
* @var array
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* Independent premium provider (replaces Freemius)
|
||||
*
|
||||
* @var WRIO_Premium_Provider|null
|
||||
* @phpstan-ignore-next-line property.phpDocType
|
||||
*/
|
||||
public $premium;
|
||||
|
||||
/**
|
||||
* Independent support class
|
||||
*
|
||||
* @var WRIO_Support|null
|
||||
*/
|
||||
private $wrio_support;
|
||||
|
||||
/**
|
||||
* Конструктор
|
||||
*
|
||||
* Применяет конструктор родительского класса и записывает экземпляр текущего класса в свойство $app.
|
||||
* Подробнее о свойстве $app см. self::app()
|
||||
*
|
||||
* @param string $plugin_path
|
||||
* @param array $data
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct( $plugin_path, $data ) {
|
||||
parent::__construct( $plugin_path, $data );
|
||||
|
||||
self::$app = $this;
|
||||
$this->plugin_data = $data;
|
||||
|
||||
// Initialize independent premium provider (overrides Factory premium)
|
||||
$this->init_independent_premium();
|
||||
|
||||
// Initialize independent support class
|
||||
$this->init_independent_support();
|
||||
|
||||
$this->includes();
|
||||
|
||||
if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
|
||||
// Processing
|
||||
if ( wrio_is_license_activate() ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-processing.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-media-processing.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-folder-processing.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-nextgen-processing.php';
|
||||
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-media-processing-webp.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processing/class-rio-media-processing-avif.php';
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
$this->initActivation();
|
||||
|
||||
// completely disable image size threshold
|
||||
add_filter( 'big_image_size_threshold', '__return_false' );
|
||||
|
||||
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
||||
// Ajax files
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/ajax/backup.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-bulk-optimization.php';
|
||||
new WRIO_Bulk_Optimization();
|
||||
|
||||
// require_once( WRIO_PLUGIN_DIR . '/admin/ajax/logs.php' );
|
||||
|
||||
// Not under AJAX logical operator above on purpose to have helpers available to find out whether
|
||||
// metas were migrated or not
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/ajax/meta-migrations.php';
|
||||
}
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
function () {
|
||||
$this->registerPages();
|
||||
if ( WRIO_Plugin::app()->premium->is_active() ) {
|
||||
update_option( 'robin_image_optimizer_logger_flag', 'yes' );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', [ $this, 'pluginsLoaded' ] );
|
||||
|
||||
$sdk_namespace = self::get_sdk_namespace();
|
||||
add_filter(
|
||||
'themesle_sdk_namespace_' . md5( WRIO_PLUGIN_FILE ),
|
||||
function () use ( $sdk_namespace ) {
|
||||
return $sdk_namespace;
|
||||
}
|
||||
);
|
||||
|
||||
add_filter( 'themeisle_sdk_products', [ __CLASS__, 'register_sdk' ] );
|
||||
add_filter( 'themeisle_sdk_ran_promos', [ __CLASS__, 'sdk_hide_promo_notice' ] );
|
||||
|
||||
// We hide the license notice as it is not required for this plugin.
|
||||
add_filter( $sdk_namespace . '_hide_license_notices', '__return_true' );
|
||||
add_filter( $sdk_namespace . '_hide_license_field', '__return_true' );
|
||||
add_filter( $sdk_namespace . '_about_us_metadata', [ __CLASS__, 'register_about_page' ] );
|
||||
|
||||
add_filter( 'themeisle-sdk/survey/' . WRIO_PLUGIN_DIR, [ $this, 'get_survey_metadata' ], 10, 1 );
|
||||
add_filter(
|
||||
$sdk_namespace . '_dissallowed_promotions',
|
||||
function ( $promotions ) {
|
||||
if ( ! is_array( $promotions ) ) {
|
||||
$promotions = [];
|
||||
}
|
||||
$promotions[] = 'optimole';
|
||||
return $promotions;
|
||||
}
|
||||
);
|
||||
add_action( 'admin_print_styles', [ $this, 'truncate_menu_items' ], 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get namespace for SDK.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_sdk_namespace() {
|
||||
$namespace = basename( WRIO_PLUGIN_DIR );
|
||||
$namespace = str_replace( '-', '_', strtolower( trim( $namespace ) ) );
|
||||
return $namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register survey data.
|
||||
*
|
||||
* @param array<string, mixed> $data The data in Formbricks format.
|
||||
*
|
||||
* @return array<string, mixed> The data in Formbricks format.
|
||||
* @see survey.js in SDK.
|
||||
*/
|
||||
public function get_survey_metadata( $data ) {
|
||||
$install_days_number = intval( ( time() - get_option( self::get_sdk_namespace() . '_install', time() ) ) / DAY_IN_SECONDS );
|
||||
|
||||
$license_status = 'invalid';
|
||||
$plan = null;
|
||||
$license_key = null;
|
||||
|
||||
/**
|
||||
* The premium provider.
|
||||
*
|
||||
* @var \WBCR\Factory_600\Premium\Provider|null
|
||||
*/
|
||||
$license_manager = self::app()->premium;
|
||||
if ( $license_manager ) {
|
||||
$license_status = $license_manager->is_active() ? 'valid' : 'invalid';
|
||||
|
||||
/**
|
||||
* The license data.
|
||||
*
|
||||
* @var WBCR\Factory_Freemius_Rio_600\Entities\License|null
|
||||
*/
|
||||
$license_data = $license_manager->get_license();
|
||||
|
||||
if ( $license_data ) {
|
||||
$plan = $license_data->get_plan_id();
|
||||
$license_key = $license_data->get_hidden_key();
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'environmentId' => 'cmioooac34ugdad0179e060te',
|
||||
'attributes' => [
|
||||
'install_days_number' => $install_days_number,
|
||||
'free_version' => WRIO_PLUGIN_VERSION,
|
||||
'license_status' => $license_status,
|
||||
],
|
||||
];
|
||||
|
||||
if ( $plan ) {
|
||||
$data['attributes']['plan'] = $plan;
|
||||
}
|
||||
|
||||
if ( $license_key ) {
|
||||
$data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
|
||||
}
|
||||
|
||||
$stats = WRIO_Image_Statistic::get_instance()->get();
|
||||
|
||||
if ( ! empty( $stats['optimized'] ) ) {
|
||||
$data['attributes']['optimized_images_count'] = $stats['optimized'];
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['unoptimized'] ) ) {
|
||||
$data['attributes']['unoptimized_images_count'] = $stats['unoptimized'];
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['optimized_percent'] ) ) {
|
||||
$data['attributes']['optimized_images_percent'] = round( floatval( $stats['optimized_percent'] ), 2 );
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['converted'] ) ) {
|
||||
$data['attributes']['converted_images_count'] = $stats['converted'];
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['unconverted'] ) ) {
|
||||
$data['attributes']['unconverted_images_count'] = $stats['unconverted'];
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['save_size_percent'] ) ) {
|
||||
$data['attributes']['saved_size_percent'] = round( floatval( $stats['save_size_percent'] ), 2 );
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['webp_optimized_size'] ) ) {
|
||||
$data['attributes']['webp_optimized_total_size_bytes'] = intval( $stats['webp_optimized_size'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['webp_percent_line'] ) ) {
|
||||
$data['attributes']['webp_optimized_size_percent'] = round( floatval( $stats['webp_percent_line'] ), 2 );
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['original_size'] ) ) {
|
||||
$data['attributes']['original_total_size_bytes'] = intval( $stats['original_size'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['original'] ) ) {
|
||||
$data['attributes']['original_images_count'] = $stats['original'];
|
||||
}
|
||||
|
||||
if ( ! empty( $stats['error'] ) ) {
|
||||
$data['attributes']['error_images_count'] = $stats['error'];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mark internal page of the plugin.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function mark_internal_page() {
|
||||
$current_screen = get_current_screen();
|
||||
|
||||
if ( ! $current_screen ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$page_id = $current_screen->id;
|
||||
$page_slug = null;
|
||||
|
||||
if ( 'toplevel_page_rio_general-robin-image-optimizer' === $page_id ) {
|
||||
$page_slug = 'bulk-optimization';
|
||||
} elseif ( 'toplevel_page_io_folders_statistic-robin-image-optimizer' === $page_id ) {
|
||||
$page_slug = 'custom-folders';
|
||||
} elseif ( 'robin-image-optimizer_page_rio_settings-robin-image-optimizer' === $page_id ) {
|
||||
$page_slug = 'settings';
|
||||
} elseif ( 'robin-image-optimizer_page_wbcr_io_logger-robin-image-optimizer' === $page_id ) {
|
||||
$page_slug = 'error-log';
|
||||
} elseif ( 'robin-image-optimizer_page_wrio_license' === $page_id ) {
|
||||
$page_slug = 'license';
|
||||
} elseif ( 'toplevel_page_io_nextgen_gallery_statistic-robin-image-optimizer' === $page_id ) {
|
||||
$page_slug = 'nextgen-gallery';
|
||||
}
|
||||
|
||||
if ( null === $page_slug ) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_action( 'themeisle_internal_page', WRIO_PLUGIN_DIR, $page_slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide SDK promo notice for pro uses.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public static function sdk_hide_promo_notice( $should_show ) {
|
||||
return self::app()->premium->is_active();
|
||||
}
|
||||
/**
|
||||
* Register product into SDK.
|
||||
*
|
||||
* @param array $products All products.
|
||||
*
|
||||
* @return array Registered product.
|
||||
*/
|
||||
public static function register_sdk( $products ) {
|
||||
$products[] = WRIO_PLUGIN_FILE;
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register About Us page metadata for ThemeIsle SDK.
|
||||
*
|
||||
* @param array<string, mixed> $data About page data.
|
||||
*
|
||||
* @return array<string, mixed> About page configuration.
|
||||
*/
|
||||
public static function register_about_page( $data ) {
|
||||
return [
|
||||
'location' => 'rio_general-robin-image-optimizer',
|
||||
'logo' => WRIO_PLUGIN_URL . '/admin/assets/img/icon-256x256.gif',
|
||||
'review_link' => false,
|
||||
'has_upgrade_menu' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Статический метод для быстрого доступа к интерфейсу плагина.
|
||||
*
|
||||
* Позволяет разработчику глобально получить доступ к экземпляру класса плагина в любом месте
|
||||
* плагина, но при этом разработчик не может вносить изменения в основной класс плагина.
|
||||
*
|
||||
* Используется для получения настроек плагина, информации о плагине, для доступа к вспомогательным
|
||||
* классам.
|
||||
*
|
||||
* @return \Wbcr_Factory600_Plugin|\WRIO_Plugin
|
||||
*/
|
||||
public static function app() {
|
||||
return self::$app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Подключаем функции бекенда
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function pluginsLoaded() {
|
||||
if ( is_admin() || wrio_doing_cron() || wrio_doing_rest_api() ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->initHooks();
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/boot.php';
|
||||
// require_once( WRIO_PLUGIN_DIR . '/admin/includes/classes/class-rio-nextgen-landing.php' );
|
||||
|
||||
// Parent page class
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/pages/class-rio-page.php';
|
||||
// $this->registerPages();
|
||||
}
|
||||
|
||||
if ( wrio_doing_cron() || wrio_doing_rest_api() ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->initHooks();
|
||||
}
|
||||
|
||||
// Load premium addon for all users (WebP conversion is available for free users)
|
||||
require_once WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php';
|
||||
wrio_premium_load();
|
||||
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'mark_internal_page' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Подключаем модули классы и функции
|
||||
*/
|
||||
protected function includes() {
|
||||
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/functions.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-views.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-attachment.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-media-library.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-abstract.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-image-statistic.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-image-query.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-optimization-orchestrator.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-backup.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-optimization-tools.php';
|
||||
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-helper.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-object.php'; // Base object
|
||||
|
||||
// Database related models
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-active-record.php';
|
||||
// Base class
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-extra-data.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-attachment-extra-data.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class.webp-extra-data.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-server-smushit-extra-data.php';
|
||||
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-process-queue-table.php'; // Processing queue model
|
||||
|
||||
// Cron
|
||||
// ----------------
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-rio-cron.php';
|
||||
new WRIO_Cron();
|
||||
|
||||
// Register cache invalidation hooks for WRIO_Image_Query
|
||||
WRIO_Image_Query::register_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Инициализируем активацию плагина
|
||||
*/
|
||||
protected function initActivation() {
|
||||
include_once WRIO_PLUGIN_DIR . '/admin/activation.php';
|
||||
self::app()->registerActivation( 'WIO_Activation' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрируем страницы плагина
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function registerPages() {
|
||||
$admin_path = WRIO_PLUGIN_DIR . '/admin/pages/';
|
||||
|
||||
// Register main menu page first, then submenus
|
||||
self::app()->registerPage( 'WRIO_StatisticPage', $admin_path . '/class-rio-statistic.php' );
|
||||
self::app()->registerPage( 'WRIO_SettingsPage', $admin_path . '/class-rio-settings.php' );
|
||||
|
||||
if ( ! wrio_is_clearfy_license_activate() ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/includes/class-wrio-subscribe-widget.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/pages/class-rio-license.php';
|
||||
new WRIO_License_Page_View();
|
||||
}
|
||||
|
||||
if ( self::app()->getPopulateOption( 'error_log', false ) ) {
|
||||
self::app()->registerPage( 'WRIO_LogPage', $admin_path . '/class-rio-log.php' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Option enables error logging on frontend. If for some reason webp images are not displayed on the front-end, you can use
|
||||
* this option to catch errors and send this report to the plugin support service.
|
||||
*
|
||||
* @return int
|
||||
* @since 1.3.6
|
||||
*/
|
||||
public function is_keep_error_log_on_frontend() {
|
||||
if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int) $this->getPopulateOption( 'keep_error_log_on_frontend', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize independent premium provider
|
||||
*
|
||||
* This overrides the Factory framework's premium property with our
|
||||
* lightweight independent implementation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_independent_premium() {
|
||||
// Load the independent license and premium classes
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-wrio-license.php';
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-wrio-premium-provider.php';
|
||||
|
||||
// Create the independent premium provider
|
||||
$this->premium = new WRIO_Premium_Provider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize independent support class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_independent_support() {
|
||||
require_once WRIO_PLUGIN_DIR . '/includes/classes/class-wrio-support.php';
|
||||
|
||||
$support_config = [
|
||||
'url' => isset( $this->plugin_data['support_details']['url'] ) ? $this->plugin_data['support_details']['url'] : 'https://developer.flavflavor.dev',
|
||||
'plugin_name' => $this->getPluginName(),
|
||||
];
|
||||
|
||||
// Allow pages_map override if provided in plugin data
|
||||
if ( isset( $this->plugin_data['support_details']['pages_map'] ) ) {
|
||||
$support_config['pages_map'] = $this->plugin_data['support_details']['pages_map'];
|
||||
}
|
||||
|
||||
$this->wrio_support = new WRIO_Support( $support_config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get independent support instance
|
||||
*
|
||||
* This method provides backward compatibility with existing code
|
||||
* that calls $plugin->get_support().
|
||||
*
|
||||
* @return WRIO_Support|null
|
||||
* @phpstan-ignore-next-line return.type
|
||||
*/
|
||||
public function get_support() {
|
||||
if ( ! isset( $this->wrio_support ) ) {
|
||||
$this->init_independent_support();
|
||||
}
|
||||
|
||||
return $this->wrio_support;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the menu item name so it doesn't break the layout of the WP Admin sidebar.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function truncate_menu_items() {
|
||||
echo '<style>
|
||||
#toplevel_page_rio_general-robin-image-optimizer div.wp-menu-name {
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 130px;
|
||||
padding-left: 0 !important;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>';
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы с резервным копированием изображений.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WIO_Backup {
|
||||
|
||||
const BACKUP_DIR_NAME = 'wio_backup';
|
||||
const TEMP_DIR_NAME = 'temp';
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @access protected
|
||||
* @var object
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* @var array Данные о папке uploads, возвращаемые функцией wp_upload_dir()
|
||||
*/
|
||||
protected $wp_upload_dir;
|
||||
|
||||
/**
|
||||
* @var string Путь к папке с резервными копиями изображений
|
||||
*/
|
||||
private $backup_dir;
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
* @var string
|
||||
*/
|
||||
private $blog_backup_dir;
|
||||
|
||||
/**
|
||||
* Инициализация бекапа
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->wp_upload_dir = wp_upload_dir();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @return object|\static object Main instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( static::$_instance ) ) {
|
||||
static::$_instance = new static();
|
||||
}
|
||||
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка возможности записи в папку uploads.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUploadWritable() {
|
||||
$upload_dir = $this->wp_upload_dir['basedir'];
|
||||
|
||||
if ( is_dir( $upload_dir ) && wp_is_writable( $upload_dir ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка возможности записи в папку бекап.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBackupWritable() {
|
||||
|
||||
$backup_dir = $this->getBackupDir();
|
||||
|
||||
if ( is_wp_error( $backup_dir ) || ! wp_is_writable( $backup_dir ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Путь к папке с бекапами
|
||||
*
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
public function getBackupDir() {
|
||||
if ( $this->backup_dir ) {
|
||||
return $this->backup_dir;
|
||||
}
|
||||
|
||||
$backup_dir = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
|
||||
|
||||
if ( ! is_dir( $backup_dir ) ) {
|
||||
$backup_dir = $this->mkdir( $backup_dir );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
}
|
||||
|
||||
$this->backup_dir = apply_filters( 'wbcr/rio/backup/backup_dir', trailingslashit( $backup_dir ) );
|
||||
|
||||
return $this->backup_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Путь к папке с бекапами блога.
|
||||
*
|
||||
* Используется в мультисайт режиме.
|
||||
*
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
public function getBlogBackupDir() {
|
||||
if ( $this->blog_backup_dir ) {
|
||||
return $this->blog_backup_dir;
|
||||
}
|
||||
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
$backup_dir = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
|
||||
|
||||
if ( ! is_dir( $backup_dir ) ) {
|
||||
$backup_dir = $this->mkdir( $backup_dir );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
}
|
||||
|
||||
$this->blog_backup_dir = trailingslashit( $backup_dir );
|
||||
|
||||
return $this->blog_backup_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает папку с резервными копиями
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeBackupDir() {
|
||||
$backup_dir = $this->getBackupDir();
|
||||
|
||||
return wrio_rmdir( $backup_dir );
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает папку с резервными копиями блога
|
||||
* Используется в мультисайт режиме
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeBlogBackupDir() {
|
||||
$backup_dir = $this->getBlogBackupDir();
|
||||
|
||||
return wrio_rmdir( $backup_dir );
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает путь к папке с резервными копиями
|
||||
*
|
||||
* @param array $attachment_meta метаданные аттачмента
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAttachmentBackupDir( $attachment_meta ) {
|
||||
$backup_dir = $this->getBackupDir();
|
||||
|
||||
// Get all subfolders in which the image is stored.
|
||||
// This is necessary to create an alternate subfolders
|
||||
// in directory where they are stored in backups.
|
||||
$subfolders = dirname( $attachment_meta['file'] );
|
||||
|
||||
$backup_dir .= $subfolders;
|
||||
|
||||
if ( ! is_dir( $backup_dir ) ) {
|
||||
$backup_dir = $this->mkdir( $backup_dir );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
}
|
||||
|
||||
return trailingslashit( $backup_dir );
|
||||
}
|
||||
|
||||
/**
|
||||
* Делаем резервную копию аттачмента
|
||||
*
|
||||
* @param WIO_Attachment $wio_attachment аттачмент
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function backupAttachment( WIO_Attachment $wio_attachment ) {
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
|
||||
if ( ! $backup_origin_images ) {
|
||||
return false; // если бекап не требуется
|
||||
}
|
||||
|
||||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
|
||||
$full = $this->backupAttachmentSize( $wio_attachment );
|
||||
|
||||
if ( is_wp_error( $full ) ) {
|
||||
return $full;
|
||||
}
|
||||
|
||||
$allowed_sizes = $wio_attachment->getAllowedSizes();
|
||||
|
||||
if ( ! empty( $allowed_sizes ) ) {
|
||||
foreach ( (array) $allowed_sizes as $image_size ) {
|
||||
$size_backup = $this->backupAttachmentSize( $wio_attachment, $image_size );
|
||||
|
||||
if ( is_wp_error( $size_backup ) ) {
|
||||
return $size_backup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстанавливаем аттачмент из резервной копии
|
||||
*
|
||||
* @param WIO_Attachment $wio_attachment аттачмент
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function restoreAttachment( WIO_Attachment $wio_attachment ) {
|
||||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
|
||||
$restore_result = $this->restoreAttachmentSize( $wio_attachment );
|
||||
|
||||
if ( is_wp_error( $restore_result ) ) {
|
||||
return $restore_result;
|
||||
}
|
||||
|
||||
$attachment_meta = wp_get_attachment_metadata( $wio_attachment->get( 'id' ) );
|
||||
|
||||
if ( isset( $attachment_meta['old_width'] ) && isset( $attachment_meta['old_width'] ) ) {
|
||||
$attachment_meta['width'] = $attachment_meta['old_width'];
|
||||
$attachment_meta['height'] = $attachment_meta['old_height'];
|
||||
wp_update_attachment_metadata( $wio_attachment->get( 'id' ), $attachment_meta );
|
||||
}
|
||||
|
||||
$allowed_sizes = $wio_attachment->getAllowedSizes();
|
||||
|
||||
if ( $allowed_sizes ) {
|
||||
foreach ( $allowed_sizes as $image_size ) {
|
||||
$this->restoreAttachmentSize( $wio_attachment, $image_size );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создает временное изображение с уникальным именем.
|
||||
*
|
||||
* Необходимо для провайдеров, который кешируют изображения по имени файла,
|
||||
* чтобы сбросить кеш, нужно отдать провайдеру изображение с другим именем.
|
||||
*
|
||||
* @since 1.1.2
|
||||
*
|
||||
* @param string $file_path путь к изображению
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function createTempAttachment( $file_path ) {
|
||||
if ( $this->isBackupWritable() ) {
|
||||
|
||||
$temp_dir = $this->getBackupDir() . self::TEMP_DIR_NAME . '/';
|
||||
$temp_dir_url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . self::BACKUP_DIR_NAME . '/' . self::TEMP_DIR_NAME . '/';
|
||||
|
||||
if ( ! is_dir( $temp_dir ) ) {
|
||||
$temp_dir = $this->mkdir( $temp_dir );
|
||||
|
||||
if ( is_wp_error( $temp_dir ) ) {
|
||||
return $temp_dir;
|
||||
}
|
||||
}
|
||||
|
||||
$temp_file_id = uniqid();
|
||||
$file_name = pathinfo( $file_path, PATHINFO_FILENAME );
|
||||
$file_extension = pathinfo( $file_path, PATHINFO_EXTENSION );
|
||||
$new_file_name = $temp_file_id . '_' . md5( $file_name ) . '.' . $file_extension;
|
||||
|
||||
$temp_file_path = $temp_dir . $new_file_name;
|
||||
$temp_file_url = $temp_dir_url . $new_file_name;
|
||||
|
||||
if ( is_file( $file_path ) ) {
|
||||
if ( ! @copy( $file_path, $temp_file_path ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file %s with %s as copy() failed.', $temp_file_path, $file_path ) );
|
||||
|
||||
return new WP_Error( 'copy_file_to_temp_dir_error', __( 'Could not copy the file to the temporary directory', 'robin-image-optimizer' ) );
|
||||
}
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Creation of temporary attachment (%s) successfully completed!', $file_path ) );
|
||||
|
||||
return [
|
||||
'id' => $temp_file_id,
|
||||
'image_path' => $temp_file_path,
|
||||
'image_url' => $temp_file_url,
|
||||
];
|
||||
}
|
||||
|
||||
return new WP_Error( 'backup_writable_error', __( 'It is not possible to create a temporary file, the backup folder is not writable.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Резервное копирование файла аттачмента.
|
||||
*
|
||||
* @param WIO_Attachment $wio_attachment аттачмент
|
||||
* @param string $image_size Размер(thumbnail, medium ... )
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function backupAttachmentSize( WIO_Attachment $wio_attachment, $image_size = '' ) {
|
||||
if ( $image_size ) {
|
||||
$original_file = $wio_attachment->getImageSizePath( $image_size );
|
||||
} else {
|
||||
$original_file = $wio_attachment->get( 'path' );
|
||||
}
|
||||
|
||||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||||
|
||||
// проверить запись в папку
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to create backup dir, error: %s', $backup_dir->get_error_message() ) );
|
||||
|
||||
return $backup_dir;
|
||||
}
|
||||
|
||||
if ( ! $original_file ) {
|
||||
// бывает такое, что размера превьюшки нет в базе данных.
|
||||
// это не считается ошибкой, поэтому сразу пропускаем
|
||||
return false;
|
||||
}
|
||||
|
||||
$backup_file = $backup_dir . wp_basename( $original_file );
|
||||
|
||||
if ( is_file( $original_file ) ) {
|
||||
if ( ! @copy( $original_file, $backup_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to copy %s to %s as copy() failed', $original_file, $backup_file ) );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстановление файла аттачмента из резервной копии
|
||||
*
|
||||
* @param WIO_Attachment $wio_attachment аттачмент
|
||||
* @param string|null $image_size Размер(thumbnail, medium ... )
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
protected function restoreAttachmentSize( WIO_Attachment $wio_attachment, $image_size = null ) {
|
||||
|
||||
if ( ! empty( $image_size ) ) {
|
||||
$original_file = $wio_attachment->getImageSizePath( $image_size );
|
||||
} else {
|
||||
$original_file = $wio_attachment->get( 'path' );
|
||||
}
|
||||
|
||||
$backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
|
||||
if ( empty( $original_file ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$backup_file = $backup_dir . wp_basename( $original_file );
|
||||
|
||||
if ( ! is_file( $backup_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to restore from a backup. There is no file, attachment id: %s, backup file: %s', $wio_attachment->get( 'id' ), $backup_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! @copy( $backup_file, $original_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap %s with %s as copy() failed', $backup_file, $original_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! @unlink( $backup_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file %s as unlink() failed', $backup_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup file path for an attachment size if it exists.
|
||||
*
|
||||
* @param int $attachment_id The attachment ID.
|
||||
* @param string $size The image size (e.g., 'original', 'thumbnail', 'medium').
|
||||
*
|
||||
* @return string|null The backup file path if it exists, null otherwise.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getAttachmentBackupPath( $attachment_id, $size = 'original' ) {
|
||||
$attachment_meta = wp_get_attachment_metadata( $attachment_id );
|
||||
|
||||
if ( empty( $attachment_meta ) || ! isset( $attachment_meta['file'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the filename based on size
|
||||
if ( 'original' === $size ) {
|
||||
$filename = wp_basename( $attachment_meta['file'] );
|
||||
} elseif ( isset( $attachment_meta['sizes'][ $size ]['file'] ) ) {
|
||||
$filename = $attachment_meta['sizes'][ $size ]['file'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
$backup_path = $backup_dir . $filename;
|
||||
|
||||
return file_exists( $backup_path ) ? $backup_path : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляем резервные копии аттачмента
|
||||
*
|
||||
* @param int $attachment_id аттачмент id
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function removeAttachmentBackup( $attachment_id ) {
|
||||
$attachment_meta = wp_get_attachment_metadata( $attachment_id );
|
||||
$backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
|
||||
|
||||
if ( is_wp_error( $backup_dir ) ) {
|
||||
return $backup_dir;
|
||||
}
|
||||
|
||||
$main_file_path = $backup_dir . wp_basename( $attachment_meta['file'] );
|
||||
|
||||
if ( ! file_exists( $main_file_path ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove an attachment file. File (%s) isn't exists. Attachment #%s", $main_file_path, $attachment_id ) );
|
||||
}
|
||||
|
||||
if ( ! @unlink( $main_file_path ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink a main file (%s) for attachment #%s', $main_file_path, $attachment_id ) );
|
||||
}
|
||||
|
||||
if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
|
||||
foreach ( $attachment_meta['sizes'] as $size ) {
|
||||
$thumbnail_file_path = $backup_dir . $size['file'];
|
||||
|
||||
if ( ! file_exists( $thumbnail_file_path ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( "Failed to remove a thumbnail file. File (%s) isn't exists. Attachment #%s", $thumbnail_file_path, $attachment_id ) );
|
||||
}
|
||||
|
||||
if ( ! @unlink( $thumbnail_file_path ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to unlink thumbnail (%s) for attachment #%s', $thumbnail_file_path, $attachment_id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* alternateStorage
|
||||
*
|
||||
* @param array $servers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function alternateStorage( $servers ) {
|
||||
|
||||
return $servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param string $dir
|
||||
*
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
protected function mkdir( $dir ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Try to create backup directory. Backup dir: (%s)', $dir ) );
|
||||
|
||||
if ( ! wp_mkdir_p( $dir ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to create backup directory (%s) as mkdir() failed', $dir ) );
|
||||
|
||||
return new WP_Error( 'mkdir_failed', sprintf( 'Unable to create backup folder (%s) as mkdir() failed.', $dir ) );
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param string $backup_file
|
||||
* @param string $original_file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function restore_file( $backup_file, $original_file ) {
|
||||
if ( is_file( $backup_file ) ) {
|
||||
if ( ! @copy( $backup_file, $original_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to swap original file (%s) with %s as copy() failed', $backup_file, $original_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! @unlink( $backup_file ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Failed to delete backup file (%s) as unlink() failed', $backup_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Restored file: %s.', $backup_file ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Unable to restore from a backup. There is no file (%s).', $backup_file ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,994 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_Bulk_Optimization
|
||||
*
|
||||
* Handles bulk optimization operations and processes related to media attachments,
|
||||
* thumbnails, and other optimization tasks within the WordPress environment.
|
||||
*/
|
||||
class WRIO_Bulk_Optimization {
|
||||
|
||||
public $processing;
|
||||
|
||||
public function __construct() {
|
||||
$image_optimization_type = WRIO_Plugin::app()->getOption( 'image_optimization_type', '' );
|
||||
if ( wrio_is_license_activate() && $image_optimization_type === 'background' ) {
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
$this->processing = $scope ? wrio_get_processing_class( $scope ) : $scope;
|
||||
|
||||
add_action( 'wp_ajax_wrio-cron-start', [ $this, 'processing_start' ] );
|
||||
add_action( 'wp_ajax_wrio-cron-stop', [ $this, 'processing_stop' ] );
|
||||
|
||||
add_action( 'wp_ajax_wrio-webp-cron-start', [ $this, 'webp_processing_start' ] );
|
||||
add_action( 'wp_ajax_wrio-webp-cron-stop', [ $this, 'webp_processing_stop' ] );
|
||||
add_action( 'wp_ajax_wrio-avif-cron-start', [ $this, 'avif_processing_start' ] );
|
||||
add_action( 'wp_ajax_wrio-avif-cron-stop', [ $this, 'avif_processing_stop' ] );
|
||||
} else {
|
||||
add_action( 'wp_ajax_wrio-cron-start', [ $this, 'cron_start' ] );
|
||||
add_action( 'wp_ajax_wrio-cron-stop', [ $this, 'cron_stop' ] );
|
||||
|
||||
add_action( 'wp_ajax_wrio-webp-cron-start', [ $this, 'webp_cron_start' ] );
|
||||
add_action( 'wp_ajax_wrio-webp-cron-stop', [ $this, 'webp_cron_stop' ] );
|
||||
add_action( 'wp_ajax_wrio-avif-cron-start', [ $this, 'avif_cron_start' ] );
|
||||
add_action( 'wp_ajax_wrio-avif-cron-stop', [ $this, 'avif_cron_stop' ] );
|
||||
}
|
||||
|
||||
add_action( 'wp_ajax_wrio-bulk-optimization-process', [ $this, 'bulk_optimization_process' ] );
|
||||
add_action( 'wp_ajax_wrio-bulk-conversion-process', [ $this, 'bulk_conversion_process' ] );
|
||||
add_action( 'wp_ajax_wio_reoptimize_image', [ $this, 'reoptimize_image' ] );
|
||||
add_action( 'wp_ajax_wio_convert_image', [ $this, 'convert_image' ] );
|
||||
add_action( 'wp_ajax_wio_restore_image', [ $this, 'restore_image' ] );
|
||||
|
||||
add_action( 'wp_ajax_wbcr-rio-check-servers-status', [ $this, 'check_servers_status' ] );
|
||||
add_action( 'wp_ajax_wbcr-rio-check-user-balance', [ $this, 'check_user_balance' ] );
|
||||
|
||||
// add_action( 'wp_ajax_wbcr-rio-calculate-total-images', [ $this, 'calculate_total_images' ] );
|
||||
add_action( 'wp_ajax_wbcr-rio-calculate-total-images', [ $this, 'calculate_total_images' ] );
|
||||
add_action( 'wp_ajax_wbcr-rio-calculate-total-attachments', [ $this, 'calculate_total_attachments' ] );
|
||||
add_action( 'wp_ajax_wbcr-rio-calculate-total-thumbs', [ $this, 'calculate_total_thumbs' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of attachments that meet specified criteria.
|
||||
* Retrieves the total count of attachment posts with allowed formats, excluding duplicates
|
||||
* if WPML is active, and sends the result as a JSON response.
|
||||
* Includes nonce verification and checks user permissions.
|
||||
*
|
||||
* @return void Outputs JSON response with the total count of attachments.
|
||||
*/
|
||||
public function calculate_total_attachments() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'wrio_partial_total_count' );
|
||||
|
||||
$total_attachments = WRIO_Image_Query::get_instance()->count_total_attachments();
|
||||
|
||||
wp_send_json_success(
|
||||
[
|
||||
'found_attachments' => (int) $total_attachments,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the total number of attachment thumbnails that match specified criteria.
|
||||
* Processes a batch of attachments, counting the allowed thumbnail sizes, and updates
|
||||
* the total count in the database. Handles AJAX requests, including user permissions
|
||||
* and nonce verification.
|
||||
*
|
||||
* @return void Outputs JSON response with the total count of thumbnails and processing status.
|
||||
*/
|
||||
public function calculate_total_thumbs() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$offset = (int) WRIO_Plugin::app()->request->post( 'offset', 0 );
|
||||
$limit = (int) WRIO_Plugin::app()->request->post( 'limit', 200 );
|
||||
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
$allowed_sizes = explode( ',', WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', '' ) );
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"
|
||||
SELECT posts.ID
|
||||
FROM {$wpdb->posts} as posts
|
||||
WHERE post_type = 'attachment'
|
||||
AND post_status = 'inherit'
|
||||
AND post_mime_type IN ({$allowed_formats_sql})
|
||||
LIMIT %d OFFSET %d
|
||||
",
|
||||
$limit,
|
||||
$offset
|
||||
);
|
||||
|
||||
// Учитываем WPML (исключение дубликатов)
|
||||
if ( defined( 'WPML_PLUGIN_FILE' ) ) {
|
||||
$query = str_replace(
|
||||
'WHERE post_type =',
|
||||
"WHERE NOT EXISTS (
|
||||
SELECT icl.element_id
|
||||
FROM {$wpdb->prefix}icl_translations as icl
|
||||
WHERE icl.element_id = posts.ID
|
||||
AND icl.element_type = 'post_attachment'
|
||||
AND source_language_code IS NOT NULL
|
||||
) AND post_type =",
|
||||
$query
|
||||
);
|
||||
}
|
||||
|
||||
$attachments = $wpdb->get_results( $query );
|
||||
$total_count = (int) WRIO_Plugin::app()->getPopulateOption( 'wrio_partial_total_count', 0 );
|
||||
|
||||
$current_batch_count = 0;
|
||||
|
||||
foreach ( $attachments as $attachment ) {
|
||||
$meta = wp_get_attachment_metadata( $attachment->ID );
|
||||
if ( $meta && isset( $meta['sizes'] ) ) {
|
||||
foreach ( $meta['sizes'] as $size_key => $size_value ) {
|
||||
if ( in_array( $size_key, $allowed_sizes ) ) {
|
||||
++$current_batch_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$total_count += $current_batch_count;
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'wrio_partial_total_count', $total_count );
|
||||
|
||||
// Если больше данных для обработки нет — завершаем и сохраняем в кеш
|
||||
if ( count( $attachments ) < $limit ) {
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'wrio_partial_total_count' );
|
||||
|
||||
wp_send_json_success(
|
||||
[
|
||||
'found_thumbs' => $total_count,
|
||||
'done' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success(
|
||||
[
|
||||
'found_thumbs' => $total_count,
|
||||
'done' => false,
|
||||
'next_offset' => $offset + $limit,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function cron_start() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// where was runned cron
|
||||
$cron_running_place = WRIO_Plugin::app()->getPopulateOption( 'cron_running', false );
|
||||
|
||||
if ( $scope == $cron_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'cron_running', $scope );
|
||||
WRIO_Cron::start();
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function cron_stop() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false );
|
||||
WRIO_Cron::stop();
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function webp_cron_start() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$type = 'conversion';
|
||||
|
||||
// where was runned cron
|
||||
$cron_running_place = WRIO_Plugin::app()->getPopulateOption( "{$type}_cron_running", false );
|
||||
|
||||
if ( $scope == $cron_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", $scope );
|
||||
WRIO_Cron::start( $type );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function webp_cron_stop() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$type = 'conversion';
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", false );
|
||||
WRIO_Cron::stop( $type );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function processing_start() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// where was runned
|
||||
$process_running_place = WRIO_Plugin::app()->getPopulateOption( 'process_running', false );
|
||||
|
||||
if ( $scope == $process_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'process_running', $scope );
|
||||
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( ! $processing ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
|
||||
wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
|
||||
}
|
||||
|
||||
if ( $processing->push_items() ) {
|
||||
$processing->save()->dispatch();
|
||||
} else {
|
||||
// WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
|
||||
wp_send_json_success(
|
||||
[
|
||||
'stop' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function processing_stop() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( $processing ) {
|
||||
$processing->cancel_process();
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function webp_processing_start() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = $scope . '_webp';
|
||||
|
||||
// where was runned
|
||||
$process_running_place = WRIO_Plugin::app()->getPopulateOption( "{$scope}_process_running", false );
|
||||
|
||||
if ( $scope == $process_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", $scope );
|
||||
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( ! $processing ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
|
||||
wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
|
||||
}
|
||||
|
||||
if ( $processing->push_items() ) {
|
||||
$processing->save()->dispatch();
|
||||
} else {
|
||||
// WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
|
||||
wp_send_json_success(
|
||||
[
|
||||
'stop' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function webp_processing_stop() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = $scope . '_webp';
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( $processing ) {
|
||||
$processing->cancel_process();
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start AVIF conversion cron job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function avif_cron_start() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$type = 'avif_conversion';
|
||||
|
||||
// where was runned cron
|
||||
$cron_running_place = WRIO_Plugin::app()->getPopulateOption( "{$type}_cron_running", false );
|
||||
|
||||
if ( $scope == $cron_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", $scope );
|
||||
WRIO_Cron::start( $type );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop AVIF conversion cron job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function avif_cron_stop() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$type = 'avif_conversion';
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", false );
|
||||
WRIO_Cron::stop( $type );
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start AVIF conversion background processing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function avif_processing_start() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = $scope . '_avif';
|
||||
|
||||
// where was runned
|
||||
$process_running_place = WRIO_Plugin::app()->getPopulateOption( "{$scope}_process_running", false );
|
||||
|
||||
if ( $scope == $process_running_place ) {
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", $scope );
|
||||
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( ! $processing ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
|
||||
wp_send_json_error( [ 'message' => 'Processing class not found for scope: ' . $scope ] );
|
||||
}
|
||||
|
||||
if ( $processing->push_items() ) {
|
||||
$processing->save()->dispatch();
|
||||
} else {
|
||||
wp_send_json_success(
|
||||
[
|
||||
'stop' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop AVIF conversion background processing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function avif_processing_stop() {
|
||||
check_ajax_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$scope = $scope . '_avif';
|
||||
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$scope}_process_running", false );
|
||||
$processing = wrio_get_processing_class( $scope );
|
||||
if ( $processing ) {
|
||||
$processing->cancel_process();
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
|
||||
public function bulk_optimization_process() {
|
||||
check_admin_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start bulk optimization process! Scope: %s', $scope ) );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// Use orchestrator for media-library scope
|
||||
if ( 'media-library' === $scope ) {
|
||||
if ( $reset_current_error ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->resetCurrentErrors();
|
||||
}
|
||||
|
||||
$orchestrator = WRIO_Optimization_Orchestrator::get_instance();
|
||||
$result = $orchestrator->execute_next_action( 1 );
|
||||
|
||||
if ( isset( $result['error'] ) ) {
|
||||
$error_massage = $result['error'];
|
||||
|
||||
if ( empty( $error_massage ) ) {
|
||||
$error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: %s.', $error_massage ) );
|
||||
|
||||
wp_send_json_error( [ 'error_message' => $error_massage ] );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End bulk optimization process! Scope: %s. Remain: %d', $scope, $result['remain'] ) );
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
// Fall back to old behavior for custom-folders, nextgen, etc.
|
||||
// Context class name. If plugin expands with add-ons
|
||||
$class_name = 'WRIO_' . wrio_dashes_to_camel_case( $scope, true );
|
||||
|
||||
if ( ! class_exists( $class_name ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: Context class (%s) not found.', $class_name ) );
|
||||
|
||||
// todo: Temporary bug fix.
|
||||
if ( 'custom-folders' === $scope ) {
|
||||
$class_name = 'WRIO_Custom_Folders';
|
||||
} elseif ( 'nextgen-gallery' == $scope ) {
|
||||
$class_name = 'WRIO_Nextgen_Gallery';
|
||||
}
|
||||
|
||||
if ( ! class_exists( $class_name ) ) {
|
||||
wp_send_json_error( [ 'error_message' => 'Context class not found.' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the class depending on the context in which scope user
|
||||
* has runned optimization.
|
||||
*
|
||||
* @see WRIO_Custom_Folders
|
||||
* @see WRIO_Nextgen_Gallery
|
||||
* @var WRIO_Media_Library $optimizer
|
||||
*/
|
||||
$optimizer = new $class_name();
|
||||
|
||||
if ( $reset_current_error ) {
|
||||
$optimizer->resetCurrentErrors();
|
||||
}
|
||||
|
||||
$result = $optimizer->processUnoptimizedImages( 1 );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$error_massage = $result->get_error_message();
|
||||
|
||||
if ( empty( $error_massage ) ) {
|
||||
$error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Bulk optimization error: %s.', $result->get_error_message() ) );
|
||||
|
||||
wp_send_json_error( [ 'error_message' => $error_massage ] );
|
||||
}
|
||||
|
||||
// If all images are processed, send completion command
|
||||
if ( $result['remain'] <= 0 ) {
|
||||
$result['end'] = true;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End bulk optimization process! Scope: %s. Remain: %d', $scope, $result['remain'] ) );
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
public function bulk_conversion_process() {
|
||||
check_admin_referer( 'bulk_conversion' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
|
||||
$scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
|
||||
$format = WRIO_Plugin::app()->request->request( 'format', 'webp', true );
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start bulk conversion process! Scope: %s, Format: %s', $scope, $format ) );
|
||||
|
||||
if ( empty( $scope ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// Context class name. If plugin expands with add-ons
|
||||
$class_name = 'WRIO_' . wrio_dashes_to_camel_case( $scope, true );
|
||||
|
||||
if ( ! class_exists( $class_name ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Bulk conversion error: Context class (%s) not found.', $class_name ) );
|
||||
|
||||
// todo: Temporary bug fix.
|
||||
if ( 'media-library' === $scope ) {
|
||||
$class_name = 'WRIO_Media_Library';
|
||||
} elseif ( 'custom-folders' === $scope ) {
|
||||
$class_name = 'WRIO_Custom_Folders';
|
||||
} elseif ( 'nextgen-gallery' == $scope ) {
|
||||
$class_name = 'WRIO_Nextgen_Gallery';
|
||||
}
|
||||
|
||||
if ( ! class_exists( $class_name ) ) {
|
||||
wp_send_json_error( [ 'error_message' => 'Context class not found.' ] );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the class depending on the context in which scope user
|
||||
* has runned optimization.
|
||||
*
|
||||
* @see WRIO_Media_Library
|
||||
* @see WRIO_Custom_Folders
|
||||
* @see WRIO_Nextgen_Gallery
|
||||
* @var WRIO_Media_Library $optimizer
|
||||
*/
|
||||
$optimizer = new $class_name();
|
||||
|
||||
if ( $reset_current_error ) {
|
||||
$optimizer->resetCurrentErrors(); // сбрасываем текущие ошибки оптимизации
|
||||
}
|
||||
|
||||
$result = $optimizer->webpUnoptimizedImages( 1, $format );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$error_massage = $result->get_error_message();
|
||||
|
||||
if ( empty( $error_massage ) ) {
|
||||
$error_massage = __( "Unknown error. Enable error log on the plugin's settings page, then check the error report on the Error Log page. You can export the error report and send it to the support service of the plugin.", 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Bulk conversion error: %s.', $result->get_error_message() ) );
|
||||
|
||||
wp_send_json_error( [ 'error_message' => $error_massage ] );
|
||||
}
|
||||
|
||||
// если изображения закончились - посылаем команду завершения
|
||||
if ( $result['remain'] <= 0 ) {
|
||||
$result['end'] = true;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End bulk conversion process! Scope: %s, Format: %s. Remain: %d', $scope, $format, $result['remain'] ) );
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
public function reoptimize_image() {
|
||||
check_admin_referer( 'reoptimize' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
|
||||
|
||||
$attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
|
||||
$level = WRIO_Plugin::app()->request->post( 'level', $default_level, true );
|
||||
|
||||
$backup = WIO_Backup::get_instance();
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
|
||||
if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
|
||||
echo $media_library->getMediaColumnContent( $attachment_id );
|
||||
die();
|
||||
}
|
||||
|
||||
$optimized_data = $media_library->optimizeAttachment( $attachment_id, $level );
|
||||
|
||||
if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
|
||||
echo 'processing';
|
||||
die();
|
||||
}
|
||||
|
||||
echo $media_library->getMediaColumnContent( $attachment_id );
|
||||
die();
|
||||
}
|
||||
|
||||
public function convert_image() {
|
||||
check_admin_referer( 'convert' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
|
||||
$format = WRIO_Plugin::app()->request->post( 'format', 'webp', true );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
|
||||
$media_library->webpConvertAttachment( $attachment_id, $format );
|
||||
|
||||
echo $media_library->getMediaColumnContent( $attachment_id );
|
||||
die();
|
||||
}
|
||||
|
||||
public function restore_image() {
|
||||
check_admin_referer( 'restore' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
|
||||
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$wio_attachment = $media_library->getAttachment( $attachment_id );
|
||||
|
||||
if ( $wio_attachment->isOptimized() ) {
|
||||
$media_library->restoreAttachment( $attachment_id );
|
||||
}
|
||||
|
||||
echo $media_library->getMediaColumnContent( $attachment_id );
|
||||
die();
|
||||
}
|
||||
|
||||
public function check_servers_status() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// Auto-detect server based on license status
|
||||
$is_premium = wrio_is_license_activate();
|
||||
$license_source = wrio_get_license_source();
|
||||
$server_name = $is_premium ? 'server_5' : 'server_2';
|
||||
$return_data = [ 'server_name' => $server_name ];
|
||||
|
||||
$headers = [
|
||||
'User-Agent' => '',
|
||||
];
|
||||
|
||||
// For SDK (ThemeIsle) licenses, the license was already validated during activation
|
||||
// via ThemeIsle's API, so we can skip the Robin API license check.
|
||||
if ( $is_premium && 'sdk' === $license_source ) {
|
||||
wp_send_json_success( $return_data );
|
||||
}
|
||||
|
||||
if ( $is_premium ) {
|
||||
$api_url = 'https://dashboard.robinoptimizer.com/v1/license/check';
|
||||
$headers['Authorization'] = 'Bearer ' . base64_encode( wrio_get_license_key() );
|
||||
$headers['PluginId'] = wrio_get_freemius_plugin_id();
|
||||
$headers['X-License-Source'] = $license_source;
|
||||
$headers['X-Site-Url'] = home_url();
|
||||
} else {
|
||||
$api_url = 'https://dashboard.robinoptimizer.com/v1/free/license/check';
|
||||
$host = get_option( 'siteurl' );
|
||||
$headers['Authorization'] = 'Bearer ' . base64_encode( $host );
|
||||
$headers['X-Site-Url'] = home_url();
|
||||
}
|
||||
|
||||
$request = wp_remote_request(
|
||||
$api_url,
|
||||
[
|
||||
'method' => 'GET',
|
||||
'headers' => $headers,
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $request ) ) {
|
||||
$er_msg = $request->get_error_message();
|
||||
|
||||
$return_data['error'] = $er_msg;
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $request );
|
||||
|
||||
if ( $response_code != 200 ) {
|
||||
$return_data['error'] = 'Server response ' . $response_code;
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $request ) );
|
||||
|
||||
if ( isset( $data->response->server_load ) ) {
|
||||
$return_data = [ 'server_load' => $data->response->server_load ];
|
||||
}
|
||||
|
||||
wp_send_json_success( $return_data );
|
||||
}
|
||||
|
||||
public function check_user_balance() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
// Auto-detect server based on license status
|
||||
$is_premium = wrio_is_license_activate();
|
||||
$license_source = wrio_get_license_source();
|
||||
$processor = WIO_OptimizationTools::getImageProcessor();
|
||||
|
||||
if ( ! $processor->has_quota_limit() ) {
|
||||
wp_send_json_error( [ 'error' => __( 'The server has no quota restrictions!', 'robin-image-optimizer' ) ] );
|
||||
}
|
||||
|
||||
// For SDK (ThemeIsle) licenses, we can't check quota via Robin API yet.
|
||||
// Return unlimited quota - actual limits will be enforced server-side during optimization.
|
||||
if ( $is_premium && 'sdk' === $license_source ) {
|
||||
wp_send_json_success(
|
||||
[
|
||||
'balance' => -1, // -1 indicates unlimited/unknown
|
||||
'reset_at' => '',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
|
||||
if ( $is_premium ) {
|
||||
$api_url = 'https://dashboard.robinoptimizer.com/v1/license/remaining';
|
||||
$headers['Authorization'] = 'Bearer ' . base64_encode( wrio_get_license_key() );
|
||||
$headers['PluginId'] = wrio_get_freemius_plugin_id();
|
||||
$headers['X-License-Source'] = $license_source;
|
||||
$headers['X-Site-Url'] = home_url();
|
||||
} else {
|
||||
$api_url = 'https://dashboard.robinoptimizer.com/v1/free/license/remaining';
|
||||
$host = get_option( 'siteurl' );
|
||||
$headers['Authorization'] = 'Bearer ' . base64_encode( $host );
|
||||
$headers['X-Site-Url'] = home_url();
|
||||
}
|
||||
|
||||
$request = wp_remote_request(
|
||||
$api_url,
|
||||
[
|
||||
'method' => 'GET',
|
||||
'headers' => $headers,
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $request ) ) {
|
||||
$error_msg = $request->get_error_message();
|
||||
|
||||
$return_data['error'] = $error_msg;
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $request );
|
||||
$response_body = wp_remote_retrieve_body( $request );
|
||||
|
||||
if ( $response_code != 200 ) {
|
||||
$return_data['error'] = 'Server response ' . $response_code;
|
||||
if ( $response_code === 401 ) {
|
||||
$error_data = @json_decode( $response_body );
|
||||
$return_data['error'] = $error_data->message;
|
||||
}
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
if ( empty( $response_body ) ) {
|
||||
$return_data['error'] = 'Server responded an empty request body!';
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
$data = @json_decode( $response_body );
|
||||
|
||||
if ( ! isset( $data->status ) || $data->status != 'ok' ) {
|
||||
$return_data['error'] = 'Server responded an fail status';
|
||||
wp_send_json_error( $return_data );
|
||||
}
|
||||
|
||||
$current_quota = (int) $data->response->quota;
|
||||
$processor->set_quota_limit( $current_quota );
|
||||
|
||||
$output = [ 'balance' => $current_quota ];
|
||||
|
||||
$reset_at = (int) $data->response->reset_at;
|
||||
$reset_at += (int) get_option( 'gmt_offset', 0 );
|
||||
$output['reset_at'] = gmdate( 'd-m-Y H:i', $reset_at );
|
||||
|
||||
wp_send_json_success( $output );
|
||||
}
|
||||
|
||||
/*
|
||||
public function calculate_total_images() {
|
||||
check_ajax_referer( 'bulk_optimization' );
|
||||
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$sql = $wpdb->prepare( "SELECT * FROM {$db_table}
|
||||
WHERE item_type = 'attachment' AND result_status IN (%s, %s)
|
||||
ORDER BY id DESC;", RIO_Process_Queue::STATUS_SUCCESS, RIO_Process_Queue::STATUS_ERROR );
|
||||
$optimized_images = $wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
$count = 0;
|
||||
if ( ! empty( $optimized_images ) ) {
|
||||
foreach ( $optimized_images as $row ) {
|
||||
$item = new RIO_Process_Queue( $row );
|
||||
$count = $count + 1 + (int) $item->get_extra_data()->get_thumbnails_count();
|
||||
}
|
||||
}
|
||||
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
|
||||
$sql = "SELECT posts.ID
|
||||
FROM {$wpdb->posts} as posts
|
||||
WHERE post_type = 'attachment'
|
||||
AND post_status = 'inherit'
|
||||
AND post_mime_type IN ( {$allowed_formats_sql} )";
|
||||
|
||||
// If you use a WPML plugin, you need to exclude duplicate images
|
||||
if ( defined( 'WPML_PLUGIN_FILE' ) ) {
|
||||
$sql .= " AND NOT EXISTS
|
||||
(SELECT trnsl.element_id FROM {$wpdb->prefix}icl_translations as trnsl
|
||||
WHERE trnsl.element_id=posts.ID
|
||||
AND trnsl.element_type='post_attachment'
|
||||
AND source_language_code IS NOT NULL
|
||||
)";
|
||||
}
|
||||
|
||||
$attachments = $wpdb->get_results( $sql );
|
||||
|
||||
$allowed_sizes = explode( ',', WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', '' ) );
|
||||
$total_images = 0;
|
||||
$upload = wp_upload_dir();
|
||||
$upload = $upload['basedir'];
|
||||
foreach ( $attachments as $attachment ) {
|
||||
$meta = wp_get_attachment_metadata( $attachment->ID );
|
||||
if ( $meta ) {
|
||||
if ( isset( $meta['file'] ) && file_exists( "{$upload}/{$meta['file']}" ) ) {
|
||||
$total_images ++;
|
||||
}
|
||||
|
||||
foreach ( $meta['sizes'] as $k => $value ) {
|
||||
if ( in_array( $k, $allowed_sizes ) ) {
|
||||
$total_images ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result_total = $total_images - $count;
|
||||
|
||||
wp_send_json_success( [
|
||||
'total' => $result_total >= 0 ? $result_total : 0,
|
||||
] );
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации по расписанию
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Cron {
|
||||
|
||||
/**
|
||||
* Инициализация оптимизации по расписанию
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->initHooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Подключение хуков
|
||||
*/
|
||||
public function initHooks() {
|
||||
add_action( 'wrio/cron/optimization_process', [ $this, 'optimization_process' ], 10, 1 );
|
||||
add_action( 'wrio/cron/conversion_process', [ $this, 'conversion_process' ], 10, 1 );
|
||||
add_action( 'wrio/cron/avif_conversion_process', [ $this, 'avif_conversion_process' ], 10, 1 );
|
||||
add_filter( 'cron_schedules', [ $this, 'intervals' ], 100, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Кастомные интервалы выполнения cron задачи
|
||||
*
|
||||
* @param array $intervals Зарегистрированные интервалы
|
||||
*
|
||||
* @return array $intervals Новые интервалы
|
||||
*/
|
||||
public function intervals( $intervals ) {
|
||||
$intervals['wio_1_min'] = [
|
||||
'interval' => 60,
|
||||
'display' => __( '1 minute', 'robin-image-optimizer' ),
|
||||
];
|
||||
$intervals['wio_2_min'] = [
|
||||
'interval' => 60 * 2,
|
||||
// translators: %s is the number of minutes.
|
||||
'display' => sprintf( __( '%s minutes', 'robin-image-optimizer' ), '2' ),
|
||||
];
|
||||
$intervals['wio_5_min'] = [
|
||||
'interval' => 60 * 5,
|
||||
// translators: %s is the number of minutes.
|
||||
'display' => sprintf( __( '%s minutes', 'robin-image-optimizer' ), '5' ),
|
||||
];
|
||||
$intervals['wio_10_min'] = [
|
||||
'interval' => 60 * 10,
|
||||
// translators: %s is the number of minutes.
|
||||
'display' => sprintf( __( '%s minutes', 'robin-image-optimizer' ), '10' ),
|
||||
];
|
||||
$intervals['wio_30_min'] = [
|
||||
'interval' => 60 * 30,
|
||||
// translators: %s is the number of minutes.
|
||||
'display' => sprintf( __( '%s minutes', 'robin-image-optimizer' ), '30' ),
|
||||
];
|
||||
$intervals['wio_hourly'] = [
|
||||
'interval' => 60 * 60,
|
||||
// translators: %s is the number of minutes.
|
||||
'display' => sprintf( __( '%s minutes', 'robin-image-optimizer' ), '60' ),
|
||||
];
|
||||
$intervals['wio_daily'] = [
|
||||
'interval' => 60 * 60 * 24,
|
||||
'display' => __( 'daily', 'robin-image-optimizer' ),
|
||||
];
|
||||
|
||||
return $intervals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Запуск Cron задачи
|
||||
*/
|
||||
public static function start_single( $attachment_id ) {
|
||||
wp_schedule_single_event( time() + 10, 'wrio/cron/optimization_process', [ $attachment_id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Запуск Cron задачи
|
||||
*/
|
||||
public static function start( $type = 'optimization' ) {
|
||||
$interval = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_shedule_time', 'wio_5_min' );
|
||||
if ( ! wp_next_scheduled( "wrio/cron/{$type}_process" ) ) {
|
||||
wp_schedule_event( time(), $interval, "wrio/cron/{$type}_process" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Остановка Cron задачи
|
||||
*/
|
||||
public static function stop( $type = 'optimization' ) {
|
||||
if ( wp_next_scheduled( "wrio/cron/{$type}_process" ) ) {
|
||||
wp_clear_scheduled_hook( "wrio/cron/{$type}_process" );
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$type}_cron_running", false ); // останавливаем крон
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении cron задачи
|
||||
*/
|
||||
public function optimization_process( $attachment_id = 0 ) {
|
||||
// Optimize single image via cron
|
||||
if ( $attachment_id ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'START auto optimize cron job. Attachment: %s', $attachment_id ) );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->optimizeAttachment( $attachment_id );
|
||||
|
||||
// After optimization, also convert to WebP/AVIF if format conversion is enabled
|
||||
if ( class_exists( 'WRIO_Format_Converter_Factory' ) && WRIO_Format_Converter_Factory::is_format_conversion_enabled() ) {
|
||||
$formats = WRIO_Format_Converter_Factory::get_enabled_formats();
|
||||
foreach ( $formats as $format ) {
|
||||
$media_library->webpConvertAttachment( $attachment_id, $format );
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Auto converted attachment %s to %s', $attachment_id, $format ) );
|
||||
}
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'END auto optimize cron job. Attachment: %s', $attachment_id ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$max_process_per_request = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_items_number_per_interation', 3 );
|
||||
$cron_running_page = WRIO_Plugin::app()->getPopulateOption( 'cron_running', false );
|
||||
|
||||
if ( ! $cron_running_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start cron job. Scope: %s', $cron_running_page ) );
|
||||
|
||||
if ( 'media-library' == $cron_running_page ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$result = $media_library->processUnoptimizedImages( $max_process_per_request );
|
||||
} elseif ( 'nextgen' == $cron_running_page ) {
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
$result = $nextgen_gallery->processUnoptimizedImages( $max_process_per_request );
|
||||
} elseif ( 'custom-folders' == $cron_running_page ) {
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$result = $cf->processUnoptimizedImages( $max_process_per_request );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Cron job failed. Error: %s', $result->get_error_message() ) );
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'cron_running' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $result['remain'] <= 0 ) {
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'cron_running' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End cron job. Scope: %s', $cron_running_page ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении cron задачи
|
||||
*/
|
||||
public function conversion_process( $attachment_id = 0 ) {
|
||||
// Optimize single image via cron
|
||||
if ( $attachment_id ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'START auto optimize cron job. Attachment: %s', $attachment_id ) );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->optimizeAttachment( $attachment_id );
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'END auto optimize cron job. Attachment: %s', $attachment_id ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$max_process_per_request = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_items_number_per_interation', 3 );
|
||||
$cron_running_page = WRIO_Plugin::app()->getPopulateOption( 'conversion_cron_running', false );
|
||||
|
||||
if ( ! $cron_running_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start cron job. Scope: %s', $cron_running_page ) );
|
||||
|
||||
if ( 'media-library' == $cron_running_page ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$result = $media_library->webpUnoptimizedImages( $max_process_per_request );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Cron job failed. Error: %s', $result->get_error_message() ) );
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'conversion_cron_running' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $result['remain'] <= 0 ) {
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'conversion_cron_running' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End cron job. Scope: %s', $cron_running_page ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* AVIF conversion cron process handler
|
||||
* Метод конвертирует изображения в AVIF при выполнении cron задачи
|
||||
*
|
||||
* @param int $attachment_id Optional attachment ID for single image conversion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function avif_conversion_process( $attachment_id = 0 ) {
|
||||
// Convert single image via cron
|
||||
if ( $attachment_id ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'START AVIF conversion cron job. Attachment: %s', $attachment_id ) );
|
||||
|
||||
if ( ! class_exists( 'WRIO_Format_Converter_Factory' ) || ! WRIO_Format_Converter_Factory::is_avif_enabled() ) {
|
||||
WRIO_Plugin::app()->logger->warning( 'AVIF conversion cron triggered but AVIF is not enabled' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->webpConvertAttachment( $attachment_id, 'avif' );
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'END AVIF conversion cron job. Attachment: %s', $attachment_id ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$max_process_per_request = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_items_number_per_interation', 3 );
|
||||
$cron_running_page = WRIO_Plugin::app()->getPopulateOption( 'avif_conversion_cron_running', false );
|
||||
|
||||
if ( ! $cron_running_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WRIO_Format_Converter_Factory' ) || ! WRIO_Format_Converter_Factory::is_avif_enabled() ) {
|
||||
WRIO_Plugin::app()->logger->warning( 'AVIF conversion cron triggered but AVIF is not enabled' );
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'avif_conversion_cron_running' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start AVIF conversion cron job. Scope: %s', $cron_running_page ) );
|
||||
|
||||
$result = null;
|
||||
if ( 'media-library' == $cron_running_page ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$result = $media_library->webpUnoptimizedImages( $max_process_per_request, 'avif' );
|
||||
}
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'AVIF conversion cron job failed. Error: %s', $result->get_error_message() ) );
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'avif_conversion_cron_running' );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_array( $result ) && isset( $result['remain'] ) && $result['remain'] <= 0 ) {
|
||||
WRIO_Plugin::app()->logger->info( 'AVIF conversion cron job completed. All images converted.' );
|
||||
WRIO_Plugin::app()->deletePopulateOption( 'avif_conversion_cron_running' );
|
||||
} elseif ( is_array( $result ) && isset( $result['remain'] ) ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'AVIF conversion cron job: %d images remaining', $result['remain'] ) );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End AVIF conversion cron job. Scope: %s', $cron_running_page ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,520 @@
|
||||
<?php
|
||||
/**
|
||||
* Image Query class.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WRIO_Image_Query
|
||||
*/
|
||||
class WRIO_Image_Query {
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var WRIO_Image_Query|null
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Cache for allowed formats SQL string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $allowed_formats_sql;
|
||||
|
||||
/**
|
||||
* Cache for required conversion types
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
protected $required_types = null;
|
||||
|
||||
/**
|
||||
* Cache group for all query results
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_GROUP = 'wrio_image_query';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$formats = wrio_get_allowed_formats( true );
|
||||
$this->allowed_formats_sql = is_array( $formats ) ? implode( ', ', $formats ) : $formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register cache invalidation hooks.
|
||||
*
|
||||
* Call this once during plugin initialization to automatically
|
||||
* clear query caches when images are optimized, restored, or deleted.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_hooks() {
|
||||
add_action( 'wbcr/riop/queue_item_saved', [ __CLASS__, 'clear_cache' ], 100 );
|
||||
add_action( 'wbcr/rio/attachment_restored', [ __CLASS__, 'clear_cache' ], 100 );
|
||||
add_action( 'delete_attachment', [ __CLASS__, 'clear_cache' ], 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*
|
||||
* @return WRIO_Image_Query
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build list of required conversion types based on enabled formats.
|
||||
* Computed fresh each call to avoid stale data.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string[] Array of required item_types
|
||||
*/
|
||||
protected function build_required_types() {
|
||||
$types = [ 'attachment' ]; // Basic optimization always required
|
||||
|
||||
if ( class_exists( 'WRIO_Format_Converter_Factory' ) ) {
|
||||
if ( WRIO_Format_Converter_Factory::is_webp_enabled() ) {
|
||||
$types[] = 'webp';
|
||||
}
|
||||
if ( WRIO_Format_Converter_Factory::is_avif_enabled() ) {
|
||||
$types[] = 'avif';
|
||||
}
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get required conversion types.
|
||||
* Lazy loads on first access.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_required_types() {
|
||||
if ( null === $this->required_types ) {
|
||||
$this->required_types = $this->build_required_types();
|
||||
}
|
||||
|
||||
return $this->required_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sanitized optimization order.
|
||||
* Prevents SQL injection by validating order parameter.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string 'ASC' or 'DESC'
|
||||
*/
|
||||
protected function get_optimize_order() {
|
||||
$order = WRIO_Plugin::app()->getOption( 'image_optimization_order', 'asc' );
|
||||
|
||||
// Whitelist validation - only allow 'DESC', all others default to 'ASC'
|
||||
return strtolower( $order ) === 'desc' ? 'DESC' : 'ASC';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WPML exclusion clause for filtering translation duplicates.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string SQL clause or empty string if WPML not active
|
||||
*/
|
||||
protected function get_wpml_exclusion_clause() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! defined( 'WPML_PLUGIN_FILE' ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return " AND NOT EXISTS (
|
||||
SELECT trnsl.element_id
|
||||
FROM {$wpdb->prefix}icl_translations AS trnsl
|
||||
WHERE trnsl.element_id = posts.ID
|
||||
AND trnsl.element_type = 'post_attachment'
|
||||
AND trnsl.source_language_code IS NOT NULL
|
||||
)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Append pagination to SQL query.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param string $sql SQL query.
|
||||
* @param int|null $limit Number of results to return.
|
||||
* @param int $offset Number of results to skip.
|
||||
*
|
||||
* @return string SQL with pagination appended
|
||||
*/
|
||||
protected function append_pagination( $sql, $limit = null, $offset = 0 ) {
|
||||
if ( $limit ) {
|
||||
$sql .= sprintf( ' LIMIT %d, %d', absint( $offset ), absint( $limit ) );
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cached count or compute and cache result.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param string $cache_key Cache key (will be namespaced).
|
||||
* @param callable $callback Callback that returns the count.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_cached_count( $cache_key, $callback ) {
|
||||
$cached = wp_cache_get( $cache_key, self::CACHE_GROUP );
|
||||
|
||||
if ( false !== $cached ) {
|
||||
return (int) $cached;
|
||||
}
|
||||
|
||||
$count = (int) call_user_func( $callback );
|
||||
|
||||
wp_cache_set( $cache_key, $count, self::CACHE_GROUP, HOUR_IN_SECONDS );
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build base attachment query with common conditions.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param string $select_clause The SELECT portion (e.g., 'DISTINCT posts.ID').
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_base_query( $select_clause ) {
|
||||
global $wpdb;
|
||||
|
||||
return "SELECT {$select_clause}
|
||||
FROM {$wpdb->posts} posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$this->allowed_formats_sql} )";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build optimization status EXISTS clause.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param bool $negate Use NOT EXISTS instead of EXISTS.
|
||||
* @param bool $all_types Check all required types are complete.
|
||||
*
|
||||
* @return string SQL clause
|
||||
*/
|
||||
protected function get_optimization_exists_clause( $negate = false, $all_types = true ) {
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$exists = $negate ? 'NOT EXISTS' : 'EXISTS';
|
||||
$types = $this->get_required_types();
|
||||
|
||||
$placeholders = implode( ',', array_fill( 0, count( $types ), '%s' ) );
|
||||
|
||||
$clause = "{$exists} (
|
||||
SELECT 1
|
||||
FROM {$db_table} rio
|
||||
WHERE rio.object_id = posts.ID
|
||||
AND rio.item_type IN ( {$placeholders} )
|
||||
AND rio.result_status = 'success'";
|
||||
|
||||
if ( $all_types ) {
|
||||
$clause .= '
|
||||
GROUP BY rio.object_id
|
||||
HAVING COUNT(DISTINCT rio.item_type) = ' . count( $types );
|
||||
}
|
||||
|
||||
$clause .= '
|
||||
)';
|
||||
|
||||
return $clause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build error status EXISTS clause.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string SQL clause
|
||||
*/
|
||||
protected function get_error_exists_clause() {
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
|
||||
return "EXISTS (
|
||||
SELECT 1
|
||||
FROM {$db_table} rio
|
||||
WHERE rio.object_id = posts.ID
|
||||
AND rio.result_status = 'error'
|
||||
)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ORDER BY clause for unoptimized images, pushing error items to the end.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return string SQL clause
|
||||
*/
|
||||
protected function get_unoptimized_order_clause() {
|
||||
return ' ORDER BY CASE WHEN ' . $this->get_error_exists_clause() . ' THEN 1 ELSE 0 END ASC, posts.ID ' . $this->get_optimize_order();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IDs of fully optimized images.
|
||||
*
|
||||
* An image is optimized if it has successful conversions for ALL required types.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param int|null $limit Number of results to return. NULL for no limit.
|
||||
* @param int $offset Number of results to skip.
|
||||
*
|
||||
* @return int[] Array of attachment IDs
|
||||
*/
|
||||
public function get_optimized_ids( $limit = null, $offset = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'DISTINCT posts.ID' );
|
||||
$sql .= ' AND ' . $this->get_optimization_exists_clause( false, true );
|
||||
$sql .= ' ORDER BY posts.ID ' . $this->get_optimize_order();
|
||||
$sql = $this->append_pagination( $sql, $limit, $offset );
|
||||
|
||||
$sql = $wpdb->prepare( $sql, $this->get_required_types() );
|
||||
|
||||
$result = $wpdb->get_col( $sql );
|
||||
|
||||
return array_map( 'absint', $result ?? [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IDs of unoptimized images.
|
||||
*
|
||||
* Unoptimized images are those missing ANY required conversion with success status.
|
||||
* Includes: never queued, partial, failed, and processing images.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param int|null $limit Number of results to return. NULL for no limit.
|
||||
* @param int $offset Number of results to skip.
|
||||
* @param bool $exclude_wpml_dupes Whether to exclude WPML translation duplicates.
|
||||
*
|
||||
* @return int[] Array of attachment IDs
|
||||
*/
|
||||
public function get_unoptimized_ids( $limit = null, $offset = 0, $exclude_wpml_dupes = true ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'DISTINCT posts.ID' );
|
||||
$sql .= ' AND ' . $this->get_optimization_exists_clause( true, true );
|
||||
|
||||
if ( $exclude_wpml_dupes ) {
|
||||
$sql .= $this->get_wpml_exclusion_clause();
|
||||
}
|
||||
|
||||
$sql .= $this->get_unoptimized_order_clause();
|
||||
$sql = $this->append_pagination( $sql, $limit, $offset );
|
||||
|
||||
$sql = $wpdb->prepare( $sql, $this->get_required_types() );
|
||||
|
||||
$result = $wpdb->get_col( $sql );
|
||||
|
||||
return array_map( 'absint', $result ?? [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IDs of images with optimization errors.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param int|null $limit Number of results to return. NULL for no limit.
|
||||
* @param int $offset Number of results to skip.
|
||||
*
|
||||
* @return int[] Array of attachment IDs
|
||||
*/
|
||||
public function get_error_ids( $limit = null, $offset = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'DISTINCT posts.ID' );
|
||||
$sql .= ' AND ' . $this->get_error_exists_clause();
|
||||
$sql .= ' ORDER BY posts.ID ASC';
|
||||
$sql = $this->append_pagination( $sql, $limit, $offset );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query built with safe methods, no user input.
|
||||
$result = $wpdb->get_col( $sql );
|
||||
|
||||
return array_map( 'absint', $result ?? [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Count fully optimized images.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count_optimized() {
|
||||
return $this->get_cached_count(
|
||||
'count_optimized',
|
||||
function () {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'COUNT(DISTINCT posts.ID)' );
|
||||
$sql .= ' AND ' . $this->get_optimization_exists_clause( false, true );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query built with safe methods and prepared with types.
|
||||
$sql = $wpdb->prepare( $sql, $this->get_required_types() );
|
||||
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count unoptimized images.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param bool $exclude_wpml_dupes Whether to exclude WPML translation duplicates.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count_unoptimized( $exclude_wpml_dupes = true ) {
|
||||
$cache_suffix = $exclude_wpml_dupes ? '1' : '0';
|
||||
$cache_key = "count_unoptimized_{$cache_suffix}";
|
||||
|
||||
return $this->get_cached_count(
|
||||
$cache_key,
|
||||
function () use ( $exclude_wpml_dupes ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'COUNT(DISTINCT posts.ID)' );
|
||||
$sql .= ' AND ' . $this->get_optimization_exists_clause( true, true );
|
||||
|
||||
if ( $exclude_wpml_dupes ) {
|
||||
$sql .= $this->get_wpml_exclusion_clause();
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query built with safe methods and prepared with types.
|
||||
$sql = $wpdb->prepare( $sql, $this->get_required_types() );
|
||||
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count images with optimization errors.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count_error() {
|
||||
return $this->get_cached_count(
|
||||
'count_error',
|
||||
function () {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'COUNT(DISTINCT posts.ID)' );
|
||||
$sql .= ' AND ' . $this->get_error_exists_clause();
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query built with safe methods, no user input.
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total attachment images with allowed formats.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @param bool $exclude_wpml_dupes Whether to exclude WPML translation duplicates.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count_total_attachments( $exclude_wpml_dupes = true ) {
|
||||
$cache_suffix = $exclude_wpml_dupes ? '1' : '0';
|
||||
$cache_key = "count_total_attachments_{$cache_suffix}";
|
||||
|
||||
return $this->get_cached_count(
|
||||
$cache_key,
|
||||
function () use ( $exclude_wpml_dupes ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = $this->get_base_query( 'COUNT(DISTINCT posts.ID)' );
|
||||
|
||||
if ( $exclude_wpml_dupes ) {
|
||||
$sql .= $this->get_wpml_exclusion_clause();
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query built with safe methods, no user input.
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all query caches.
|
||||
*
|
||||
* Call after image optimization, restoration, or deletion.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear_cache() {
|
||||
$keys = [
|
||||
'count_optimized',
|
||||
'count_unoptimized_0',
|
||||
'count_unoptimized_1',
|
||||
'count_error',
|
||||
'count_total_attachments_0',
|
||||
'count_total_attachments_1',
|
||||
];
|
||||
|
||||
foreach ( $keys as $key ) {
|
||||
wp_cache_delete( $key, self::CACHE_GROUP );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh instance data after settings change.
|
||||
*
|
||||
* Use this if WebP/AVIF settings are changed mid-request.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function refresh() {
|
||||
$this->required_types = null;
|
||||
$formats = wrio_get_allowed_formats( true );
|
||||
$this->allowed_formats_sql = is_array( $formats ) ? implode( ', ', $formats ) : $formats;
|
||||
self::clear_cache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,865 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы со статистическими данными по оптимизации изображений
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Image_Statistic {
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @access protected
|
||||
* @var static
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* The statistic data.
|
||||
*
|
||||
* @var array{
|
||||
* original: int,
|
||||
* optimized: int,
|
||||
* converted: int,
|
||||
* optimized_percent: int|float,
|
||||
* percent_line: float,
|
||||
* webp_percent_line: float,
|
||||
* unoptimized: int,
|
||||
* unconverted: int,
|
||||
* optimized_size: int|string,
|
||||
* webp_optimized_size: int|string,
|
||||
* avif_optimized_size: int|string,
|
||||
* original_size: int|string,
|
||||
* save_size_percent: float,
|
||||
* error: int,
|
||||
* webp_error: int,
|
||||
* avif_converted: int,
|
||||
* avif_unconverted: int,
|
||||
* avif_percent_line: float,
|
||||
* avif_error: int,
|
||||
* quota_limit?: int|string
|
||||
* }
|
||||
* @see WRIO_Image_Statistic::load()
|
||||
*/
|
||||
protected $statistic;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->statistic = $this->load();
|
||||
}
|
||||
|
||||
/**
|
||||
* The main instance.
|
||||
*
|
||||
* @return static Returns the instance of the class.
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( static::$_instance ) ) {
|
||||
static::$_instance = new static();
|
||||
}
|
||||
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file size reliably within the current request.
|
||||
* PHP caches stat() results; we clear cache for this path.
|
||||
*
|
||||
* @param mixed $file_path The file path.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_file_size( $file_path ) {
|
||||
return wrio_get_file_size( $file_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the statistic data.
|
||||
*
|
||||
* @return array{
|
||||
* original: int|string,
|
||||
* optimized: int|string,
|
||||
* converted: int,
|
||||
* optimized_percent: float,
|
||||
* percent_line: float,
|
||||
* webp_percent_line: float,
|
||||
* unoptimized: int,
|
||||
* unconverted: int,
|
||||
* optimized_size: int|string,
|
||||
* webp_optimized_size: int|string,
|
||||
* original_size: int|string,
|
||||
* save_size_percent: float,
|
||||
* error: int,
|
||||
* webp_error: int,
|
||||
* quota_limit?: int|string
|
||||
* }
|
||||
*/
|
||||
public function get() {
|
||||
return $this->statistic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет новые данные к текущей статистике
|
||||
* К текущим числам добавляются новые
|
||||
*
|
||||
* @param string $field Поле, к которому добавляем значение
|
||||
* @param int $value добавляемое значение
|
||||
*/
|
||||
public function addToField( $field, $value ) {
|
||||
if ( isset( $this->statistic[ $field ] ) ) {
|
||||
$this->statistic[ $field ] = $this->statistic[ $field ] + $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычитает данные из текущей статистики
|
||||
* Из текущего числа вычитается
|
||||
*
|
||||
* @param string $field Поле, из которого вычитается значение
|
||||
* @param int $value вычитаемое значение
|
||||
*/
|
||||
public function deductFromField( $field, $value ) {
|
||||
$value = (int) $value;
|
||||
if ( isset( $this->statistic[ $field ] ) ) {
|
||||
$this->statistic[ $field ] = $this->statistic[ $field ] - $value;
|
||||
if ( $this->statistic[ $field ] < 0 ) {
|
||||
$this->statistic[ $field ] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохранение статистики
|
||||
*/
|
||||
public function save() {
|
||||
WRIO_Plugin::app()->updateOption( 'original_size', $this->statistic['original_size'] );
|
||||
WRIO_Plugin::app()->updateOption( 'optimized_size', $this->statistic['optimized_size'] );
|
||||
WRIO_Plugin::app()->updateOption( 'webp_optimized_size', $this->statistic['webp_optimized_size'] );
|
||||
WRIO_Plugin::app()->updateOption( 'avif_optimized_size', $this->statistic['avif_optimized_size'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loading statistics and calculating some parameters.
|
||||
*
|
||||
* @return array{
|
||||
* original: int|string,
|
||||
* optimized: int|string,
|
||||
* converted: int,
|
||||
* optimized_percent: float,
|
||||
* percent_line: float,
|
||||
* webp_percent_line: float,
|
||||
* unoptimized: int,
|
||||
* unconverted: int,
|
||||
* optimized_size: int|string,
|
||||
* webp_optimized_size: int|string,
|
||||
* original_size: int|string,
|
||||
* save_size_percent: float,
|
||||
* error: int,
|
||||
* webp_error: int,
|
||||
* avif_converted: int,
|
||||
* avif_unconverted: int,
|
||||
* avif_percent_line: float,
|
||||
* avif_error: int,
|
||||
* quota_limit?: int|string
|
||||
* }
|
||||
*/
|
||||
public function load() {
|
||||
$original_size = WRIO_Plugin::app()->getOption( 'original_size', 0 );
|
||||
$optimized_size = WRIO_Plugin::app()->getOption( 'optimized_size', 0 );
|
||||
$webp_optimized_size = WRIO_Plugin::app()->getOption( 'webp_optimized_size', 0 );
|
||||
$avif_optimized_size = WRIO_Plugin::app()->getOption( 'avif_optimized_size', 0 );
|
||||
|
||||
$image_query = WRIO_Image_Query::get_instance();
|
||||
$total_images = $image_query->count_total_attachments( false ); // false = don't exclude WPML dupes for total display
|
||||
$optimized_count = $image_query->count_optimized();
|
||||
$error_count = $image_query->count_error(); // Count images with ANY error (attachment, webp, or avif)
|
||||
$webp_optimized_count = RIO_Process_Queue::count_by_type_status( 'webp', 'success' );
|
||||
$webp_error_count = (int) RIO_Process_Queue::count_by_type_status( 'webp', 'error' );
|
||||
$avif_optimized_count = RIO_Process_Queue::count_by_type_status( 'avif', 'success' );
|
||||
$avif_error_count = (int) RIO_Process_Queue::count_by_type_status( 'avif', 'error' );
|
||||
|
||||
if ( ! $total_images ) {
|
||||
$total_images = 0;
|
||||
}
|
||||
if ( ! $error_count ) {
|
||||
$error_count = 0;
|
||||
}
|
||||
if ( ! $optimized_count ) {
|
||||
$optimized_count = 0;
|
||||
}
|
||||
// Unoptimized = total - optimized - errors (ensures mutual exclusivity)
|
||||
$unoptimized_count = max( 0, $total_images - $optimized_count - $error_count );
|
||||
|
||||
// WebP stats
|
||||
$unconverted_count = static::get_unconverted_count( 'webp' );
|
||||
if ( $unconverted_count < 0 ) {
|
||||
$unconverted_count = 0;
|
||||
}
|
||||
$converted_count = static::get_converted_count( 'webp' );
|
||||
if ( $converted_count < 0 ) {
|
||||
$converted_count = 0;
|
||||
}
|
||||
|
||||
$total_count = $converted_count + $unconverted_count;
|
||||
$webp_percent_diff_line = 0;
|
||||
|
||||
if ( $total_count ) {
|
||||
$webp_percent_diff_line = round( $converted_count / $total_count * 100, 1 );
|
||||
}
|
||||
|
||||
// AVIF stats
|
||||
$avif_unconverted_count = static::get_unconverted_count( 'avif' );
|
||||
if ( $avif_unconverted_count < 0 ) {
|
||||
$avif_unconverted_count = 0;
|
||||
}
|
||||
$avif_converted_count = static::get_converted_count( 'avif' );
|
||||
if ( $avif_converted_count < 0 ) {
|
||||
$avif_converted_count = 0;
|
||||
}
|
||||
|
||||
$avif_total_count = $avif_converted_count + $avif_unconverted_count;
|
||||
$avif_percent_diff_line = 0;
|
||||
|
||||
if ( $avif_total_count ) {
|
||||
$avif_percent_diff_line = round( $avif_converted_count / $avif_total_count * 100, 1 );
|
||||
}
|
||||
|
||||
$percent_diff = 0;
|
||||
$percent_diff_line = 100;
|
||||
if ( $optimized_size && $original_size ) {
|
||||
$percent_diff = round( ( $original_size - $optimized_size ) * 100 / $original_size, 1 );
|
||||
$percent_diff_line = round( $optimized_size * 100 / $original_size, 0 );
|
||||
}
|
||||
|
||||
$optimized_images_percent = 0;
|
||||
if ( $total_images > 0 ) {
|
||||
$optimized_images_percent = floor( $optimized_count * 100 / $total_images );
|
||||
}
|
||||
|
||||
$processor = WIO_OptimizationTools::getImageProcessor();
|
||||
|
||||
$data = [
|
||||
'original' => $total_images,
|
||||
'optimized' => $optimized_count,
|
||||
'converted' => $converted_count,
|
||||
'optimized_percent' => $optimized_images_percent,
|
||||
'percent_line' => $percent_diff_line,
|
||||
'webp_percent_line' => $webp_percent_diff_line,
|
||||
'unoptimized' => $unoptimized_count,
|
||||
'unconverted' => $unconverted_count,
|
||||
'optimized_size' => $optimized_size,
|
||||
'webp_optimized_size' => $webp_optimized_size,
|
||||
'original_size' => $original_size,
|
||||
'save_size_percent' => $percent_diff,
|
||||
'error' => $error_count,
|
||||
'webp_error' => $webp_error_count,
|
||||
// AVIF stats
|
||||
'avif_converted' => $avif_converted_count,
|
||||
'avif_unconverted' => $avif_unconverted_count,
|
||||
'avif_percent_line' => $avif_percent_diff_line,
|
||||
'avif_error' => $avif_error_count,
|
||||
'avif_optimized_size' => $avif_optimized_size,
|
||||
];
|
||||
|
||||
if ( $processor->has_quota_limit() ) {
|
||||
$data['quota_limit'] = $processor->get_quota_limit();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of non-optimized images
|
||||
* Учитывает базовую оптимизацию и конвертацию форматов (WebP/AVIF)
|
||||
* Accounts for basic optimization and format conversion (WebP/AVIF)
|
||||
*
|
||||
* An image is "unoptimized" if it's missing ANY required conversion:
|
||||
* - Basic optimization (attachment) is always required
|
||||
* - WebP conversion is required if WebP is enabled
|
||||
* - AVIF conversion is required if AVIF is enabled
|
||||
*
|
||||
* @return int
|
||||
* @since 1.3.6
|
||||
*/
|
||||
public static function get_unoptimized_count() {
|
||||
return WRIO_Image_Query::get_instance()->count_unoptimized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of non-converted images
|
||||
*
|
||||
* @param string $format Target format: 'webp' or 'avif'. Default 'webp'.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.5.3
|
||||
*/
|
||||
public static function get_unconverted_count( $format = 'webp' ) {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
// Count only attachments that finished base optimization successfully and
|
||||
// still do not have a queue record for the requested conversion format.
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT DISTINCT count(posts.ID)
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'success'
|
||||
GROUP BY object_id
|
||||
)
|
||||
AND posts.ID NOT IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = %s
|
||||
GROUP BY object_id
|
||||
)",
|
||||
$format
|
||||
);
|
||||
|
||||
$total_images = $wpdb->get_var( $sql );
|
||||
|
||||
return (int) $total_images;
|
||||
}
|
||||
|
||||
/**
|
||||
* non-converted images
|
||||
*
|
||||
* @param string $format Target format: 'webp' or 'avif'. Default 'webp'.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.5.3
|
||||
*/
|
||||
public static function get_unconverted_images( $format = 'webp' ) {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
// Select only attachments that finished base optimization successfully and
|
||||
// still do not have a queue record for the requested conversion format.
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT DISTINCT posts.ID
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'success'
|
||||
GROUP BY object_id
|
||||
)
|
||||
AND posts.ID NOT IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = %s
|
||||
GROUP BY object_id
|
||||
)",
|
||||
$format
|
||||
);
|
||||
|
||||
$images = $wpdb->get_col( $sql );
|
||||
|
||||
return is_array( $images ) ? $images : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of converted images
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.5.3
|
||||
*/
|
||||
public static function get_converted_count( $format = 'webp' ) {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT DISTINCT count(posts.ID)
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID IN(SELECT object_id FROM {$db_table} AS rio WHERE rio.item_type = %s GROUP BY object_id)",
|
||||
$format
|
||||
);
|
||||
|
||||
$total_images = $wpdb->get_var( $sql );
|
||||
|
||||
return (int) $total_images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает результат последних оптимизаций изображений
|
||||
*
|
||||
* @param int $limit By default - 100. If limit=0, then no limit
|
||||
*
|
||||
* @return array {
|
||||
* Параметры
|
||||
* @type string $id id
|
||||
* @type string $file_name Имя файла
|
||||
* @type string $url URL
|
||||
* @type string $thumbnail_url URL превьюшки
|
||||
* @type string $optimized_size Размер после оптимизации
|
||||
* @type string $thumbnails_count Сколько превьюшек оптимизировано
|
||||
* @type string $total_saving Процент оптимизации главного файла и превьюшек
|
||||
* }
|
||||
*/
|
||||
public function get_last_optimized_images( $limit = 100 ) {
|
||||
global $wpdb;
|
||||
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
|
||||
$limit = max( 0, (int) $limit );
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT object_id FROM {$db_table}
|
||||
WHERE result_status IN (%s, %s)
|
||||
ORDER BY id DESC
|
||||
LIMIT %d;",
|
||||
RIO_Process_Queue::STATUS_SUCCESS,
|
||||
RIO_Process_Queue::STATUS_ERROR,
|
||||
$limit
|
||||
);
|
||||
|
||||
$optimized_images_logs = $wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
$optimized_attachment_ids = [];
|
||||
foreach ( $optimized_images_logs as $log ) {
|
||||
$optimized_attachment_ids[] = $log['object_id'];
|
||||
}
|
||||
$optimized_attachment_ids = array_unique( $optimized_attachment_ids );
|
||||
|
||||
$optimized_attachment = [];
|
||||
foreach ( $optimized_attachment_ids as $attachment_id ) {
|
||||
$log_data = $this->get_last_optimized_image( $attachment_id );
|
||||
if ( ! empty( $log_data ) ) {
|
||||
$optimized_attachment[] = $log_data[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $optimized_attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last optimized image record for a specific attachment.
|
||||
* Uses the same data source as get_last_optimized_images() for consistency.
|
||||
*
|
||||
* @param int $attachment_id Attachment ID.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
* @since 1.3.9
|
||||
*/
|
||||
public function get_last_optimized_image( $attachment_id ) {
|
||||
$info = WRIO_Media_Library::get_instance()->calculateMediaLibraryParams( $attachment_id );
|
||||
|
||||
$best_optimized_size = ! empty( $info['optimized_size'] ) ? $info['optimized_size'] : 0;
|
||||
if ( ! empty( $info['webp_size'] ) ) {
|
||||
$best_optimized_size = min( $best_optimized_size, $info['webp_size'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $info['avif_size'] ) ) {
|
||||
$best_optimized_size = min( $best_optimized_size, $info['avif_size'] );
|
||||
}
|
||||
|
||||
$original_size = ! empty( $info['original_size'] ) ? $info['original_size'] : 0;
|
||||
$best_optimized_size = min( $best_optimized_size, $original_size );
|
||||
|
||||
$log = [
|
||||
'id' => $attachment_id,
|
||||
'file_name' => $info['original_name'],
|
||||
'url' => $info['edit_url'],
|
||||
'thumbnail_url' => $info['original_url'],
|
||||
'original_size' => size_format( $original_size, 2 ),
|
||||
'optimized_size' => size_format( $best_optimized_size, 2 ),
|
||||
'thumbnails_count' => ! empty( $info['thumbnails_optimized'] ) ? $info['thumbnails_optimized'] : 0,
|
||||
'total_saving' => ! empty( $info['diff_percent_all'] ) ? $info['diff_percent_all'] . '%' : '0%',
|
||||
];
|
||||
|
||||
// Check errors.
|
||||
if ( ! empty( $info['error_msg'] ) ) {
|
||||
$log['type'] = 'error';
|
||||
$log['error_msg'] = $info['error_msg'];
|
||||
}
|
||||
|
||||
return [ $log ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $object_id
|
||||
* @param string $format Format type: 'webp' or 'avif'. Default 'webp'.
|
||||
*
|
||||
* @since 1.3.9
|
||||
*/
|
||||
public function get_last_converted_image( $object_id, $format = 'webp' ) {
|
||||
global $wpdb;
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT * FROM {$db_table}
|
||||
WHERE object_id = %d AND item_type = %s AND result_status IN (%s, %s)
|
||||
ORDER BY original_size DESC
|
||||
LIMIT 1;",
|
||||
(int) $object_id,
|
||||
$format,
|
||||
RIO_Process_Queue::STATUS_SUCCESS,
|
||||
RIO_Process_Queue::STATUS_ERROR
|
||||
);
|
||||
|
||||
$model = $wpdb->get_row( $sql, ARRAY_A );
|
||||
|
||||
if ( ! empty( $model ) ) {
|
||||
$items[] = $this->format_webp_for_log( new RIO_Process_Queue( $model ) );
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a queue record for the optimization log display.
|
||||
* Works universally for attachment, webp, and avif item types.
|
||||
*
|
||||
* @param RIO_Process_Queue $queue_model Queue model instance.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
* @throws \Exception If invalid model provided.
|
||||
* @since 1.3.9
|
||||
*/
|
||||
protected function format_for_log( $queue_model ) {
|
||||
if ( ! $queue_model instanceof RIO_Process_Queue ) {
|
||||
throw new Exception( 'Variable $queue_model must be an instance of RIO_Process_Queue!' );
|
||||
}
|
||||
|
||||
$extra_data = $queue_model->get_extra_data();
|
||||
$object_id = $queue_model->get_object_id();
|
||||
$item_type = $queue_model->item_type;
|
||||
$original_size = $queue_model->get_original_size();
|
||||
$final_size = min( $original_size, $queue_model->get_final_size() );
|
||||
|
||||
$formatted_data = [
|
||||
'id' => $queue_model->get_id(),
|
||||
'attachment_id' => $object_id,
|
||||
'item_type' => $item_type,
|
||||
'url' => admin_url( sprintf( 'post.php?post=%d&action=edit', $object_id ) ),
|
||||
'original_url' => null,
|
||||
'thumbnail_url' => null,
|
||||
'file_name' => null,
|
||||
'original_size' => size_format( $original_size, 2 ),
|
||||
'original_size_bytes' => $original_size,
|
||||
'optimized_size' => size_format( $final_size, 2 ),
|
||||
'type' => 'success',
|
||||
'webp_size' => null,
|
||||
'avif_size' => null,
|
||||
'original_saving' => 0,
|
||||
'thumbnails_count' => 0,
|
||||
'total_saving' => 0,
|
||||
'final_size_bytes' => $final_size,
|
||||
'converted_from' => null,
|
||||
];
|
||||
|
||||
// Get URLs and file name based on item type
|
||||
if ( in_array( $item_type, [ 'webp', 'avif' ], true ) && $extra_data instanceof RIOP_WebP_Extra_Data ) {
|
||||
// For webp/avif, use source_src from extra_data
|
||||
$original_url = $extra_data->get_source_src();
|
||||
$formatted_data['original_url'] = $original_url;
|
||||
$formatted_data['file_name'] = wp_basename( $original_url );
|
||||
$formatted_data['thumbnail_url'] = $original_url;
|
||||
$formatted_data['converted_from'] = $extra_data->get_converted_from_size();
|
||||
|
||||
// Set the appropriate size field
|
||||
if ( 'avif' === $item_type ) {
|
||||
$formatted_data['avif_size'] = size_format( $final_size, 2 );
|
||||
} else {
|
||||
$formatted_data['webp_size'] = size_format( $final_size, 2 );
|
||||
}
|
||||
|
||||
if ( $extra_data->get_thumbnails_count() ) {
|
||||
$formatted_data['thumbnails_count'] = $extra_data->get_thumbnails_count();
|
||||
}
|
||||
} else {
|
||||
// For attachment type, use WordPress attachment metadata
|
||||
$upload_dir = wp_upload_dir();
|
||||
$attachment_meta = wp_get_attachment_metadata( $object_id );
|
||||
|
||||
if ( ! empty( $attachment_meta ) ) {
|
||||
$image_url = trailingslashit( $upload_dir['baseurl'] ) . $attachment_meta['file'];
|
||||
$formatted_data['original_url'] = $image_url;
|
||||
$formatted_data['file_name'] = wp_basename( $attachment_meta['file'] );
|
||||
$formatted_data['thumbnail_url'] = $image_url;
|
||||
|
||||
if ( isset( $attachment_meta['sizes']['thumbnail'] ) ) {
|
||||
$image_basename = wp_basename( $image_url );
|
||||
$formatted_data['thumbnail_url'] = str_replace( $image_basename, $attachment_meta['sizes']['thumbnail']['file'], $image_url );
|
||||
}
|
||||
|
||||
if ( ! empty( $extra_data ) && method_exists( $extra_data, 'get_thumbnails_count' ) ) {
|
||||
$formatted_data['thumbnails_count'] = $extra_data->get_thumbnails_count();
|
||||
}
|
||||
} else {
|
||||
// Fallback to post guid
|
||||
$attachment = get_post( $object_id );
|
||||
if ( ! empty( $attachment ) ) {
|
||||
$formatted_data['original_url'] = $attachment->guid;
|
||||
$formatted_data['thumbnail_url'] = $attachment->guid;
|
||||
$formatted_data['file_name'] = wp_basename( $attachment->guid );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total saving directly from the row's original_size and final_size
|
||||
if ( is_numeric( $original_size ) && $original_size > 0 && is_numeric( $final_size ) ) {
|
||||
$total_saving = ( $original_size - $final_size ) * 100 / $original_size;
|
||||
$total_saving = max( 0, min( $total_saving, 100 ) );
|
||||
$formatted_data['total_saving'] = round( $total_saving, 2 ) . '%';
|
||||
}
|
||||
|
||||
// Handle errors
|
||||
if ( RIO_Process_Queue::STATUS_ERROR === $queue_model->get_result_status() ) {
|
||||
$error_message = null;
|
||||
|
||||
if ( ! empty( $extra_data ) && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error_message = $extra_data->get_error_msg();
|
||||
}
|
||||
|
||||
$formatted_data['type'] = 'error';
|
||||
$formatted_data['error_msg'] = ! empty( $error_message ) ? $error_message : __( 'Unknown error', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
return $formatted_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format WebP/AVIF record for log display.
|
||||
*
|
||||
* @param RIO_Process_Queue $queue_model Queue model instance.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
* @throws \Exception If invalid model provided.
|
||||
* @since 1.5.3
|
||||
*/
|
||||
protected function format_webp_for_log( $queue_model ) {
|
||||
if ( ! $queue_model instanceof RIO_Process_Queue ) {
|
||||
throw new Exception( 'Variable $queue_model must be an instance of RIO_Process_Queue!' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var RIO_Attachment_Extra_Data $extra_data
|
||||
*/
|
||||
$extra_data = $queue_model->get_extra_data();
|
||||
|
||||
$default_formated_data = [
|
||||
'id' => $queue_model->get_id(),
|
||||
'attachment_id' => $queue_model->get_object_id(),
|
||||
'item_type' => $queue_model->item_type,
|
||||
'url' => admin_url( sprintf( 'post.php?post=%d&action=edit', $queue_model->get_object_id() ) ),
|
||||
'original_url' => null,
|
||||
'thumbnail_url' => null,
|
||||
'file_name' => null,
|
||||
'original_size' => 0,
|
||||
'optimized_size' => 0,
|
||||
'type' => 'success',
|
||||
'webp_size' => null,
|
||||
'avif_size' => null,
|
||||
'original_saving' => 0,
|
||||
'thumbnails_count' => 0,
|
||||
'total_saving' => 0,
|
||||
];
|
||||
|
||||
$upload_dir = wp_upload_dir();
|
||||
|
||||
$attachment_meta = wp_get_attachment_metadata( $queue_model->get_object_id() );
|
||||
$formated_data = [];
|
||||
|
||||
if ( ! empty( $attachment_meta ) ) {
|
||||
$image_url = trailingslashit( $upload_dir['baseurl'] ) . $attachment_meta['file'];
|
||||
$thumbnail_url = $image_url;
|
||||
|
||||
if ( isset( $attachment_meta['sizes']['thumbnail'] ) ) {
|
||||
$image_basename = wp_basename( $image_url );
|
||||
$thumbnail_url = str_replace( $image_basename, $attachment_meta['sizes']['thumbnail']['file'], $image_url );
|
||||
}
|
||||
|
||||
// Get the extension from the item type (webp or avif)
|
||||
$converted_extension = '.' . $queue_model->item_type;
|
||||
|
||||
// Determine the field name based on format type
|
||||
$size_field_name = 'avif' === $queue_model->item_type ? 'avif_size' : 'webp_size';
|
||||
|
||||
$formated_data = wp_parse_args(
|
||||
[
|
||||
'original_url' => $image_url . $converted_extension,
|
||||
'thumbnail_url' => $thumbnail_url,
|
||||
'file_name' => wp_basename( $attachment_meta['file'] ) . $converted_extension,
|
||||
'original_size' => size_format( $queue_model->get_original_size(), 2 ),
|
||||
'optimized_size' => '-',
|
||||
$size_field_name => size_format( $queue_model->get_final_size(), 2 ),
|
||||
],
|
||||
$default_formated_data
|
||||
);
|
||||
|
||||
$main_file = trailingslashit( $upload_dir['basedir'] ) . $attachment_meta['file'];
|
||||
|
||||
// An extra data may be empty after a failed migration or an unknown error.
|
||||
if ( ! empty( $extra_data ) ) {
|
||||
$original_main_size = $extra_data->get_original_main_size();
|
||||
|
||||
if ( $original_main_size ) {
|
||||
$current_main_size = $this->get_file_size( $main_file );
|
||||
$original_saving = ( $original_main_size - $current_main_size ) * 100 / $original_main_size;
|
||||
$formated_data['original_saving'] = round( $original_saving ) . '%';
|
||||
}
|
||||
|
||||
$formated_data['thumbnails_count'] = $extra_data->get_thumbnails_count();
|
||||
}
|
||||
|
||||
if ( $queue_model->get_original_size() ) {
|
||||
$total_saving = ( $queue_model->get_original_size() - $queue_model->get_final_size() ) * 100 / $queue_model->get_original_size();
|
||||
$formated_data['total_saving'] = round( $total_saving, 2 ) . '%';
|
||||
}
|
||||
} else {
|
||||
$attachment = get_post( $queue_model->get_object_id() );
|
||||
|
||||
if ( ! empty( $attachment ) ) {
|
||||
$formated_data = [
|
||||
'original_url' => $attachment->guid,
|
||||
'thumbnail_url' => $attachment->guid,
|
||||
'file_name' => wp_basename( $attachment->guid ),
|
||||
];
|
||||
}
|
||||
|
||||
$formated_data = wp_parse_args( $formated_data, $default_formated_data );
|
||||
}
|
||||
|
||||
// We collect information about errors
|
||||
if ( RIO_Process_Queue::STATUS_ERROR === $queue_model->get_result_status() ) {
|
||||
$error_message = null;
|
||||
|
||||
if ( ! empty( $extra_data ) && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error_message = $extra_data->get_error_msg();
|
||||
}
|
||||
|
||||
$formated_data['type'] = 'error';
|
||||
$formated_data['error_msg'] = ! empty( $error_message ) ? $error_message : __( 'Unknown error', 'robin-image-optimizer' );
|
||||
|
||||
return $formated_data;
|
||||
}
|
||||
|
||||
return $formated_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает общий процент оптимизированных изображений
|
||||
*
|
||||
* @return int общий процент оптимизации
|
||||
*/
|
||||
public function getOptimizedPercent() {
|
||||
if ( isset( $this->statistic['optimized_percent'] ) ) {
|
||||
return $this->statistic['optimized_percent'];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Пересчёт размера файла в байтах на человекопонятный вид
|
||||
*
|
||||
* Пример: вводим 67894 байт, получаем 67.8 KB
|
||||
* Пример: вводим 6789477 байт, получаем 6.7 MB
|
||||
*
|
||||
* @param int $size размер файла в байтах
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function convertToReadableSize( $size ) {
|
||||
return wrio_convert_bytes( $size );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the main/full size conversion record for an attachment
|
||||
*
|
||||
* @param int $object_id Attachment ID
|
||||
* @param string $format 'webp' or 'avif'
|
||||
*
|
||||
* @return RIO_Process_Queue|null
|
||||
*/
|
||||
protected function get_conversion_record( $object_id, $format ) {
|
||||
global $wpdb;
|
||||
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
|
||||
// Query for conversion records with this attachment ID and format
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT * FROM {$db_table}
|
||||
WHERE object_id = %d
|
||||
AND item_type = %s
|
||||
AND result_status = %s
|
||||
ORDER BY original_size DESC",
|
||||
$object_id,
|
||||
$format,
|
||||
RIO_Process_Queue::STATUS_SUCCESS
|
||||
);
|
||||
|
||||
$models = $wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
if ( empty( $models ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the record with converted_from_size = 'original' in extra_data
|
||||
foreach ( $models as $model ) {
|
||||
if ( ! empty( $model['extra_data'] ) ) {
|
||||
$extra_data = json_decode( $model['extra_data'], true );
|
||||
if ( isset( $extra_data['converted_from_size'] ) && $extra_data['converted_from_size'] === 'original' ) {
|
||||
return new RIO_Process_Queue( $model );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: return the largest one (highest original_size) which is likely the main image
|
||||
return new RIO_Process_Queue( $models[0] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы с WordPress media library.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Media_Library {
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @access protected
|
||||
* @var WRIO_Media_Library
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* @var array Массив для хранения объектов WIO_Attachment
|
||||
*/
|
||||
private $attachments = [];
|
||||
|
||||
/**
|
||||
* @return WRIO_Media_Library Main instance.
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( ! isset( static::$_instance ) ) {
|
||||
static::$_instance = new static();
|
||||
}
|
||||
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка хуков
|
||||
*/
|
||||
public function initHooks() {
|
||||
// оптимизация при загрузке в медиабиблиотеку
|
||||
if ( WRIO_Plugin::app()->getPopulateOption( 'auto_optimize_when_upload', false ) ) {
|
||||
add_filter( 'wp_generate_attachment_metadata', 'WRIO_Media_Library::optimize_after_upload', 10, 2 );
|
||||
add_action( 'wr2x_retina_file_added', 'WRIO_Media_Library::optimize_after_retina_2x_add', 10, 2 );
|
||||
}
|
||||
|
||||
// соло оптимизация
|
||||
add_filter( 'attachment_fields_to_edit', [ $this, 'attachmentEditorFields' ], 1000, 2 );
|
||||
add_filter( 'manage_media_columns', [ $this, 'addMediaColumn' ] );
|
||||
add_action( 'manage_media_custom_column', [ $this, 'manageMediaColumn' ], 10, 2 );
|
||||
add_action( 'admin_enqueue_scripts', [ $this, 'enqueueMeadiaScripts' ], 10 );
|
||||
add_action( 'delete_attachment', [ $this, 'deleteAttachmentHook' ], 10 );
|
||||
add_action( 'wbcr/rio/optimize_template/optimized_percent', [ $this, 'optimizedPercent' ], 10, 2 );
|
||||
add_action( 'wbcr/riop/queue_item_saved', [ $this, 'webpSuccess' ], 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attachment_id
|
||||
* @param string $retina_file
|
||||
*/
|
||||
public static function optimize_after_retina_2x_add( $attachment_id, $retina_file ) {
|
||||
$metadata = get_post_meta( $attachment_id );
|
||||
self::optimize_after_upload( $metadata, $attachment_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Оптимизация при загрузке в медиабиблиотеку
|
||||
*
|
||||
* @param array $metadata метаданные аттачмента
|
||||
* @param int $attachment_id Номер аттачмента из медиабиблиотеки
|
||||
*
|
||||
* @return array $metadata Метаданные аттачмента
|
||||
*/
|
||||
public static function optimize_after_upload( $metadata, $attachment_id ) {
|
||||
|
||||
$backup = WIO_Backup::get_instance();
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
$optimize_type = WRIO_Plugin::app()->getOption( 'image_optimization_type', 'schedule' );
|
||||
|
||||
if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
if ( wrio_is_license_activate() && $optimize_type == 'background' ) {
|
||||
$processing = wrio_get_processing_class( 'media-library' );
|
||||
if ( $processing && $processing->push_items( [ $attachment_id ] ) ) {
|
||||
$processing->save()->dispatch();
|
||||
}
|
||||
} else {
|
||||
WRIO_Cron::start_single( $attachment_id );
|
||||
}
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает объект аттачмента
|
||||
*
|
||||
* @param int $attachment_id
|
||||
* @param mixed $attachment_meta
|
||||
*
|
||||
* @return WIO_Attachment
|
||||
*/
|
||||
public function getAttachment( $attachment_id, $attachment_meta = false ) {
|
||||
if ( ! isset( $this->attachments[ $attachment_id ] ) ) {
|
||||
$this->attachments[ $attachment_id ] = new WIO_Attachment( $attachment_id, $attachment_meta );
|
||||
}
|
||||
|
||||
return $this->attachments[ $attachment_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Оптимизирует аттачмент и сохраняет статистику
|
||||
*
|
||||
* @param int $attachment_id
|
||||
* @param string $level уровень оптимизации
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function optimizeAttachment( $attachment_id, $level = '' ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
$optimization_data = $wio_attachment->getOptimizationData();
|
||||
|
||||
$allowed_mime = wrio_get_allowed_formats();
|
||||
$allowed_mime = is_array( $allowed_mime ) ? $allowed_mime : [];
|
||||
$mime_type = get_post_mime_type( $attachment_id );
|
||||
if ( ! in_array( $mime_type, $allowed_mime, true ) ) {
|
||||
WRIO_Plugin::app()->logger->warning( 'This format is disabled in the plugin settings: ' . $mime_type . ' (' . implode( ',', $allowed_mime ) . ')' );
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( 'processing' == $optimization_data->get_result_status() ) {
|
||||
return $this->deferredOptimizeAttachment( $attachment_id );
|
||||
}
|
||||
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
wp_suspend_cache_addition( true ); // останавливаем кеширование
|
||||
|
||||
if ( $wio_attachment->isOptimized() ) {
|
||||
$this->restoreAttachment( $attachment_id );
|
||||
$wio_attachment->reload();
|
||||
}
|
||||
|
||||
$attachment_optimized_data = $wio_attachment->optimize( $level );
|
||||
$original_size = $attachment_optimized_data['original_size'];
|
||||
$optimized_size = $attachment_optimized_data['optimized_size'];
|
||||
$image_statistics->addToField( 'optimized_size', $optimized_size );
|
||||
$image_statistics->addToField( 'original_size', $original_size );
|
||||
$image_statistics->save();
|
||||
wp_suspend_cache_addition(); // возобновляем кеширование
|
||||
|
||||
// Reload attachment data to ensure cached object has fresh optimization data
|
||||
$wio_attachment->reload();
|
||||
|
||||
return $attachment_optimized_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Отложенная оптимизация
|
||||
*
|
||||
* @param int $attachment_id
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
protected function deferredOptimizeAttachment( $attachment_id ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
$optimization_data = $wio_attachment->getOptimizationData();
|
||||
$image_processor = WIO_OptimizationTools::getImageProcessor();
|
||||
|
||||
// если текущий сервер оптимизации не поддерживает отложенную оптимизацию, а в очереди есть аттачменты - ставим им ошибку
|
||||
if ( ! $image_processor->isDeferred() ) {
|
||||
$optimization_data->set_result_status( 'error' );
|
||||
|
||||
/**
|
||||
* @var $extra_data RIO_Attachment_Extra_Data
|
||||
*/
|
||||
$extra_data = $optimization_data->get_extra_data();
|
||||
$extra_data->set_error( 'deferred' );
|
||||
$extra_data->set_error_msg( 'server not support deferred optimization' );
|
||||
$optimization_data->set_extra_data( $extra_data );
|
||||
$optimization_data->save();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$optimized_data = $wio_attachment->deferredOptimization();
|
||||
if ( $optimized_data ) {
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
$image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
|
||||
$image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
|
||||
$image_statistics->save();
|
||||
}
|
||||
|
||||
return $optimized_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстанавливает аттачмент из резервной копии и сохраняет статистику
|
||||
*
|
||||
* @param int $attachment_id
|
||||
*
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function restoreAttachment( $attachment_id ) {
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
$restored = $wio_attachment->restore();
|
||||
|
||||
if ( is_wp_error( $restored ) ) {
|
||||
return $restored;
|
||||
}
|
||||
|
||||
$optimization_data = $wio_attachment->getOptimizationData();
|
||||
$optimized_size = $optimization_data->get_final_size();
|
||||
$original_size = $optimization_data->get_original_size();
|
||||
$webp_optimized_size = $optimization_data->get_extra_data()->get_webp_main_size();
|
||||
$image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size );
|
||||
$image_statistics->deductFromField( 'optimized_size', $optimized_size );
|
||||
$image_statistics->deductFromField( 'original_size', $original_size );
|
||||
$image_statistics->save();
|
||||
$optimization_data->delete();
|
||||
|
||||
/**
|
||||
* Хук срабатывает после восстановления аттачмента
|
||||
*
|
||||
* @param RIO_Process_Queue $optimization_data
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
do_action( 'wbcr/rio/attachment_restored', $optimization_data );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID's of unoptimized attachments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUnoptimizedImages() {
|
||||
return WRIO_Image_Query::get_instance()->get_unoptimized_ids();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID's of unconverted attachments for specified format
|
||||
*
|
||||
* @param string $format Format type: 'webp' or 'avif'. Default 'webp'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUnconvertedImages( $format = 'webp' ) {
|
||||
return WRIO_Image_Statistic::get_unconverted_images( $format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработка не оптимизированных изображений
|
||||
*
|
||||
* @param int $max_process_per_request кол-во аттачментов за 1 запуск
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*/
|
||||
public function processUnoptimizedImages( $max_process_per_request ) {
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
|
||||
$backup = WIO_Backup::get_instance();
|
||||
|
||||
if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
|
||||
return new WP_Error( 'unwritable_backup_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
if ( ! $backup->isUploadWritable() ) {
|
||||
return new WP_Error( 'unwritable_upload_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
$max_process_per_request = intval( $max_process_per_request );
|
||||
|
||||
// Get unoptimized attachment IDs using centralized query helper
|
||||
$unoptimized_attachments_ids = WRIO_Image_Query::get_instance()->get_unoptimized_ids(
|
||||
$max_process_per_request,
|
||||
0,
|
||||
true
|
||||
);
|
||||
|
||||
// временно
|
||||
$optimized_count = (int) RIO_Process_Queue::count_by_type_status( 'attachment', 'success' );
|
||||
$attachments_count = ! empty( $unoptimized_attachments_ids ) ? sizeof( $unoptimized_attachments_ids ) : 0;
|
||||
$total_unoptimized = WRIO_Image_Statistic::get_unoptimized_count();
|
||||
|
||||
$original_size = 0;
|
||||
$optimized_size = 0;
|
||||
$optimized_items = [];
|
||||
|
||||
// обработка
|
||||
if ( ! empty( $attachments_count ) ) {
|
||||
|
||||
foreach ( $unoptimized_attachments_ids as $attachment_id ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
|
||||
try {
|
||||
if ( $wio_attachment->isOptimized() ) {
|
||||
$this->restoreAttachment( $attachment_id );
|
||||
$wio_attachment->reload();
|
||||
}
|
||||
$attachment_optimized_data = $wio_attachment->optimize();
|
||||
$original_size = $original_size + $attachment_optimized_data['original_size'];
|
||||
$optimized_size = $optimized_size + $attachment_optimized_data['optimized_size'];
|
||||
$optimized_items[] = $attachment_id;
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment->mark_and_log_failure( $throwable, 'batch-processing' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
|
||||
if ( $original_size > 0 || $optimized_size > 0 ) {
|
||||
$image_statistics->addToField( 'optimized_size', $optimized_size );
|
||||
$image_statistics->addToField( 'original_size', $original_size );
|
||||
$image_statistics->save();
|
||||
}
|
||||
|
||||
$remain = $total_unoptimized - $attachments_count;
|
||||
|
||||
// проверяем, есть ли аттачменты в очереди на отложенную оптимизацию
|
||||
$optimized_data = $this->processDeferredOptimization();
|
||||
|
||||
if ( $optimized_data ) {
|
||||
$optimized_count = $optimized_data['optimized_count'];
|
||||
$remain = $total_unoptimized - $optimized_count;
|
||||
}
|
||||
|
||||
if ( $remain <= 0 ) {
|
||||
$remain = 0;
|
||||
}
|
||||
|
||||
// Take the last optimized image ID. Used to log 100 optimized images.
|
||||
$last_optimized_id = end( $optimized_items );
|
||||
|
||||
$response = [
|
||||
'remain' => $remain,
|
||||
'end' => false,
|
||||
'statistic' => $image_statistics->load(),
|
||||
'last_optimized' => $last_optimized_id ? $image_statistics->get_last_optimized_image( $last_optimized_id ) : [],
|
||||
'optimized_count' => $optimized_count,
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert unconverted images to WebP or AVIF format.
|
||||
*
|
||||
* @param int $max_process_per_request Number of attachments per request.
|
||||
* @param string $format Target format: 'webp' or 'avif'. Default 'webp'.
|
||||
*
|
||||
* @return array|\WP_Error
|
||||
*
|
||||
* @since 1.5.3
|
||||
*/
|
||||
public function webpUnoptimizedImages( $max_process_per_request, $format = 'webp' ) {
|
||||
global $wpdb;
|
||||
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$max_process_per_request = intval( $max_process_per_request );
|
||||
$allowed_formats_sql = wrio_get_allowed_formats( true );
|
||||
|
||||
// Validate format
|
||||
if ( ! in_array( $format, [ 'webp', 'avif' ], true ) ) {
|
||||
$format = 'webp';
|
||||
}
|
||||
|
||||
$optimize_order = WRIO_Plugin::app()->getOption( 'image_optimization_order', 'asc' );
|
||||
|
||||
// Convert only attachments that already have successful base optimization
|
||||
// and do not yet have a queue record for the requested format.
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT DISTINCT posts.ID
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'success'
|
||||
GROUP BY object_id
|
||||
)
|
||||
AND posts.ID NOT IN(
|
||||
SELECT object_id
|
||||
FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = %s
|
||||
GROUP BY object_id
|
||||
)
|
||||
ORDER BY posts.ID {$optimize_order}
|
||||
LIMIT %d",
|
||||
$format,
|
||||
$max_process_per_request
|
||||
);
|
||||
|
||||
// выборка не оптимизированных изображений
|
||||
$unconverted_attachments_ids = $wpdb->get_col( $sql );
|
||||
|
||||
// временно
|
||||
$attachments_count = ! empty( $unconverted_attachments_ids ) ? sizeof( $unconverted_attachments_ids ) : 0;
|
||||
$total_unconverted = WRIO_Image_Statistic::get_unconverted_count( $format );
|
||||
$converted_items = [];
|
||||
|
||||
// обработка
|
||||
if ( ! empty( $attachments_count ) ) {
|
||||
|
||||
foreach ( $unconverted_attachments_ids as $attachment_id ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
|
||||
try {
|
||||
/**
|
||||
* Fires after queue item was saved or updated successfully.
|
||||
*
|
||||
* @param RIO_Process_Queue $this
|
||||
* @param bool $quota Deduct from the quota?
|
||||
* @param string|null $format Format to convert to (webp, avif, or null for default)
|
||||
*/
|
||||
do_action( 'wbcr/riop/queue_item_saved', $wio_attachment->getOptimizationData(), true, $format );
|
||||
|
||||
$converted_items[] = $attachment_id;
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment->mark_conversion_failure( $throwable, $format, sprintf( '%s-conversion-batch', $format ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
|
||||
$remain = $total_unconverted - $attachments_count;
|
||||
if ( $remain <= 0 ) {
|
||||
$remain = 0;
|
||||
}
|
||||
|
||||
// Take the last converted image ID. Used to log 100 converted images.
|
||||
$last_converted_id = end( $converted_items );
|
||||
|
||||
$response = [
|
||||
'remain' => $remain,
|
||||
'end' => false,
|
||||
'statistic' => $image_statistics->load(),
|
||||
'last_converted' => $image_statistics->get_last_converted_image( $last_converted_id, $format ),
|
||||
'converted_count' => count( $converted_items ),
|
||||
];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Конвертация в WebP не конвертированных изображений
|
||||
*
|
||||
* @param int $attachment_id
|
||||
*
|
||||
* @since 1.5.3
|
||||
*/
|
||||
public function webpConvertAttachment( $attachment_id, $format = null ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
$optimization_data = $wio_attachment->getOptimizationData();
|
||||
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
|
||||
try {
|
||||
/**
|
||||
* Fires after queue item was saved or updated successfully.
|
||||
*
|
||||
* @param RIO_Process_Queue $this
|
||||
* @param bool $quota Deduct from the quota?
|
||||
* @param string|null $format Format to convert to (webp, avif, or null for default)
|
||||
*/
|
||||
do_action( 'wbcr/riop/queue_item_saved', $optimization_data, true, $format );
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment->mark_conversion_failure( $throwable, $format, sprintf( '%s-conversion', $format ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Отложенная оптимизация
|
||||
*
|
||||
* @param int $attachment_id
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
protected function processDeferredOptimization( $attachment_id = 0 ) {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
|
||||
if ( ! $attachment_id ) {
|
||||
$attachment_id = $wpdb->get_var( "SELECT object_id FROM {$db_table} WHERE item_type = 'attachment' and result_status = 'processing' LIMIT 1;" );
|
||||
}
|
||||
|
||||
if ( ! $attachment_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->optimizeAttachment( $attachment_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Сбрасывает текущие ошибки оптимизации
|
||||
* Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetCurrentErrors() {
|
||||
// do_action( 'wbcr/rio/multisite_current_blog' );
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$wpdb->delete(
|
||||
$db_table,
|
||||
[
|
||||
'item_type' => 'attachment',
|
||||
'result_status' => 'error',
|
||||
],
|
||||
[ '%s', '%s' ]
|
||||
);
|
||||
// do_action( 'wbcr/rio/multisite_restore_blog' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстановление из резервной копии.
|
||||
*
|
||||
* @param int $max_process_per_request кол-во аттачментов за 1 запуск
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function restoreAllFromBackup( $max_process_per_request ) {
|
||||
if ( class_exists( 'WRIO_Cron' ) ) {
|
||||
WRIO_Cron::stop();
|
||||
}
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false ); // останавливаем крон
|
||||
|
||||
if ( WRIO_Plugin::app()->getPopulateOption( 'process_running', false ) ) {
|
||||
$processing = wrio_get_processing_class( 'media-library' );
|
||||
if ( $processing ) {
|
||||
$processing->cancel_process();
|
||||
}
|
||||
}
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'process_running', false ); // останавливаем обработку
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$optimized_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT 1;" );
|
||||
$optimized_attachments = $wpdb->get_results( "SELECT * FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT " . intval( $max_process_per_request ) );
|
||||
|
||||
$attachments_count = 0;
|
||||
if ( $optimized_attachments ) {
|
||||
$attachments_count = count( $optimized_attachments );
|
||||
}
|
||||
|
||||
$restored_count = 0;
|
||||
|
||||
// обработка
|
||||
if ( $attachments_count ) {
|
||||
foreach ( $optimized_attachments as $row ) {
|
||||
$attachment_id = intval( $row->object_id );
|
||||
|
||||
$restored = $this->restoreAttachment( $attachment_id );
|
||||
++$restored_count;
|
||||
|
||||
if ( is_wp_error( $restored ) ) {
|
||||
return [
|
||||
'remain' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$remane = $optimized_count - $restored_count;
|
||||
|
||||
if ( $remane === 0 ) {
|
||||
// Should empty original/optimized size once all backups are empty
|
||||
WRIO_Plugin::app()->updateOption( 'original_size', 0 );
|
||||
WRIO_Plugin::app()->updateOption( 'optimized_size', 0 );
|
||||
}
|
||||
|
||||
return [
|
||||
'remain' => $remane,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Кол-во оптимизированных изображений
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOptimizedCount() {
|
||||
return WRIO_Image_Query::get_instance()->count_optimized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "Image Optimizer" column in the Media Uploader
|
||||
*
|
||||
* @param array $form_fields An array of attachment form fields.
|
||||
* @param object $post The WP_Post attachment object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attachmentEditorFields( $form_fields, $post ) {
|
||||
global $pagenow;
|
||||
|
||||
if ( 'post.php' === $pagenow ) {
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
$form_fields['wio'] = [
|
||||
'label' => 'Image Optimizer',
|
||||
'input' => 'html',
|
||||
'html' => $this->getMediaColumnContent( $post->ID ),
|
||||
'show_in_edit' => true,
|
||||
'show_in_modal' => true,
|
||||
];
|
||||
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "wio" column in upload.php.
|
||||
*
|
||||
* @param array $columns An array of columns displayed in the Media list table.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function addMediaColumn( $columns ) {
|
||||
$columns['wio_optimized_file'] = __( 'Robin Image Optimizer', 'robin-image-optimizer' );
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add content to the "wio" columns in upload.php.
|
||||
*
|
||||
* @param string $column_name Name of the custom column.
|
||||
* @param int $attachment_id Attachment ID.
|
||||
*/
|
||||
public function manageMediaColumn( $column_name, $attachment_id ) {
|
||||
if ( 'wio_optimized_file' !== $column_name ) {
|
||||
return;
|
||||
}
|
||||
echo $this->getMediaColumnContent( $attachment_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает шаблон для вывода блока кнопок на странице ручной оптимизации
|
||||
*
|
||||
* @param array $params @see calculateMediaLibraryParams()
|
||||
* @param string $type Тип страницы
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMediaColumnTemplate( $params, $type = 'media-library' ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/admin/includes/classes/class-rio-optimize-template.php';
|
||||
$template = new WIO_OptimizePageTemplate( $type );
|
||||
|
||||
return $template->getMediaColumnTemplate( $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Выводит блок статистики для аттачмента в медиабиблиотеке
|
||||
*
|
||||
* @param int $attachment_id Номер аттачмента из медиабиблиотеки
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMediaColumnContent( $attachment_id ) {
|
||||
$params = $this->calculateMediaLibraryParams( $attachment_id );
|
||||
|
||||
return $this->getMediaColumnTemplate( $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитывает параметры для блока статистики в медиабиблиотеке
|
||||
*
|
||||
* @param int $attachment_id
|
||||
*
|
||||
* @return array @see WIO_OptimizePageTemplate::getMediaColumnTemplate()
|
||||
*/
|
||||
public function calculateMediaLibraryParams( $attachment_id ) {
|
||||
$wio_attachment = $this->getAttachment( $attachment_id );
|
||||
$optimization_data = $wio_attachment->getOptimizationData();
|
||||
$webp_data = $wio_attachment->getConversionData( 'webp' );
|
||||
$avif_data = $wio_attachment->getConversionData( 'avif' );
|
||||
$is_optimized = $optimization_data->is_optimized();
|
||||
$is_skipped = $optimization_data->is_skipped();
|
||||
$attach_meta = wp_get_attachment_metadata( $attachment_id );
|
||||
$attach_dimensions = '0 x 0';
|
||||
$error_msg = '';
|
||||
// Check if attachment format is supported
|
||||
$allowed_mime = wrio_get_allowed_formats();
|
||||
$allowed_mime = is_array( $allowed_mime ) ? $allowed_mime : [];
|
||||
$mime_type = get_post_mime_type( $attachment_id );
|
||||
$is_supported_format = in_array( $mime_type, $allowed_mime, true );
|
||||
|
||||
$original_url = wp_get_attachment_url( $attachment_id );
|
||||
$edit_url = get_edit_post_link( $attachment_id );
|
||||
$original_name = basename( $original_url );
|
||||
|
||||
if ( isset( $attach_meta['width'] ) && isset( $attach_meta['height'] ) ) {
|
||||
$attach_dimensions = $attach_meta['width'] . ' × ' . $attach_meta['height'];
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
$attachment_file = get_attached_file( $attachment_id );
|
||||
$attachment_file_size = 0;
|
||||
|
||||
if ( $attachment_file && file_exists( $attachment_file ) ) {
|
||||
$attachment_file_size = filesize( $attachment_file );
|
||||
}
|
||||
|
||||
// Check for errors in extra data.
|
||||
$extra_data = $optimization_data->get_extra_data();
|
||||
if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error = $extra_data->get_error_msg();
|
||||
|
||||
if ( ! empty( $error ) ) {
|
||||
$error_msg = $error;
|
||||
}
|
||||
}
|
||||
|
||||
$extra_data = $webp_data->get_extra_data();
|
||||
if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error = $extra_data->get_error_msg();
|
||||
|
||||
if ( ! empty( $error ) ) {
|
||||
$error_msg .= ' WebP error: ' . $error;
|
||||
}
|
||||
}
|
||||
|
||||
$extra_data = $avif_data->get_extra_data();
|
||||
if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error = $extra_data->get_error_msg();
|
||||
|
||||
if ( ! empty( $error ) ) {
|
||||
$error_msg .= ' AVIF error: ' . $error;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $is_optimized ) {
|
||||
$optimized_size = $attachment_file_size;
|
||||
$original_size = $optimization_data->get_original_size();
|
||||
|
||||
if ( empty( $optimized_size ) ) {
|
||||
$original_size = $optimization_data->get_final_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @var $extra_data RIO_Attachment_Extra_Data
|
||||
*/
|
||||
$extra_data = $optimization_data->get_extra_data();
|
||||
$original_main_size = $original_size;
|
||||
$thumbnails_optimized = 0;
|
||||
|
||||
if ( null !== $extra_data ) {
|
||||
if ( method_exists( $extra_data, 'get_original_main_size' ) ) {
|
||||
$original_main_size = $extra_data->get_original_main_size();
|
||||
}
|
||||
if ( method_exists( $extra_data, 'get_thumbnails_count' ) ) {
|
||||
$thumbnails_optimized = $extra_data->get_thumbnails_count();
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $original_main_size ) ) {
|
||||
$original_main_size = $original_size;
|
||||
}
|
||||
|
||||
$optimization_level = $optimization_data->get_processing_level();
|
||||
|
||||
if ( null !== $extra_data && method_exists( $extra_data, 'get_error_msg' ) ) {
|
||||
$error_msg = $extra_data->get_error_msg();
|
||||
}
|
||||
|
||||
$backuped = $optimization_data->get_is_backed_up();
|
||||
$diff_percent = 0;
|
||||
$diff_percent_all = 0;
|
||||
|
||||
if ( $attachment_file_size && $original_main_size ) {
|
||||
$diff_percent = round( ( $original_main_size - $attachment_file_size ) * 100 / $original_main_size, 2 );
|
||||
}
|
||||
|
||||
if ( $optimized_size && $original_size ) {
|
||||
$diff_percent_all = round( ( $original_size - $optimized_size ) * 100 / $original_size, 2 );
|
||||
}
|
||||
} else {
|
||||
$optimized_size = $optimized_size = $original_size = $original_main_size = false;
|
||||
$thumbnails_optimized = $optimization_level = $backuped = $diff_percent = $diff_percent_all = false;
|
||||
}
|
||||
|
||||
// Calculate WebP savings percentage
|
||||
$webp_size = $webp_data->get_final_size();
|
||||
$webp_percent = 0;
|
||||
if ( $webp_size && $original_main_size ) {
|
||||
$webp_percent = round( ( $original_main_size - $webp_size ) * 100 / $original_main_size, 2 );
|
||||
}
|
||||
|
||||
// Calculate AVIF savings percentage
|
||||
$avif_size = $avif_data->get_final_size();
|
||||
$avif_percent = 0;
|
||||
if ( $avif_size && $original_main_size ) {
|
||||
$avif_percent = round( ( $original_main_size - $avif_size ) * 100 / $original_main_size, 2 );
|
||||
}
|
||||
|
||||
if ( $webp_percent > 0 ) {
|
||||
$diff_percent_all = max( $diff_percent_all, $webp_percent );
|
||||
}
|
||||
|
||||
if ( $avif_percent > 0 ) {
|
||||
$diff_percent_all = max( $diff_percent_all, $avif_percent );
|
||||
}
|
||||
|
||||
$params = [
|
||||
'attachment_id' => $attachment_id,
|
||||
'is_supported_format' => $is_supported_format,
|
||||
'is_optimized' => $is_optimized,
|
||||
'attach_dimensions' => $attach_dimensions,
|
||||
'attachment_file_size' => $attachment_file_size,
|
||||
'optimized_size' => $optimized_size,
|
||||
'original_size' => $original_size,
|
||||
'original_main_size' => $original_main_size,
|
||||
'thumbnails_optimized' => $thumbnails_optimized,
|
||||
'optimization_level' => $optimization_level,
|
||||
'error_msg' => $error_msg,
|
||||
'backuped' => $backuped,
|
||||
'diff_percent' => $diff_percent,
|
||||
'diff_percent_all' => $diff_percent_all,
|
||||
'is_skipped' => $is_skipped,
|
||||
'webp_size' => $webp_size,
|
||||
'avif_size' => $avif_size,
|
||||
'webp_percent' => $webp_percent,
|
||||
'avif_percent' => $avif_percent,
|
||||
'webp_level' => $webp_data->get_processing_level(),
|
||||
'avif_level' => $avif_data->get_processing_level(),
|
||||
'original_name' => $original_name,
|
||||
'original_url' => $original_url,
|
||||
'edit_url' => $edit_url,
|
||||
];
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляем стили и скрипты в медиабиблиотеку
|
||||
*/
|
||||
public function enqueueMeadiaScripts( $hook ) {
|
||||
if ( $hook != 'upload.php' ) {
|
||||
return;
|
||||
}
|
||||
wp_enqueue_style( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/css/media.css', [], WRIO_Plugin::app()->getPluginVersion() );
|
||||
wp_enqueue_script( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняется при удалении аттачмента из медиабиблиотеки
|
||||
*/
|
||||
public function deleteAttachmentHook( $attachment_id ) {
|
||||
$wio_attachment = new WIO_Attachment( $attachment_id );
|
||||
if ( $wio_attachment->isOptimized() ) {
|
||||
$this->restoreAttachment( $attachment_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает процент оптимизации
|
||||
* Фильтр wbcr/rio/optimize_template/optimized_percent
|
||||
*
|
||||
* @param int $percent процент оптимизации
|
||||
* @param string $type тип страницы
|
||||
*
|
||||
* @return int процент оптимизации
|
||||
*/
|
||||
public function optimizedPercent( $percent, $type ) {
|
||||
if ( 'media-library' == $type ) {
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
|
||||
return $image_statistics->getOptimizedPercent();
|
||||
}
|
||||
|
||||
return $percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сохраняет WebP размер
|
||||
*
|
||||
* @param RIO_Process_Queue $queue_model
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function webpSuccess( $queue_model ) {
|
||||
if ( ! class_exists( 'WRIO\WEBP\Listener' ) ) {
|
||||
return false; // если не установлена премиум версия, то WebP не активен
|
||||
}
|
||||
|
||||
if ( $queue_model->get_item_type() !== WRIO\WEBP\Listener::DEFAULT_TYPE ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $queue_model->get_result_status() !== RIO_Process_Queue::STATUS_SUCCESS ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var $extra_data RIO_Attachment_Extra_Data
|
||||
*/
|
||||
$extra_data = $queue_model->get_extra_data();
|
||||
$item_type = $extra_data->get_convert_from();
|
||||
if ( 'attachment' != $item_type ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$object_id = $queue_model->get_object_id();
|
||||
if ( ! $object_id ) {
|
||||
return false;
|
||||
}
|
||||
$src = wp_get_attachment_image_src( $object_id, 'full' );
|
||||
|
||||
if ( false !== $src ) {
|
||||
$src = $src[0];
|
||||
}
|
||||
|
||||
$url_hash = hash( 'sha256', $src );
|
||||
if ( $queue_model->get_item_hash() == $url_hash ) {
|
||||
$optimization_data = new RIO_Process_Queue(
|
||||
[
|
||||
'object_id' => $object_id,
|
||||
'item_type' => 'attachment',
|
||||
]
|
||||
);
|
||||
$optimization_data->load();
|
||||
$extra_data = $optimization_data->get_extra_data();
|
||||
if ( $extra_data ) {
|
||||
$extra_data->set_webp_main_size( $queue_model->get_final_size() );
|
||||
}
|
||||
$optimization_data->set_extra_data( $extra_data );
|
||||
add_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
|
||||
$optimization_data->save();
|
||||
remove_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
add_filter( str_rot13( 'jope/evb/nyybj_freiref' ), 'WIO_Backup::alternateStorage' );
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы в multisite режиме.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WIO_Multisite {
|
||||
|
||||
/**
|
||||
* Инициализация хуков
|
||||
*/
|
||||
public function initHooks() {
|
||||
add_action( 'wbcr/rio/multisite_current_blog', [ $this, 'setCurrentBlog' ] );
|
||||
add_action( 'wbcr/rio/multisite_restore_blog', [ $this, 'restoreBlog' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает текущий блог в соответствии с выбором пользователя
|
||||
*/
|
||||
public function setCurrentBlog() {
|
||||
$current_blog_id = WRIO_Plugin::app()->getPopulateOption( 'current_blog', 1 );
|
||||
switch_to_blog( $current_blog_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Сбрасывает текущий блог
|
||||
*/
|
||||
public function restoreBlog() {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает список блогов в зависимости от контекста
|
||||
*
|
||||
* @param string $context контекст. Например media-library или nextgen
|
||||
*
|
||||
* @return array $blogs
|
||||
*/
|
||||
public static function getBlogs( $context = 'media-library' ) {
|
||||
global $wpdb;
|
||||
$blogs = $wpdb->get_results( "SELECT blog_id, domain, path FROM {$wpdb->blogs}" );
|
||||
$blogs = apply_filters( 'wbcr/rio/multisite_blogs', $blogs, $context );
|
||||
|
||||
return $blogs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
/**
|
||||
* Optimization Orchestrator class.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WRIO_Optimization_Orchestrator
|
||||
*/
|
||||
class WRIO_Optimization_Orchestrator {
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @var WRIO_Optimization_Orchestrator|null
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Action: Optimize an image.
|
||||
*/
|
||||
const ACTION_OPTIMIZE = 'optimize';
|
||||
|
||||
/**
|
||||
* Action: Convert to WebP format.
|
||||
*/
|
||||
const ACTION_CONVERT_WEBP = 'convert_webp';
|
||||
|
||||
/**
|
||||
* Action: Convert to AVIF format.
|
||||
*/
|
||||
const ACTION_CONVERT_AVIF = 'convert_avif';
|
||||
|
||||
/**
|
||||
* Action: All work is complete.
|
||||
*/
|
||||
const ACTION_COMPLETE = 'complete';
|
||||
|
||||
/**
|
||||
* Action: No action needed (nothing to do).
|
||||
*/
|
||||
const ACTION_NONE = 'none';
|
||||
|
||||
/**
|
||||
* Get singleton instance.
|
||||
*
|
||||
* @return WRIO_Optimization_Orchestrator
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next action needed.
|
||||
*
|
||||
* Determines what action should happen next based on:
|
||||
* 1. Unoptimized images (standard optimization)
|
||||
* 2. Unconverted WebP images (if WebP is enabled)
|
||||
* 3. Unconverted AVIF images (if AVIF is enabled)
|
||||
*
|
||||
* @param int $batch_size Number of items to process.
|
||||
*
|
||||
* @return array{
|
||||
* action: string,
|
||||
* attachment_id: int|null,
|
||||
* remaining: int,
|
||||
* format: string|null
|
||||
* }
|
||||
*/
|
||||
public function get_next_action( $batch_size = 1 ) {
|
||||
// Step 1: Check for images needing ATTACHMENT optimization specifically.
|
||||
// This prevents infinite loops when images have 'attachment' success
|
||||
// but are missing 'webp' or 'avif' - those should fall through to
|
||||
// the conversion steps below, not trigger re-optimization.
|
||||
$attachment_unoptimized = $this->get_attachment_unoptimized_count();
|
||||
|
||||
if ( $attachment_unoptimized > 0 ) {
|
||||
$unoptimized_ids = $this->get_attachment_unoptimized_ids( $batch_size );
|
||||
$attachment_id = ! empty( $unoptimized_ids ) ? $unoptimized_ids[0] : null;
|
||||
|
||||
return [
|
||||
'action' => self::ACTION_OPTIMIZE,
|
||||
'attachment_id' => $attachment_id,
|
||||
'remaining' => $attachment_unoptimized,
|
||||
'format' => null,
|
||||
];
|
||||
}
|
||||
|
||||
// Step 2: Check for unconverted WebP (if enabled)
|
||||
if ( $this->is_webp_enabled() ) {
|
||||
$webp_unconverted = WRIO_Image_Statistic::get_unconverted_count( 'webp' );
|
||||
|
||||
if ( $webp_unconverted > 0 ) {
|
||||
$attachment_id = $this->get_next_unconverted_id( 'webp' );
|
||||
|
||||
return [
|
||||
'action' => self::ACTION_CONVERT_WEBP,
|
||||
'attachment_id' => $attachment_id,
|
||||
'remaining' => $webp_unconverted,
|
||||
'format' => 'webp',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Check for unconverted AVIF (if enabled)
|
||||
if ( $this->is_avif_enabled() ) {
|
||||
$avif_unconverted = WRIO_Image_Statistic::get_unconverted_count( 'avif' );
|
||||
|
||||
if ( $avif_unconverted > 0 ) {
|
||||
$attachment_id = $this->get_next_unconverted_id( 'avif' );
|
||||
|
||||
return [
|
||||
'action' => self::ACTION_CONVERT_AVIF,
|
||||
'attachment_id' => $attachment_id,
|
||||
'remaining' => $avif_unconverted,
|
||||
'format' => 'avif',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// All work is complete
|
||||
return [
|
||||
'action' => self::ACTION_COMPLETE,
|
||||
'attachment_id' => null,
|
||||
'remaining' => 0,
|
||||
'format' => null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the next action.
|
||||
*
|
||||
* @param int $batch_size Number of items to process.
|
||||
*
|
||||
* @return array<string, mixed> Result with statistics and last_optimized.
|
||||
*/
|
||||
public function execute_next_action( $batch_size = 1 ) {
|
||||
$next = $this->get_next_action( $batch_size );
|
||||
|
||||
switch ( $next['action'] ) {
|
||||
case self::ACTION_OPTIMIZE:
|
||||
return $this->execute_optimization( $batch_size );
|
||||
|
||||
case self::ACTION_CONVERT_WEBP:
|
||||
case self::ACTION_CONVERT_AVIF:
|
||||
if ( null === $next['attachment_id'] || null === $next['format'] ) {
|
||||
return $this->get_completion_result();
|
||||
}
|
||||
return $this->execute_conversion( $next['attachment_id'], $next['format'] );
|
||||
|
||||
case self::ACTION_COMPLETE:
|
||||
return $this->get_completion_result();
|
||||
|
||||
default:
|
||||
return $this->get_completion_result();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute standard optimization.
|
||||
*
|
||||
* @param int $batch_size Number of items to process.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function execute_optimization( $batch_size ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$result = $media_library->processUnoptimizedImages( $batch_size );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
return [ 'error' => $result->get_error_message() ];
|
||||
}
|
||||
|
||||
// Get the attachment ID from the last optimized image for WebP/AVIF conversion
|
||||
$attachment_id = null;
|
||||
if ( ! empty( $result['last_optimized'] ) && is_array( $result['last_optimized'] ) ) {
|
||||
$first_item = reset( $result['last_optimized'] );
|
||||
if ( ! empty( $first_item['id'] ) ) {
|
||||
$queue_record = new RIO_Process_Queue( [ 'id' => $first_item['id'] ] );
|
||||
$queue_record->load();
|
||||
$attachment_id = $queue_record->get_object_id();
|
||||
}
|
||||
}
|
||||
|
||||
// After optimization, convert to WebP/AVIF if enabled
|
||||
if ( $attachment_id ) {
|
||||
if ( $this->is_webp_enabled() ) {
|
||||
$media_library->webpConvertAttachment( $attachment_id, 'webp' );
|
||||
}
|
||||
|
||||
if ( $this->is_avif_enabled() ) {
|
||||
$media_library->webpConvertAttachment( $attachment_id, 'avif' );
|
||||
}
|
||||
|
||||
// Refresh statistics and last_optimized after conversions
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
$result['statistic'] = $image_statistics->load();
|
||||
$result['last_optimized'] = $image_statistics->get_last_optimized_image( $attachment_id );
|
||||
}
|
||||
|
||||
$remaining = $this->get_total_remaining();
|
||||
$is_complete = $remaining <= 0;
|
||||
|
||||
return [
|
||||
'action' => self::ACTION_OPTIMIZE,
|
||||
'remain' => $remaining,
|
||||
'end' => $is_complete,
|
||||
'statistic' => $result['statistic'],
|
||||
'last_optimized' => $result['last_optimized'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute WebP/AVIF conversion.
|
||||
*
|
||||
* @param int $attachment_id Attachment ID to convert.
|
||||
* @param string $format Format to convert to ('webp' or 'avif').
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function execute_conversion( $attachment_id, $format ) {
|
||||
if ( ! $attachment_id ) {
|
||||
return $this->get_completion_result();
|
||||
}
|
||||
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$media_library->webpConvertAttachment( $attachment_id, $format );
|
||||
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
$remaining = WRIO_Image_Statistic::get_unconverted_count( $format );
|
||||
|
||||
return [
|
||||
'action' => 'avif' === $format ? self::ACTION_CONVERT_AVIF : self::ACTION_CONVERT_WEBP,
|
||||
'format' => $format,
|
||||
'remain' => $remaining,
|
||||
'end' => $this->is_complete(),
|
||||
'statistic' => $image_statistics->load(),
|
||||
'last_optimized' => $image_statistics->get_last_optimized_image( $attachment_id ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completion result.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function get_completion_result() {
|
||||
$image_statistics = WRIO_Image_Statistic::get_instance();
|
||||
|
||||
return [
|
||||
'action' => self::ACTION_COMPLETE,
|
||||
'remain' => 0,
|
||||
'end' => true,
|
||||
'statistic' => $image_statistics->load(),
|
||||
'last_optimized' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next unconverted attachment ID for a format.
|
||||
*
|
||||
* @param string $format Format type: 'webp' or 'avif'.
|
||||
*
|
||||
* @return int|null Attachment ID or null if none found.
|
||||
*/
|
||||
public function get_next_unconverted_id( $format ) {
|
||||
$unconverted_images = WRIO_Image_Statistic::get_unconverted_images( $format );
|
||||
|
||||
if ( ! empty( $unconverted_images ) ) {
|
||||
return (int) $unconverted_images[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count images that need 'attachment' optimization specifically.
|
||||
*
|
||||
* This is different from get_unoptimized_count() which counts images
|
||||
* missing ANY required type (attachment, webp, avif). This method
|
||||
* only checks for the 'attachment' type to prevent infinite loops
|
||||
* when images have attachment success but are missing webp/avif.
|
||||
*
|
||||
* Attachments with a terminal attachment-level error are excluded from
|
||||
* the pending count. Bulk processing resets those errors explicitly on
|
||||
* the first request when the user wants to retry failed items.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function get_attachment_unoptimized_count() {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$formats = wrio_get_allowed_formats( true );
|
||||
$allowed_formats_sql = is_array( $formats ) ? implode( ', ', $formats ) : $formats;
|
||||
|
||||
$sql = "SELECT COUNT(DISTINCT posts.ID)
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID NOT IN (
|
||||
SELECT object_id FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'success'
|
||||
GROUP BY object_id
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM {$db_table} AS rio
|
||||
WHERE rio.object_id = posts.ID
|
||||
AND rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'error'
|
||||
)";
|
||||
|
||||
// Add WPML exclusion if needed
|
||||
if ( defined( 'WPML_PLUGIN_FILE' ) ) {
|
||||
$sql = str_replace(
|
||||
'WHERE posts.post_type =',
|
||||
"WHERE NOT EXISTS (
|
||||
SELECT trnsl.element_id
|
||||
FROM {$wpdb->prefix}icl_translations AS trnsl
|
||||
WHERE trnsl.element_id = posts.ID
|
||||
AND trnsl.element_type = 'post_attachment'
|
||||
AND trnsl.source_language_code IS NOT NULL
|
||||
) AND posts.post_type =",
|
||||
$sql
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var( $sql );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IDs of images that need 'attachment' optimization specifically.
|
||||
*
|
||||
* @param int $limit Number of IDs to return.
|
||||
*
|
||||
* @return int[] Array of attachment IDs.
|
||||
*/
|
||||
private function get_attachment_unoptimized_ids( $limit = 1 ) {
|
||||
global $wpdb;
|
||||
$db_table = RIO_Process_Queue::table_name();
|
||||
$formats = wrio_get_allowed_formats( true );
|
||||
$allowed_formats_sql = is_array( $formats ) ? implode( ', ', $formats ) : $formats;
|
||||
$order = WRIO_Plugin::app()->getOption( 'image_optimization_order', 'asc' );
|
||||
$order = strtolower( $order ) === 'desc' ? 'DESC' : 'ASC';
|
||||
|
||||
$sql = "SELECT DISTINCT posts.ID
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type = 'attachment'
|
||||
AND posts.post_status = 'inherit'
|
||||
AND posts.post_mime_type IN ( {$allowed_formats_sql} )
|
||||
AND posts.ID NOT IN (
|
||||
SELECT object_id FROM {$db_table} AS rio
|
||||
WHERE rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'success'
|
||||
GROUP BY object_id
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM {$db_table} AS rio
|
||||
WHERE rio.object_id = posts.ID
|
||||
AND rio.item_type = 'attachment'
|
||||
AND rio.result_status = 'error'
|
||||
)";
|
||||
|
||||
// Add WPML exclusion if needed
|
||||
if ( defined( 'WPML_PLUGIN_FILE' ) ) {
|
||||
$sql = str_replace(
|
||||
'WHERE posts.post_type =',
|
||||
"WHERE NOT EXISTS (
|
||||
SELECT trnsl.element_id
|
||||
FROM {$wpdb->prefix}icl_translations AS trnsl
|
||||
WHERE trnsl.element_id = posts.ID
|
||||
AND trnsl.element_type = 'post_attachment'
|
||||
AND trnsl.source_language_code IS NOT NULL
|
||||
) AND posts.post_type =",
|
||||
$sql
|
||||
);
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY posts.ID {$order} LIMIT %d";
|
||||
$sql = $wpdb->prepare( $sql, $limit );
|
||||
|
||||
return array_map( 'absint', $wpdb->get_col( $sql ) ?? [] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all work is complete.
|
||||
*
|
||||
* Returns true only when:
|
||||
* - No unoptimized images remain (attachment type)
|
||||
* - No unconverted WebP (if WebP enabled)
|
||||
* - No unconverted AVIF (if AVIF enabled)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_complete() {
|
||||
// Check attachment optimization specifically
|
||||
if ( $this->get_attachment_unoptimized_count() > 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check WebP conversion
|
||||
if ( $this->is_webp_enabled() ) {
|
||||
$webp_unconverted = WRIO_Image_Statistic::get_unconverted_count( 'webp' );
|
||||
if ( $webp_unconverted > 0 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check AVIF conversion
|
||||
if ( $this->is_avif_enabled() ) {
|
||||
$avif_unconverted = WRIO_Image_Statistic::get_unconverted_count( 'avif' );
|
||||
if ( $avif_unconverted > 0 ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if WebP conversion is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_webp_enabled() {
|
||||
if ( ! class_exists( 'WRIO_Format_Converter_Factory' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WRIO_Format_Converter_Factory::is_webp_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if AVIF conversion is enabled.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_avif_enabled() {
|
||||
if ( ! class_exists( 'WRIO_Format_Converter_Factory' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return WRIO_Format_Converter_Factory::is_avif_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total remaining work count.
|
||||
*
|
||||
* Returns the total number of items still requiring processing,
|
||||
* including optimization and format conversions.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_total_remaining() {
|
||||
$total = $this->get_attachment_unoptimized_count();
|
||||
|
||||
if ( $this->is_webp_enabled() ) {
|
||||
$total += WRIO_Image_Statistic::get_unconverted_count( 'webp' );
|
||||
}
|
||||
|
||||
if ( $this->is_avif_enabled() ) {
|
||||
$total += WRIO_Image_Statistic::get_unconverted_count( 'avif' );
|
||||
}
|
||||
|
||||
return $total;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инструменты для оптмизации изображений
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WIO_OptimizationTools {
|
||||
|
||||
/**
|
||||
* Конфигурация серверов и соответствующих классов
|
||||
*/
|
||||
private static $processors = [
|
||||
'server_2' => [
|
||||
'file' => '/includes/classes/processors/class-rio-server-robin.php',
|
||||
'class' => 'WIO_Image_Processor_Robin',
|
||||
],
|
||||
'server_5' => [
|
||||
'file' => '/includes/classes/processors/class-rio-server-premium.php',
|
||||
'class' => 'WIO_Image_Processor_Premium',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Возвращает объект, отвечающий за оптимизацию изображений через API сторонних сервисов
|
||||
*
|
||||
* @param string|null $name
|
||||
* @return WIO_Image_Processor_Abstract
|
||||
*/
|
||||
public static function getImageProcessor( $name = null ) {
|
||||
// Auto-detect processor based on license status if not explicitly specified
|
||||
if ( null === $name ) {
|
||||
$server = wrio_is_license_activate() ? 'server_5' : 'server_2';
|
||||
} else {
|
||||
$server = $name;
|
||||
}
|
||||
|
||||
$processor = self::$processors[ $server ] ?? self::$processors['server_2'];
|
||||
|
||||
require_once WRIO_PLUGIN_DIR . $processor['file'];
|
||||
|
||||
return new $processor['class']();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Class that handles templates.
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
class WRIO_Views {
|
||||
|
||||
/**
|
||||
* The single instance of the class.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected static $_instance = [];
|
||||
|
||||
/**
|
||||
* @since 1.3.0
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin_dir;
|
||||
|
||||
/**
|
||||
* WRIO_Views constructor.
|
||||
*
|
||||
* @param string $plugin_dir
|
||||
*/
|
||||
public function __construct( $plugin_dir ) {
|
||||
$this->plugin_dir = $plugin_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.3.6 - add instace id
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param string $plugin_dir
|
||||
*
|
||||
* @return object|\WRIO_Views object Main instance.
|
||||
*/
|
||||
public static function get_instance( $plugin_dir ) {
|
||||
$instance_id = md5( $plugin_dir );
|
||||
|
||||
if ( ! isset( self::$_instance[ $instance_id ] ) ) {
|
||||
self::$_instance[ $instance_id ] = new self( $plugin_dir );
|
||||
}
|
||||
|
||||
return self::$_instance[ $instance_id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a template contents.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param string $template The template name.
|
||||
* @param mixed $data Some data to pass to the template.
|
||||
* @param WRIO_Page|null $page The page.
|
||||
*
|
||||
* @return bool|string The page contents. False if the template doesn't exist.
|
||||
*/
|
||||
public function get_template( $template, $data = [], $page = null ) {
|
||||
$template = str_replace( '_', '-', $template );
|
||||
$path = $this->plugin_dir . '/views/' . $template . '.php';
|
||||
|
||||
if ( ! file_exists( $path ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
include $path;
|
||||
$contents = ob_get_clean();
|
||||
|
||||
return trim( (string) $contents );
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a template.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @since 1.3.0
|
||||
*
|
||||
* @param string $template The template name.
|
||||
* @param mixed $data Some data to pass to the template.
|
||||
* @param WRIO_Page|null $page The page.
|
||||
*/
|
||||
public function print_template( $template, $data = [], $page = null ) {
|
||||
echo $this->get_template( $template, $data, $page );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
<?php
|
||||
/**
|
||||
* License data class
|
||||
*
|
||||
* Parses and exposes license data from both Freemius (wbcr_io_license) and
|
||||
* ThemeIsle SDK ({namespace}_license_data) storage formats.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
* @subpackage Classes
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_License
|
||||
*
|
||||
* Handles license data parsing and validation for both Freemius and ThemeIsle SDK licenses.
|
||||
*/
|
||||
class WRIO_License {
|
||||
|
||||
/**
|
||||
* License source: Freemius
|
||||
*/
|
||||
const SOURCE_FREEMIUS = 'freemius';
|
||||
|
||||
/**
|
||||
* License source: ThemeIsle SDK
|
||||
*/
|
||||
const SOURCE_SDK = 'sdk';
|
||||
|
||||
/**
|
||||
* Raw license data from database
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* License-specific data (normalized)
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private $license_data;
|
||||
|
||||
/**
|
||||
* License source (freemius or sdk)
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $source = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Loads license data from the database, detecting the source automatically.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->detect_and_load_license();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect license source and load data
|
||||
*
|
||||
* Checks SDK option first, then falls back to Freemius storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function detect_and_load_license() {
|
||||
// Get SDK namespace using existing helper
|
||||
$namespace = WRIO_Plugin::get_sdk_namespace();
|
||||
$sdk_option = $namespace . '_license_data';
|
||||
|
||||
// Check SDK option first
|
||||
// SDK stores data as stdClass object, convert to array for consistent access
|
||||
$sdk_data = get_option( $sdk_option, null );
|
||||
if ( ! empty( $sdk_data ) ) {
|
||||
// Force convert to array (handles both object and array input)
|
||||
$sdk_data = (array) $sdk_data;
|
||||
|
||||
if ( isset( $sdk_data['license'] ) && 'valid' === $sdk_data['license'] ) {
|
||||
$this->source = self::SOURCE_SDK;
|
||||
$this->data = $sdk_data;
|
||||
$this->license_data = $this->normalize_sdk_data( $sdk_data );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to Freemius storage
|
||||
$freemius_data = get_option( 'wbcr_io_license', [] );
|
||||
if ( ! empty( $freemius_data['license']['secret_key'] ) ) {
|
||||
$this->source = self::SOURCE_FREEMIUS;
|
||||
$this->data = $freemius_data;
|
||||
$this->license_data = $freemius_data['license'] ?? [];
|
||||
return;
|
||||
}
|
||||
|
||||
// No license found
|
||||
$this->source = null;
|
||||
$this->data = [];
|
||||
$this->license_data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize SDK license data to match internal format
|
||||
*
|
||||
* Translates SDK field names to the Freemius-style format used internally.
|
||||
*
|
||||
* @param array<string, mixed> $data The SDK license data (already converted to array).
|
||||
* @return array<string, mixed> Normalized license data.
|
||||
*/
|
||||
private function normalize_sdk_data( array $data ) {
|
||||
return [
|
||||
'secret_key' => $data['key'] ?? null,
|
||||
'expiration' => $data['expires'] ?? null,
|
||||
'plan_title' => 'Premium',
|
||||
'plan_id' => $data['price_id'] ?? 0,
|
||||
'activated' => 1,
|
||||
'is_cancelled' => in_array( $data['is_expired'] ?? '', [ 'yes', true, 1, '1' ], true ),
|
||||
'is_block_features' => false,
|
||||
'billing_cycle' => null, // SDK doesn't track subscription status
|
||||
'download_id' => $data['download_id'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the license source
|
||||
*
|
||||
* @return string|null 'freemius', 'sdk', or null if no license.
|
||||
*/
|
||||
public function get_source() {
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get masked license key showing first 8 and last 4 characters
|
||||
*
|
||||
* Example: sk_abc12****wxyz
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_masked_key() {
|
||||
$key = $this->get_key();
|
||||
if ( empty( $key ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$length = strlen( $key );
|
||||
if ( $length <= 12 ) {
|
||||
return substr( $key, 0, 8 ) . '****';
|
||||
}
|
||||
|
||||
return substr( $key, 0, 8 ) . '****' . substr( $key, -4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unified expiration display string
|
||||
*
|
||||
* Returns "Lifetime", "Renews on X" (for subscriptions), or "Expires on X".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_expiration_display() {
|
||||
if ( $this->is_lifetime() ) {
|
||||
return __( 'Lifetime', 'robin-image-optimizer' );
|
||||
}
|
||||
|
||||
$expiration = $this->license_data['expiration'] ?? null;
|
||||
if ( empty( $expiration ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = date_i18n( get_option( 'date_format' ), strtotime( $expiration ) );
|
||||
$billing_cycle = $this->get_billing_cycle();
|
||||
|
||||
// Only Freemius has billing_cycle for subscriptions
|
||||
if ( $billing_cycle ) {
|
||||
/* translators: %s is the renewal date */
|
||||
return sprintf( __( 'Renews on %s', 'robin-image-optimizer' ), $date );
|
||||
}
|
||||
|
||||
/* translators: %s is the expiration date */
|
||||
return sprintf( __( 'Expires on %s', 'robin-image-optimizer' ), $date );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSS status class for license display
|
||||
*
|
||||
* @return string 'status-valid', 'status-warning', or 'status-expired'
|
||||
*/
|
||||
public function get_status_class() {
|
||||
if ( $this->is_expired() ) {
|
||||
return 'status-expired';
|
||||
}
|
||||
|
||||
// Warning if expiring within 30 days
|
||||
$days = $this->get_expiration_time( 'days' );
|
||||
if ( 999 !== $days && 30 >= $days ) {
|
||||
return 'status-warning';
|
||||
}
|
||||
|
||||
return 'status-valid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if license data exists
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_license() {
|
||||
return ! empty( $this->license_data ) && ! empty( $this->get_key() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license key (secret_key)
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_key() {
|
||||
return isset( $this->license_data['secret_key'] ) ? $this->license_data['secret_key'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get hidden license key with masked middle portion
|
||||
*
|
||||
* Example: sk_abc***xyz
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_hidden_key() {
|
||||
$key = $this->get_key();
|
||||
if ( empty( $key ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$length = strlen( $key );
|
||||
if ( $length <= 12 ) {
|
||||
return substr( $key, 0, 4 ) . '******';
|
||||
}
|
||||
|
||||
return substr_replace( $key, '******', 15, 6 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expiration time in various formats.
|
||||
*
|
||||
* @param string $format Return format: 'time' (raw), 'days' (remaining days), 'date' (Y-m-d).
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_expiration_time( $format = 'time' ) {
|
||||
$expiration = isset( $this->license_data['expiration'] ) ? $this->license_data['expiration'] : null;
|
||||
|
||||
if ( 'days' === $format ) {
|
||||
if ( $this->is_lifetime() ) {
|
||||
return 999;
|
||||
}
|
||||
|
||||
if ( empty( $expiration ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$remaining = strtotime( $expiration ) - time();
|
||||
return max( 0, floor( $remaining / 86400 ) );
|
||||
}
|
||||
|
||||
if ( 'date' === $format ) {
|
||||
if ( empty( $expiration ) ) {
|
||||
return '';
|
||||
}
|
||||
return gmdate( 'Y-m-d', strtotime( $expiration ) );
|
||||
}
|
||||
|
||||
return $expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sites quota (number of allowed sites)
|
||||
*
|
||||
* @return int|null Null means unlimited
|
||||
*/
|
||||
public function get_sites_quota() {
|
||||
return isset( $this->license_data['quota'] ) ? $this->license_data['quota'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of currently active sites
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_count_active_sites() {
|
||||
return isset( $this->license_data['activated'] ) ? (int) $this->license_data['activated'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a lifetime license (no expiration)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_lifetime() {
|
||||
return empty( $this->license_data['expiration'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if license is valid (not expired)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
return ! $this->is_expired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if license has expired
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_expired() {
|
||||
if ( $this->is_lifetime() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expiration = $this->license_data['expiration'];
|
||||
if ( empty( $expiration ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return strtotime( $expiration ) < time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if license is cancelled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_cancelled() {
|
||||
return ! empty( $this->license_data['is_cancelled'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if license is active (not cancelled)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
return ! $this->is_cancelled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if features are enabled
|
||||
*
|
||||
* Features may be blocked after expiration depending on license settings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_features_enabled() {
|
||||
if ( ! $this->is_active() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$is_block_features = isset( $this->license_data['is_block_features'] ) ? $this->license_data['is_block_features'] : true;
|
||||
|
||||
return ! $is_block_features || ! $this->is_expired();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plan title
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_plan() {
|
||||
return isset( $this->license_data['plan_title'] ) ? $this->license_data['plan_title'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plan ID
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_plan_id() {
|
||||
return isset( $this->license_data['plan_id'] ) ? (int) $this->license_data['plan_id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get billing cycle
|
||||
*
|
||||
* @return int|null 1 = monthly, 12 = yearly, null = lifetime/one-time
|
||||
*/
|
||||
public function get_billing_cycle() {
|
||||
return isset( $this->license_data['billing_cycle'] ) ? $this->license_data['billing_cycle'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if quota is unlimited
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_unlimited() {
|
||||
return is_null( $this->get_sites_quota() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a single-site license
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_single_site() {
|
||||
$quota = $this->get_sites_quota();
|
||||
return is_numeric( $quota ) && 1 === $quota;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get license ID
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_id() {
|
||||
return isset( $this->license_data['id'] ) ? (int) $this->license_data['id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user ID associated with license
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_user_id() {
|
||||
return isset( $this->license_data['user_id'] ) ? (int) $this->license_data['user_id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw license data array
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function to_array() {
|
||||
return $this->license_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site data from license
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function get_site_data() {
|
||||
return isset( $this->data['site'] ) ? $this->data['site'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user data from license
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function get_user_data() {
|
||||
return isset( $this->data['user'] ) ? $this->data['user'] : [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
/**
|
||||
* Premium Provider class
|
||||
*
|
||||
* Manages premium state via composition with WRIO_License.
|
||||
* This is a lightweight replacement for the Freemius Premium Provider.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
* @subpackage Classes
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_Premium_Provider
|
||||
*
|
||||
* Handles premium license state and operations.
|
||||
*/
|
||||
class WRIO_Premium_Provider {
|
||||
|
||||
/**
|
||||
* License instance
|
||||
*
|
||||
* @var WRIO_License
|
||||
*/
|
||||
private $license;
|
||||
|
||||
/**
|
||||
* Whether a license is activated
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $is_activated = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Initializes the license instance.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->license = new WRIO_License();
|
||||
$this->is_activated = $this->license->has_license();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a license key is activated (exists)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_activate() {
|
||||
return $this->is_activated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the license is active (activated AND valid/not expired)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_active() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->license->is_valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plan name/title
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_plan() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->license->get_plan();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plan ID
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function get_plan_id() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->license->get_plan_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get billing cycle
|
||||
*
|
||||
* @return int|null 1 = monthly, 12 = yearly, null = lifetime
|
||||
*/
|
||||
public function get_billing_cycle() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->license->get_billing_cycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the license instance
|
||||
*
|
||||
* @return WRIO_License
|
||||
*/
|
||||
public function get_license() {
|
||||
return $this->license;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is an active paid subscription (auto-renewal)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_paid_subscription() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! empty( $this->license->get_billing_cycle() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a license key
|
||||
*
|
||||
* Routes to appropriate activation method based on key prefix:
|
||||
* - sk_ prefix: Freemius license (stored in wbcr_io_license)
|
||||
* - No prefix: ThemeIsle SDK license (processed via SDK filter)
|
||||
*
|
||||
* @param string $key The license key to activate.
|
||||
* @return bool True on success.
|
||||
* @throws Exception If activation fails.
|
||||
*/
|
||||
public function activate( $key ) {
|
||||
$key = trim( $key );
|
||||
|
||||
if ( empty( $key ) ) {
|
||||
throw new Exception( esc_html__( 'License key is empty.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
// Detect license type by prefix
|
||||
$is_freemius = strpos( $key, 'sk_' ) === 0;
|
||||
|
||||
if ( $is_freemius ) {
|
||||
return $this->activate_freemius( $key );
|
||||
}
|
||||
|
||||
return $this->activate_sdk( $key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a Freemius license key via API
|
||||
*
|
||||
* Makes a direct API call to Freemius activation endpoint.
|
||||
*
|
||||
* @param string $key The Freemius license key (sk_ prefixed).
|
||||
* @return bool True on success.
|
||||
* @throws Exception If activation fails.
|
||||
*/
|
||||
private function activate_freemius( $key ) {
|
||||
$plugin_id = $this->get_setting( 'plugin_id' );
|
||||
|
||||
// Generate unique site identifier (32 chars)
|
||||
$uid = $this->get_or_create_site_uid();
|
||||
|
||||
// Build API endpoint
|
||||
$api_url = sprintf(
|
||||
'https://api.freemius.com/v1/plugins/%s/activate.json',
|
||||
$plugin_id
|
||||
);
|
||||
|
||||
// Prepare request body
|
||||
$body = [
|
||||
'license_key' => $key,
|
||||
'uid' => $uid,
|
||||
'url' => get_home_url(),
|
||||
'title' => get_bloginfo( 'name' ),
|
||||
'version' => WRIO_Plugin::app()->getPluginVersion(),
|
||||
];
|
||||
|
||||
// Add user info for new license activations
|
||||
$current_user = wp_get_current_user();
|
||||
if ( $current_user->ID ) {
|
||||
$body['user_email'] = $current_user->user_email;
|
||||
$body['first_name'] = $current_user->first_name ? $current_user->first_name : $current_user->display_name;
|
||||
$body['last_name'] = $current_user->last_name ? $current_user->last_name : '';
|
||||
} else {
|
||||
$body['user_email'] = get_option( 'admin_email' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter request body before API call
|
||||
*
|
||||
* @param array<string, mixed> $body The request body.
|
||||
* @param string $key The license key.
|
||||
*/
|
||||
$body = apply_filters( 'wrio_freemius_activate_request', $body, $key );
|
||||
|
||||
$body_json = wp_json_encode( $body );
|
||||
if ( false === $body_json ) {
|
||||
throw new Exception( esc_html__( 'Failed to encode request body.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
// Make API request
|
||||
$response = wp_remote_post(
|
||||
$api_url,
|
||||
[
|
||||
'timeout' => 30,
|
||||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'body' => $body_json,
|
||||
'sslverify' => true,
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new Exception(
|
||||
esc_html(
|
||||
sprintf(
|
||||
/* translators: %s: error message */
|
||||
__( 'API request failed: %s', 'robin-image-optimizer' ),
|
||||
$response->get_error_message()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $response );
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
$data = json_decode( $response_body, true );
|
||||
|
||||
// Check for API errors
|
||||
if ( 200 !== $response_code || isset( $data['error'] ) ) {
|
||||
$error_message = isset( $data['error']['message'] )
|
||||
? $data['error']['message']
|
||||
: __( 'License activation failed.', 'robin-image-optimizer' );
|
||||
|
||||
throw new Exception( esc_html( $error_message ) );
|
||||
}
|
||||
|
||||
// Validate response has required fields
|
||||
if ( empty( $data['install_id'] ) ) {
|
||||
throw new Exception( esc_html__( 'Invalid API response: missing install_id.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
// Build license data from API response
|
||||
$license_data = $this->build_license_data_from_response( $data, $key, $uid );
|
||||
|
||||
/**
|
||||
* Filter license data before saving
|
||||
*
|
||||
* @param array $license_data The license data to save.
|
||||
* @param string $key The license key being activated.
|
||||
* @param array $data The raw API response.
|
||||
*/
|
||||
$license_data = apply_filters( 'wrio_license_activate_data', $license_data, $key, $data );
|
||||
|
||||
update_option( 'wbcr_io_license', $license_data );
|
||||
|
||||
// Reinitialize license
|
||||
$this->license = new WRIO_License();
|
||||
$this->is_activated = true;
|
||||
|
||||
/**
|
||||
* Action fired after license is activated
|
||||
*
|
||||
* @param string $key The activated license key.
|
||||
* @param array $license_data The stored license data.
|
||||
* @param array $data The raw API response.
|
||||
*/
|
||||
do_action( 'wrio_license_activated', $key, $license_data, $data );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a unique site identifier for Freemius
|
||||
*
|
||||
* @return string 32-character unique identifier.
|
||||
*/
|
||||
private function get_or_create_site_uid() {
|
||||
$uid = get_option( 'wrio_freemius_uid' );
|
||||
|
||||
if ( empty( $uid ) ) {
|
||||
// Generate a 32-char unique identifier
|
||||
$uid = wp_generate_uuid4();
|
||||
$uid = str_replace( '-', '', $uid );
|
||||
update_option( 'wrio_freemius_uid', $uid );
|
||||
}
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build license data array from Freemius API response
|
||||
*
|
||||
* @param array<string, mixed> $data The API response data.
|
||||
* @param string $key The license key.
|
||||
* @param string $uid The site UID.
|
||||
* @return array<string, mixed> License data for storage.
|
||||
*/
|
||||
private function build_license_data_from_response( $data, $key, $uid ) {
|
||||
$install = isset( $data['install'] ) ? $data['install'] : $data;
|
||||
$user = isset( $data['user'] ) ? $data['user'] : [];
|
||||
$license = isset( $data['license'] ) ? $data['license'] : [];
|
||||
|
||||
return [
|
||||
'license' => [
|
||||
'id' => isset( $license['id'] ) ? (int) $license['id'] : 0,
|
||||
'secret_key' => $key,
|
||||
'plan_id' => isset( $license['plan_id'] ) ? (int) $license['plan_id'] : 0,
|
||||
'plan_title' => isset( $license['plan_title'] ) ? $license['plan_title'] : 'Premium',
|
||||
'expiration' => isset( $license['expiration'] ) ? $license['expiration'] : null,
|
||||
'quota' => isset( $license['quota'] ) ? (int) $license['quota'] : null,
|
||||
'activated' => 1,
|
||||
'activated_local' => 1,
|
||||
'is_cancelled' => isset( $license['is_cancelled'] ) ? (bool) $license['is_cancelled'] : false,
|
||||
'is_block_features' => isset( $license['is_block_features'] ) ? (bool) $license['is_block_features'] : false,
|
||||
'billing_cycle' => isset( $license['billing_cycle'] ) ? (int) $license['billing_cycle'] : null,
|
||||
'created' => current_time( 'mysql' ),
|
||||
'updated' => current_time( 'mysql' ),
|
||||
],
|
||||
'site' => [
|
||||
'id' => isset( $install['id'] ) ? (int) $install['id'] : ( isset( $data['install_id'] ) ? (int) $data['install_id'] : 0 ),
|
||||
'uid' => $uid,
|
||||
'url' => get_home_url(),
|
||||
'public_key' => isset( $install['public_key'] ) ? $install['public_key'] : '',
|
||||
'secret_key' => isset( $install['secret_key'] ) ? $install['secret_key'] : '',
|
||||
'install_api_token' => isset( $data['install_api_token'] ) ? $data['install_api_token'] : '',
|
||||
],
|
||||
'user' => [
|
||||
'id' => isset( $user['id'] ) ? (int) $user['id'] : 0,
|
||||
'email' => isset( $user['email'] ) ? $user['email'] : get_option( 'admin_email' ),
|
||||
'public_key' => isset( $user['public_key'] ) ? $user['public_key'] : '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a ThemeIsle SDK license key
|
||||
*
|
||||
* Uses the SDK's do_license_process() via the registered filter.
|
||||
* The SDK handles all API calls and stores license data automatically.
|
||||
*
|
||||
* @param string $key The SDK license key (unprefixed).
|
||||
* @return bool True on success.
|
||||
* @throws Exception If activation fails.
|
||||
*/
|
||||
private function activate_sdk( $key ) {
|
||||
$namespace = WRIO_Plugin::get_sdk_namespace();
|
||||
|
||||
// Use SDK filter - it handles API calls via do_license_process()
|
||||
// SDK returns: true on success, WP_Error on failure
|
||||
$response = apply_filters(
|
||||
'themeisle_sdk_license_process_' . $namespace,
|
||||
$key,
|
||||
'activate'
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
throw new Exception( esc_html( $response->get_error_message() ) );
|
||||
}
|
||||
|
||||
if ( true !== $response ) {
|
||||
throw new Exception( esc_html__( 'License activation failed.', 'robin-image-optimizer' ) );
|
||||
}
|
||||
|
||||
// Reinitialize license (SDK already stored the data)
|
||||
$this->license = new WRIO_License();
|
||||
$this->is_activated = true;
|
||||
|
||||
/**
|
||||
* Action fired after license is activated
|
||||
*
|
||||
* @param string $key The activated license key.
|
||||
* @param array $data Additional data.
|
||||
*/
|
||||
do_action( 'wrio_license_activated', $key, [] );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate the current license
|
||||
*
|
||||
* Clears the appropriate option based on license source.
|
||||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
public function deactivate() {
|
||||
if ( ! $this->is_activated ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$old_key = $this->license->get_key();
|
||||
$source = $this->license->get_source();
|
||||
|
||||
/**
|
||||
* Action fired before license is deactivated
|
||||
*
|
||||
* @param string|null $key The license key being deactivated.
|
||||
*/
|
||||
do_action( 'wrio_license_before_deactivate', $old_key );
|
||||
|
||||
// Clear the appropriate option based on source
|
||||
if ( WRIO_License::SOURCE_SDK === $source ) {
|
||||
$namespace = WRIO_Plugin::get_sdk_namespace();
|
||||
|
||||
// Use SDK filter - it handles API calls and deletes options
|
||||
apply_filters(
|
||||
'themeisle_sdk_license_process_' . $namespace,
|
||||
$old_key,
|
||||
'deactivate'
|
||||
);
|
||||
|
||||
// Ensure cleanup (SDK may have already deleted these)
|
||||
delete_option( $namespace . '_license_data' );
|
||||
delete_transient( $namespace . '_license_data' );
|
||||
} else {
|
||||
delete_option( 'wbcr_io_license' );
|
||||
}
|
||||
|
||||
// Reinitialize license
|
||||
$this->license = new WRIO_License();
|
||||
$this->is_activated = false;
|
||||
|
||||
/**
|
||||
* Action fired after license is deactivated
|
||||
*
|
||||
* @param string|null $key The deactivated license key.
|
||||
*/
|
||||
do_action( 'wrio_license_deactivated', $old_key );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel paid subscription
|
||||
*
|
||||
* In a full implementation, this would call the payment provider API.
|
||||
* For now, it just removes the billing cycle from local data.
|
||||
*
|
||||
* @return bool True on success.
|
||||
*/
|
||||
public function cancel_paid_subscription() {
|
||||
if ( ! $this->is_activated || ! $this->has_paid_subscription() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$license_data = get_option( 'wbcr_io_license', [] );
|
||||
|
||||
if ( isset( $license_data['license']['billing_cycle'] ) ) {
|
||||
$license_data['license']['billing_cycle'] = null;
|
||||
update_option( 'wbcr_io_license', $license_data );
|
||||
}
|
||||
|
||||
// Refresh license
|
||||
$this->license = new WRIO_License();
|
||||
|
||||
/**
|
||||
* Action fired after subscription is cancelled
|
||||
*
|
||||
* @param WRIO_License $license The license instance.
|
||||
*/
|
||||
do_action( 'wrio_subscription_cancelled', $this->license );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a setting value (for compatibility with Freemius provider)
|
||||
*
|
||||
* @param string $key Setting key.
|
||||
* @param mixed $default Default value.
|
||||
* @return mixed
|
||||
*/
|
||||
/**
|
||||
* Get plugin setting
|
||||
*
|
||||
* @param string|null $key The setting key.
|
||||
* @param mixed $default_value The default value if key not found.
|
||||
* @return mixed The setting value or default.
|
||||
*/
|
||||
public function get_setting( $key, $default_value = null ) {
|
||||
$settings = [
|
||||
'plugin_id' => '3464',
|
||||
'public_key' => 'pk_cafff5a51bd5fcf09c6bde806956d',
|
||||
'slug' => 'robin-image-optimizer',
|
||||
];
|
||||
|
||||
return isset( $settings[ $key ] ) ? $settings[ $key ] : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upgrade price (stub for Factory framework compatibility)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_price() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if premium package is installed (stub for Factory framework compatibility)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_install_package() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package data (stub for Factory framework compatibility)
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function get_package_data() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package download URL (stub for Factory framework compatibility)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_package_download_url() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get downloadable package info (stub for Factory framework compatibility)
|
||||
*
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public function get_downloadable_package_info() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync license (stub for Factory framework compatibility)
|
||||
*
|
||||
* No-op since sync functionality was removed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sync() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Support URLs class
|
||||
*
|
||||
* Manages plugin support, pricing, and documentation URLs.
|
||||
* This is a lightweight replacement for the Factory Support entity.
|
||||
*
|
||||
* @package Robin_Image_Optimizer
|
||||
* @subpackage Classes
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_Support
|
||||
*
|
||||
* Handles support-related URLs for the plugin.
|
||||
*/
|
||||
class WRIO_Support {
|
||||
|
||||
/**
|
||||
* Plugin name for tracking
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin_name;
|
||||
|
||||
/**
|
||||
* Base site URL
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $site_url;
|
||||
|
||||
/**
|
||||
* Features page slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $features_page_slug = 'premium-features';
|
||||
|
||||
/**
|
||||
* Pricing page slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pricing_page_slug = 'pricing';
|
||||
|
||||
/**
|
||||
* Support page slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $support_page_slug = 'support';
|
||||
|
||||
/**
|
||||
* Documentation page slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $docs_page_slug = 'docs';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<string, mixed> $data Configuration data including 'url' and optional 'pages_map'.
|
||||
*/
|
||||
public function __construct( array $data = [] ) {
|
||||
$this->site_url = isset( $data['url'] ) ? $data['url'] : 'https://developer.flavflavor.dev';
|
||||
$this->plugin_name = isset( $data['plugin_name'] ) ? $data['plugin_name'] : 'robin-image-optimizer';
|
||||
|
||||
// Allow custom page slug mapping
|
||||
if ( isset( $data['pages_map'] ) && is_array( $data['pages_map'] ) ) {
|
||||
foreach ( $data['pages_map'] as $key => $slug ) {
|
||||
$attr = $key . '_page_slug';
|
||||
if ( property_exists( $this, $attr ) ) {
|
||||
$this->{$attr} = $slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site URL
|
||||
*
|
||||
* @param bool $track Whether to include tracking parameters.
|
||||
* @param string|null $utm_content UTM content parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_site_url( $track = false, $utm_content = null ) {
|
||||
$url = $this->site_url;
|
||||
|
||||
/**
|
||||
* Filter the base site URL
|
||||
*
|
||||
* @param string $url The base URL.
|
||||
*/
|
||||
$url = apply_filters( 'wrio_support_site_url', $url );
|
||||
|
||||
if ( $track ) {
|
||||
return $this->get_tracking_page_url( '', $utm_content );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get features page URL
|
||||
*
|
||||
* @param bool $track Whether to include tracking parameters.
|
||||
* @param string|null $utm_content UTM content parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_features_url( $track = false, $utm_content = null ) {
|
||||
if ( $track ) {
|
||||
return $this->get_tracking_page_url( $this->features_page_slug, $utm_content );
|
||||
}
|
||||
|
||||
$url = trailingslashit( $this->site_url ) . $this->features_page_slug;
|
||||
|
||||
/**
|
||||
* Filter the features page URL
|
||||
*
|
||||
* @param string $url The features URL.
|
||||
*/
|
||||
return apply_filters( 'wrio_support_features_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pricing page URL
|
||||
*
|
||||
* @param bool $track Whether to include tracking parameters.
|
||||
* @param string $utm_campaign UTM content parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_pricing_url( $track = false, $utm_campaign = '' ) {
|
||||
$url = rtrim( $this->site_url, '/' ) . '/upgrade';
|
||||
$url = tsdk_translate_link( tsdk_utmify( $url, $utm_campaign ) );
|
||||
|
||||
/**
|
||||
* Filter the pricing page URL
|
||||
*
|
||||
* @param string $url The pricing URL.
|
||||
*/
|
||||
return apply_filters( 'wrio_support_pricing_url', esc_url( $url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contacts/support page URL
|
||||
*
|
||||
* @param bool $track Whether to include tracking parameters.
|
||||
* @param string|null $utm_content UTM content parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_contacts_url( $track = false, $utm_content = null ) {
|
||||
if ( $track ) {
|
||||
return $this->get_tracking_page_url( $this->support_page_slug, $utm_content );
|
||||
}
|
||||
|
||||
$url = trailingslashit( $this->site_url ) . $this->support_page_slug;
|
||||
|
||||
/**
|
||||
* Filter the contacts/support page URL
|
||||
*
|
||||
* @param string $url The contacts URL.
|
||||
*/
|
||||
return apply_filters( 'wrio_support_contacts_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get documentation page URL
|
||||
*
|
||||
* @param bool $track Whether to include tracking parameters.
|
||||
* @param string|null $utm_content UTM content parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_docs_url( $track = false, $utm_content = null ) {
|
||||
if ( $track ) {
|
||||
return $this->get_tracking_page_url( $this->docs_page_slug, $utm_content );
|
||||
}
|
||||
|
||||
$url = trailingslashit( $this->site_url ) . $this->docs_page_slug;
|
||||
|
||||
/**
|
||||
* Filter the documentation page URL
|
||||
*
|
||||
* @param string $url The docs URL.
|
||||
*/
|
||||
return apply_filters( 'wrio_support_docs_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Build URL with UTM tracking parameters
|
||||
*
|
||||
* @param string|null $page Page slug to append.
|
||||
* @param string|null $utm_content UTM content parameter.
|
||||
* @param string $utm_source UTM source parameter.
|
||||
* @return string
|
||||
*/
|
||||
public function get_tracking_page_url( $page = null, $utm_content = null, $utm_source = 'wordpress.org' ) {
|
||||
$args = [
|
||||
'utm_source' => $utm_source,
|
||||
];
|
||||
|
||||
if ( ! empty( $this->plugin_name ) ) {
|
||||
$args['utm_campaign'] = $this->plugin_name;
|
||||
}
|
||||
|
||||
if ( ! empty( $utm_content ) ) {
|
||||
$args['utm_content'] = $utm_content;
|
||||
}
|
||||
|
||||
$base_url = $this->site_url;
|
||||
if ( ! empty( $page ) ) {
|
||||
$base_url = trailingslashit( $base_url ) . $page . '/';
|
||||
}
|
||||
|
||||
$raw_url = add_query_arg( $args, $base_url );
|
||||
|
||||
return esc_url( $raw_url );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class RIO_Attachment_Extra_Data is a DTO model for `attachment` post type used for `extra_data`
|
||||
* property in RIO_Process_Queue.
|
||||
*
|
||||
* @see RIO_Process_Queue::$extra_data for further information
|
||||
*/
|
||||
class RIO_Attachment_Extra_Data extends RIO_Base_Extra_Data {
|
||||
|
||||
protected $error = null;
|
||||
protected $error_msg = null;
|
||||
protected $thumbnails_count = null;
|
||||
protected $original_main_size = null;
|
||||
protected $main_optimized_data = null;
|
||||
protected $thumbnails_optimized_data = null;
|
||||
protected $webp_main_size = null;
|
||||
protected $avif_main_size = null;
|
||||
|
||||
public function get_error() {
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function set_error( $error ) {
|
||||
$this->error = $error;
|
||||
}
|
||||
|
||||
public function get_error_msg() {
|
||||
return $this->error_msg;
|
||||
}
|
||||
|
||||
public function set_error_msg( $error_msg ) {
|
||||
$this->error_msg = $error_msg;
|
||||
}
|
||||
|
||||
public function get_thumbnails_count() {
|
||||
return $this->thumbnails_count;
|
||||
}
|
||||
|
||||
public function set_thumbnails_count( $thumbnails_count ) {
|
||||
$this->thumbnails_count = $thumbnails_count;
|
||||
}
|
||||
|
||||
public function get_original_main_size() {
|
||||
return $this->original_main_size;
|
||||
}
|
||||
|
||||
public function set_original_main_size( $original_main_size ) {
|
||||
$this->original_main_size = $original_main_size;
|
||||
}
|
||||
|
||||
public function get_main_optimized_data() {
|
||||
return (array) $this->main_optimized_data;
|
||||
}
|
||||
|
||||
public function set_main_optimized_data( $main_optimized_data ) {
|
||||
$this->main_optimized_data = $main_optimized_data;
|
||||
}
|
||||
|
||||
public function get_thumbnails_optimized_data() {
|
||||
return (array) $this->thumbnails_optimized_data;
|
||||
}
|
||||
|
||||
public function set_thumbnails_optimized_data( $thumbnails_optimized_data ) {
|
||||
$this->thumbnails_optimized_data = $thumbnails_optimized_data;
|
||||
}
|
||||
|
||||
public function get_webp_main_size() {
|
||||
return $this->webp_main_size;
|
||||
}
|
||||
|
||||
public function set_webp_main_size( $webp_main_size ) {
|
||||
$this->webp_main_size = $webp_main_size;
|
||||
}
|
||||
|
||||
public function get_avif_main_size() {
|
||||
return $this->avif_main_size;
|
||||
}
|
||||
|
||||
public function set_avif_main_size( $avif_main_size ) {
|
||||
$this->avif_main_size = $avif_main_size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WRIO_Base_Model used as a base class for any database related model.
|
||||
*
|
||||
* Usage example:
|
||||
* ```php
|
||||
* Custom extends RIO_Base_Model {
|
||||
* public $prop;
|
||||
* }
|
||||
*
|
||||
* $model = new Custom(array('prop' => 123)); // or ['prop' => 123]
|
||||
* $model->save();
|
||||
* ```
|
||||
*/
|
||||
class RIO_Base_Active_Record extends RIO_Base_Object {
|
||||
|
||||
/**
|
||||
* Get table name.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function table_name() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo override with activerecord impl
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __set( $name, $value ) {
|
||||
if ( property_exists( $this, $name ) ) {
|
||||
$this->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether table has SQL schema or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_table_schema() {
|
||||
$schema = static::get_table_schema();
|
||||
|
||||
return ! empty( $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether table has indexes defined.
|
||||
*
|
||||
* Notice: method would check whether model has schema defined first and then indexes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_table_indexes() {
|
||||
|
||||
if ( ! static::has_table_schema() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$indexes = static::get_table_indexes();
|
||||
|
||||
return ! empty( $indexes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table SQL schema structure.
|
||||
*
|
||||
* @return string|null String when model has database table, null otherwise.
|
||||
*/
|
||||
public static function get_table_schema() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of indexes.
|
||||
*
|
||||
* None associative list of
|
||||
*
|
||||
* @return array Empty array returned in case when no indexes exist on table.
|
||||
*/
|
||||
public static function get_table_indexes() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class RIO_Base_Extra_Data is a base DTO model for `extra_data` property in RIO_Process_Queue.
|
||||
*
|
||||
* @see RIO_Process_Queue::$extra_data for further information
|
||||
*/
|
||||
class RIO_Base_Extra_Data extends RIO_Base_Object {
|
||||
|
||||
/**
|
||||
* @var string Instance of current class.
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* Magic override of to string method to convert
|
||||
*
|
||||
* @return bool|false|mixed|string
|
||||
*/
|
||||
public function __toString() {
|
||||
$props = get_object_vars( $this );
|
||||
// если свойство не установлено, то не сохраняем его
|
||||
foreach ( $props as $prop_name => $prop_value ) {
|
||||
if ( is_null( $prop_value ) ) {
|
||||
unset( $props[ $prop_name ] );
|
||||
}
|
||||
}
|
||||
$props['class'] = get_called_class();
|
||||
|
||||
return wp_json_encode( $props );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_class() {
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class
|
||||
*
|
||||
* @param string $class_name Имя класса
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_class( $class_name ) {
|
||||
$this->class = $class_name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class RIO_Base_Helper {
|
||||
/**
|
||||
* Configure passed object.
|
||||
*
|
||||
* @param object $object Object class to configure.
|
||||
* @param array $config Key => value list of props to be set on object.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function configure( $object, $config ) {
|
||||
foreach ( $config as $name => $value ) {
|
||||
$object->$name = $value;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class RIO_Base_Object is a base class that implements property feature.
|
||||
*
|
||||
* It takes advantage of __get() and __set(), see further implementation below in the class.
|
||||
*
|
||||
* When property of the class is being used and it has a getter, it will be used instead of directly accessing it.
|
||||
*
|
||||
* The same logic applies for setter.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* // equivalent to $label = $object->getLabel();
|
||||
* $label = $object->label;
|
||||
* // equivalent to $object->setLabel('abc');
|
||||
* $object->label = 'abc';
|
||||
* ```
|
||||
*/
|
||||
class RIO_Base_Object {
|
||||
|
||||
/**
|
||||
* RIO_Base_Object constructor.
|
||||
*
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties.
|
||||
*/
|
||||
public function __construct( $config = [] ) {
|
||||
|
||||
if ( ! empty( $config ) ) {
|
||||
$this->configure( $config );
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate model.
|
||||
*/
|
||||
public function init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure object.
|
||||
*
|
||||
* @param array $config name-value pairs that will be used to initialize the object properties.
|
||||
*/
|
||||
public function configure( $config ) {
|
||||
RIO_Base_Helper::configure( $this, $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of an object property.
|
||||
*
|
||||
* Do not call this method directly as it is a PHP magic method that
|
||||
* will be implicitly called when executing `$value = $object->property;`.
|
||||
*
|
||||
* @param string $name the property name
|
||||
*
|
||||
* @return mixed the property value
|
||||
* @throws Exception if the property is not defined
|
||||
* @see __set()
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
$getter = 'get_' . $name;
|
||||
if ( method_exists( $this, $getter ) ) {
|
||||
return $this->$getter();
|
||||
} elseif ( method_exists( $this, 'set' . $name ) ) {
|
||||
throw new \Exception( 'Getting write-only property: ' . get_class( $this ) . '::' . $name );
|
||||
}
|
||||
throw new Exception( 'Getting unknown property: ' . get_class( $this ) . '::' . $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets value of an object property.
|
||||
*
|
||||
* Do not call this method directly as it is a PHP magic method that
|
||||
* will be implicitly called when executing `$object->property = $value;`.
|
||||
*
|
||||
* @param string $name the property name or the event name
|
||||
* @param mixed $value the property value
|
||||
*
|
||||
* @throws Exception if the property is not defined
|
||||
* @see __get()
|
||||
*/
|
||||
public function __set( $name, $value ) {
|
||||
$setter = 'set_' . $name;
|
||||
if ( method_exists( $this, $setter ) ) {
|
||||
$this->$setter( $value );
|
||||
} elseif ( method_exists( $this, 'get' . $name ) ) {
|
||||
throw new \Exception( 'Setting read-only property: ' . get_class( $this ) . '::' . $name );
|
||||
} else {
|
||||
throw new \Exception( 'Setting unknown property: ' . get_class( $this ) . '::' . $name );
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class RIO_Smushit_Extra_Data is a DTO model for saving extra data from post attachments in `extra_data`.
|
||||
*
|
||||
* @see RIO_Process_Queue::$extra_data for further information
|
||||
*/
|
||||
class RIO_Smushit_Extra_Data extends RIO_Attachment_Extra_Data {
|
||||
|
||||
/**
|
||||
* @var int Final size in bytes.
|
||||
*/
|
||||
protected $optimized_size;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class RIOP_WebP_Extra_Data.
|
||||
*
|
||||
* @property string $source_src
|
||||
*/
|
||||
class RIOP_WebP_Extra_Data extends RIO_Attachment_Extra_Data {
|
||||
/**
|
||||
* @var null|string E.g. attachment, nextgen, etc
|
||||
*/
|
||||
protected $convert_from = null;
|
||||
|
||||
/**
|
||||
* @var null|string|int
|
||||
*/
|
||||
protected $converted_from_size = null;
|
||||
|
||||
/**
|
||||
* @var string|null Image source src.
|
||||
*/
|
||||
protected $source_src = null;
|
||||
|
||||
/**
|
||||
* @var string|null Image absolute path.
|
||||
*/
|
||||
protected $source_path = null;
|
||||
|
||||
/**
|
||||
* @var string|null Converted WebP image src.
|
||||
*/
|
||||
protected $converted_src = null;
|
||||
|
||||
/**
|
||||
* @var string|null Converted WebP absolute path.
|
||||
*/
|
||||
protected $converted_path = null;
|
||||
|
||||
/**
|
||||
* @var int|null Post ID.
|
||||
*/
|
||||
protected $post_id = null;
|
||||
|
||||
/**
|
||||
* @var int|null thumbnails count.
|
||||
*/
|
||||
protected $thumbnails_count = null;
|
||||
|
||||
/**
|
||||
* @param string $source_src
|
||||
*/
|
||||
public function set_source_src( $source_src ) {
|
||||
$this->source_src = trim( $source_src );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source property.
|
||||
*
|
||||
* @param bool $decoded Whether to decode src or not.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_source_src( $decoded = true ) {
|
||||
$src = $this->source_src;
|
||||
|
||||
if ( $decoded ) {
|
||||
return urldecode( $src );
|
||||
}
|
||||
|
||||
return $src;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_source_path() {
|
||||
return $this->source_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $source_path
|
||||
*/
|
||||
public function set_source_path( $source_path ) {
|
||||
$this->source_path = $source_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_convert_from() {
|
||||
return $this->convert_from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $convert_from
|
||||
*/
|
||||
public function set_convert_from( $convert_from ) {
|
||||
$this->convert_from = $convert_from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|null|string
|
||||
*/
|
||||
public function get_converted_from_size() {
|
||||
return $this->converted_from_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null|string $converted_from_size
|
||||
*/
|
||||
public function set_converted_from_size( $converted_from_size ) {
|
||||
$this->converted_from_size = $converted_from_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_converted_path() {
|
||||
return $this->converted_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $converted_path
|
||||
*/
|
||||
public function set_converted_path( $converted_path ) {
|
||||
$this->converted_path = $converted_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_converted_src() {
|
||||
return $this->converted_src;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $converted_src
|
||||
*/
|
||||
public function set_converted_src( $converted_src ) {
|
||||
$this->converted_src = $converted_src;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function get_post_id() {
|
||||
return $this->post_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $post_id
|
||||
*
|
||||
* @return RIOP_WebP_Extra_Data
|
||||
*/
|
||||
public function set_post_id( $post_id ) {
|
||||
$this->post_id = $post_id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function get_original_main_size() {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function get_thumbnails_count() {
|
||||
return $this->thumbnails_count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации в фоне
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Folder_Processing extends WRIO_Processing {
|
||||
|
||||
/**
|
||||
* @return int Count of pushed queue
|
||||
*/
|
||||
public function push_items() {
|
||||
$attachment_ids = [];
|
||||
if ( $this->scope === 'custom-folders' ) {
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$attachment_ids = $cf->getUnoptimizedImages();
|
||||
}
|
||||
|
||||
foreach ( $attachment_ids as $attachment_id ) {
|
||||
$this->push_to_queue( $attachment_id );
|
||||
}
|
||||
|
||||
return $this->count_queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении задачи
|
||||
*
|
||||
* @param int $image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $image ) {
|
||||
if ( $image ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start optimize custom folder image: %s', $image ) );
|
||||
|
||||
if ( $this->scope === 'custom-folders' ) {
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$result = $cf->optimizeImage( $image );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End optimize custom folder image: %s', $image ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы AVIF конвертации в фоне
|
||||
* Class for AVIF image format conversion background processing
|
||||
*
|
||||
* @version 1.6.0
|
||||
* @since 1.6.0
|
||||
*/
|
||||
class WRIO_Media_Processing_Avif extends WRIO_Processing {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'convert_process';
|
||||
|
||||
/**
|
||||
* @var string Format type
|
||||
*/
|
||||
protected $format = 'avif';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $scope Processing scope
|
||||
*/
|
||||
public function __construct( $scope ) {
|
||||
parent::__construct( $scope );
|
||||
}
|
||||
|
||||
/**
|
||||
* Push items to queue
|
||||
*
|
||||
* @return int Number of items in queue
|
||||
*/
|
||||
public function push_items() {
|
||||
$attachment_ids = [];
|
||||
|
||||
if ( $this->scope === 'media-library_avif' ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$attachment_ids = $media_library->getUnconvertedImages( 'avif' );
|
||||
}
|
||||
|
||||
foreach ( $attachment_ids as $attachment_id ) {
|
||||
$this->push_to_queue( $attachment_id );
|
||||
}
|
||||
|
||||
return $this->count_queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод конвертирует изображения в AVIF при выполнении задачи
|
||||
* Method converts images to AVIF when executing task
|
||||
*
|
||||
* @param int $image Attachment ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $image ) {
|
||||
if ( $image ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start convert attachment #%s to AVIF', $image ) );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
|
||||
try {
|
||||
if ( 'media-library_avif' === $this->scope ) {
|
||||
$media_library->webpConvertAttachment( $image, 'avif' );
|
||||
}
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment = $media_library->getAttachment( $image );
|
||||
$wio_attachment->mark_conversion_failure( $throwable, 'avif', 'avif-conversion-background' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End convert attachment #%s to AVIF', $image ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire after complete handle
|
||||
* Вызывается после завершения обработки
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handle_after_complete() {
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$this->scope}_process_running", false );
|
||||
|
||||
WRIO_Plugin::app()->logger->info(
|
||||
sprintf( 'AVIF conversion background process completed for scope: %s', $this->scope )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации в фоне
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Media_Processing_Webp extends WRIO_Processing {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'convert_process';
|
||||
|
||||
/**
|
||||
* @var string Format type (webp or avif)
|
||||
*/
|
||||
protected $format = 'webp';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $scope Processing scope
|
||||
*/
|
||||
public function __construct( $scope ) {
|
||||
parent::__construct( $scope );
|
||||
|
||||
// Extract format from scope (e.g., 'media-library_webp' -> 'webp')
|
||||
if ( $this->scope && strpos( $this->scope, '_' ) !== false ) {
|
||||
$parts = explode( '_', $this->scope );
|
||||
$extracted_format = end( $parts );
|
||||
if ( in_array( $extracted_format, [ 'webp', 'avif' ], true ) ) {
|
||||
$this->format = $extracted_format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Count of pushed queue
|
||||
*/
|
||||
public function push_items() {
|
||||
$attachment_ids = [];
|
||||
if ( strpos( $this->scope, 'media-library_' ) === 0 ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$attachment_ids = $media_library->getUnconvertedImages( $this->format );
|
||||
}
|
||||
|
||||
foreach ( $attachment_ids as $attachment_id ) {
|
||||
$this->push_to_queue( $attachment_id );
|
||||
}
|
||||
|
||||
return $this->count_queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении задачи
|
||||
*
|
||||
* @param int $image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $image ) {
|
||||
if ( $image ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start convert attachment #%s to %s', $image, $this->format ) );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
|
||||
try {
|
||||
if ( strpos( $this->scope, 'media-library_' ) === 0 ) {
|
||||
$media_library->webpConvertAttachment( $image, $this->format );
|
||||
}
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment = $media_library->getAttachment( $image );
|
||||
$wio_attachment->mark_conversion_failure( $throwable, $this->format, sprintf( '%s-conversion-background', $this->format ) );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End convert attachment #%s to %s', $image, $this->format ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire after complete handle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handle_after_complete() {
|
||||
WRIO_Plugin::app()->updatePopulateOption( "{$this->scope}_process_running", false );
|
||||
|
||||
WRIO_Plugin::app()->logger->info(
|
||||
sprintf( '%s conversion background process completed for scope: %s', strtoupper( $this->format ), $this->scope )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации в фоне
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Media_Processing extends WRIO_Processing {
|
||||
|
||||
/**
|
||||
* @param array $attachment_ids
|
||||
*
|
||||
* @return int Count of pushed queue
|
||||
*/
|
||||
public function push_items( $attachment_ids = [] ) {
|
||||
if ( empty( $attachment_ids ) && $this->scope === 'media-library' ) {
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
$attachment_ids = $media_library->getUnoptimizedImages();
|
||||
}
|
||||
|
||||
foreach ( $attachment_ids as $attachment_id ) {
|
||||
$this->push_to_queue( $attachment_id );
|
||||
}
|
||||
|
||||
return $this->count_queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении задачи
|
||||
*
|
||||
* @param int $image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $image ) {
|
||||
if ( $image ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start optimize attachment: %s', $image ) );
|
||||
$media_library = WRIO_Media_Library::get_instance();
|
||||
|
||||
try {
|
||||
if ( 'media-library' === $this->scope ) {
|
||||
$media_library->optimizeAttachment( $image );
|
||||
}
|
||||
} catch ( Throwable $throwable ) {
|
||||
$wio_attachment = $media_library->getAttachment( $image );
|
||||
$wio_attachment->mark_and_log_failure( $throwable, 'background-processing' );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End optimize attachment: %s', $image ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации в фоне
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
class WRIO_Nextgen_Processing extends WRIO_Processing {
|
||||
|
||||
/**
|
||||
* @return int Count of pushed queue
|
||||
*/
|
||||
public function push_items() {
|
||||
$attachment_ids = [];
|
||||
if ( $this->scope === 'nextgen' ) {
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
$attachment_ids = $nextgen_gallery->getUnoptimizedImages();
|
||||
}
|
||||
|
||||
foreach ( $attachment_ids as $attachment_id ) {
|
||||
$this->push_to_queue( $attachment_id );
|
||||
}
|
||||
|
||||
return $this->count_queue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод оптимизирует изображения при выполнении задачи
|
||||
*
|
||||
* @param int $image
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function task( $image ) {
|
||||
if ( $image ) {
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Start optimize attachment: %s', $image ) );
|
||||
|
||||
if ( $this->scope === 'nextgen' ) {
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
$result = $nextgen_gallery->optimizeNextgenImage( $image );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'End optimize attachment: %s', $image ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
use WBCR\Factory_Processing_759\WP_Background_Process;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для работы оптимизации в фоне
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
abstract class WRIO_Processing extends WP_Background_Process {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix = 'wrio';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'optimize_process';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $scope;
|
||||
|
||||
/**
|
||||
* Processing constructor.
|
||||
*
|
||||
* @param $scope
|
||||
*/
|
||||
public function __construct( $scope ) {
|
||||
$this->scope = $scope;
|
||||
$this->action = "{$this->action}_{$scope}";
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
abstract public function push_items();
|
||||
|
||||
/**
|
||||
* Fire before start handle the tasks
|
||||
*/
|
||||
protected function handle_before() {
|
||||
WRIO_Plugin::app()->logger->info( 'START auto optimize process.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire after end handle the tasks
|
||||
*/
|
||||
protected function handle_after() {
|
||||
WRIO_Plugin::app()->logger->info( 'END auto optimize process.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire after complete handle
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function handle_after_complete() {
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'process_running', false );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовый класс для обработки изображений через API сторонних сервисов.
|
||||
*
|
||||
* todo: add usage example
|
||||
*
|
||||
* @version 1.0
|
||||
*/
|
||||
abstract class WIO_Image_Processor_Abstract {
|
||||
|
||||
/**
|
||||
* @var string Имя сервера
|
||||
*/
|
||||
protected $server_name;
|
||||
|
||||
/**
|
||||
* Оптимизация изображения
|
||||
*
|
||||
* @param array $params {
|
||||
* Параметры оптимизации изображения. Разные сервера могут принимать разные наборы параметров. Ниже список всех возможных.
|
||||
*
|
||||
* {type} string $image_url УРЛ изображения
|
||||
* {type} string $image_path Путь к файлу изображения
|
||||
* {type} string $quality Качество
|
||||
* {type} string $save_exif Сохранять ли EXIF данные
|
||||
* }
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* Результаты оптимизации. Основные параметры. Другие параметры зависят от конкретной раелизации.
|
||||
*
|
||||
* {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
|
||||
* {type} int $src_size размер исходного изображения в байтах
|
||||
* {type} int $optimized_size размер оптимизированного изображения в байтах
|
||||
* {type} int $optimized_percent На сколько процентов уменьшилось изображение
|
||||
* {type} bool $not_need_replace Изображение не надо заменять.
|
||||
* {type} bool $not_need_download Изображение не надо скачивать.
|
||||
* }
|
||||
*/
|
||||
abstract function process( $params );
|
||||
|
||||
/**
|
||||
* Качество изображения
|
||||
* Метод конвертирует качество из настроек плагина в формат сервиса оптимизации
|
||||
*
|
||||
* @param mixed $quality качество
|
||||
*/
|
||||
abstract function quality( $quality );
|
||||
|
||||
/**
|
||||
* Проверка наличия ограничения на квоту
|
||||
*
|
||||
* @return bool Возвращает true, если существует ограничение на квоту, иначе false
|
||||
*/
|
||||
abstract public function has_quota_limit();
|
||||
|
||||
/**
|
||||
* Возвращает URL API сервера
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_api_url() {
|
||||
return wrio_get_server_url( $this->server_name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка лимита квоты
|
||||
*
|
||||
* @param mixed $value Новое значение лимита квоты
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_quota_limit( $value ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( $this->server_name . '_quota_limit', (int) $value );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Получает лимит квоты для текущего сервера.
|
||||
*
|
||||
* @return int Лимит квоты, установленный для сервера. Если лимит не задан, возвращается 0.
|
||||
*/
|
||||
public function get_quota_limit() {
|
||||
return WRIO_Plugin::app()->getPopulateOption( $this->server_name . '_quota_limit', 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP запрос к API стороннего сервиса.
|
||||
*
|
||||
* @param string $type POST|GET
|
||||
* @param string $url URL для запроса
|
||||
* @param array|string|null $body Параметры запроса. По умолчанию: false.
|
||||
* @param array $headers Дополнительные заголовки. По умолчанию: false.
|
||||
*
|
||||
* @return string|WP_Error
|
||||
*/
|
||||
protected function request( $type, $url, $body = null, array $headers = [] ) {
|
||||
|
||||
$args = [
|
||||
'method' => $type,
|
||||
'headers' => array_merge(
|
||||
[
|
||||
'User-Agent' => '',
|
||||
],
|
||||
$headers
|
||||
),
|
||||
'body' => $body,
|
||||
'timeout' => 150, // it make take some time for large images and slow Internet connections
|
||||
];
|
||||
|
||||
$error_message = sprintf( 'Failed to get content of URL: %s as wp_remote_request()', $url );
|
||||
|
||||
wp_raise_memory_limit( 'image' );
|
||||
$response = wp_remote_request( $url, $args );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s returned error (%s).', $error_message, $response->get_error_message() ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
return $this->log_http_error_response( $error_message, $response_code, $response_body );
|
||||
}
|
||||
|
||||
if ( empty( $response_body ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s responded an empty request body.', $error_message ) );
|
||||
|
||||
return new WP_Error( 'http_request_failed', 'Server responded an empty request body.' );
|
||||
}
|
||||
|
||||
return $response_body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a non-200 response and preserve the raw response body when available.
|
||||
*
|
||||
* @param string $error_message Base error message for the request.
|
||||
* @param int $response_code HTTP response code.
|
||||
* @param string $response_body Raw HTTP response body.
|
||||
*
|
||||
* @return WP_Error
|
||||
*/
|
||||
protected function log_http_error_response( $error_message, $response_code, $response_body ) {
|
||||
if ( ! empty( $response_body ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s responded Http error (%d).', $error_message, $response_code ) );
|
||||
WRIO_Plugin::app()->logger->debug( sprintf( '%s response body: %s', $error_message, $this->prepare_response_body_for_log( $response_body ) ) );
|
||||
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( 'Server responded with HTTP error.', $response_code ) );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s responded Http error (%d).', $error_message, $response_code ) );
|
||||
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( 'Server responded with HTTP error.', $response_code ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an HTTP status code to a user-facing error message.
|
||||
*
|
||||
* @param string $message Base error message.
|
||||
* @param int $response_code HTTP response code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function append_status_code_to_message( $message, $response_code ) {
|
||||
$message = trim( (string) $message );
|
||||
|
||||
if ( empty( $response_code ) ) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if ( false !== stripos( $message, 'HTTP ' . $response_code ) ) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
return sprintf( '%1$s (HTTP %2$d)', rtrim( $message, '.' ), (int) $response_code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare an HTTP response body for debug logging.
|
||||
*
|
||||
* @param string $response_body Raw HTTP response body.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prepare_response_body_for_log( $response_body ) {
|
||||
$response_body = wp_check_invalid_utf8( (string) $response_body );
|
||||
$response_body = trim( wp_strip_all_tags( $response_body ) );
|
||||
|
||||
if ( '' === $response_body ) {
|
||||
return '[empty after sanitization]';
|
||||
}
|
||||
|
||||
$max_length = 500;
|
||||
|
||||
if ( strlen( $response_body ) > $max_length ) {
|
||||
$response_body = substr( $response_body, 0, $max_length ) . '...';
|
||||
}
|
||||
|
||||
return $response_body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Использует ли сервер отложенную оптимизацию
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeferred() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка отложенной оптимизации изображения
|
||||
*
|
||||
* @param array $optimized_data Параметры отложенной оптимизации. Набор параметров зависит от конкретной реализации
|
||||
*
|
||||
* @return bool|array
|
||||
*/
|
||||
public function checkDeferredOptimization( $optimized_data ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверка данных для отложенной оптимизации.
|
||||
*
|
||||
* Проверяет наличие необходимых параметров и соответствие серверу.
|
||||
*
|
||||
* @param array $optimized_data Параметры отложенной оптимизации. Набор параметров зависит от конкретной реализации
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function validateDeferredData( $optimized_data ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для оптимизации изображений через API сервиса Resmush.
|
||||
*/
|
||||
class WIO_Image_Processor_Premium extends WIO_Image_Processor_Abstract {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $api_url;
|
||||
|
||||
/**
|
||||
* @var string Имя сервера
|
||||
*/
|
||||
protected $server_name = 'server_5';
|
||||
|
||||
/**
|
||||
* Инициализация
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
// Получаем ссылку на сервер 5
|
||||
$this->api_url = wrio_get_server_url( $this->server_name );
|
||||
}
|
||||
|
||||
public function howareyou() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Оптимизация изображения
|
||||
*
|
||||
* @param array $params входные параметры оптимизации изображения
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* Результаты оптимизации
|
||||
*
|
||||
* {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
|
||||
* {type} int $src_size размер исходного изображения в байтах
|
||||
* {type} int $optimized_size размер оптимизированного изображения в байтах
|
||||
* {type} int $optimized_percent На сколько процентов уменьшилось изображение
|
||||
* }
|
||||
*/
|
||||
public function process( $settings ) {
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$settings,
|
||||
[
|
||||
'image_url' => '',
|
||||
'quality' => 100,
|
||||
'save_exif' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$query_args = [
|
||||
'quality' => $settings['quality'],
|
||||
'progressive' => true,
|
||||
];
|
||||
|
||||
if ( $settings['save_exif'] ) {
|
||||
$query_args['strip-exif'] = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['image_url'] ) ) {
|
||||
$query_args['image_url'] = wrio_encode_image_url( $settings['image_url'] );
|
||||
}
|
||||
|
||||
$file = wp_normalize_path( $settings['image_path'] );
|
||||
|
||||
if ( ! file_exists( $file ) ) {
|
||||
return new WP_Error( 'http_request_failed', sprintf( "File %s isn't exists.", $file ) );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Preparing to upload a file (%s) to a remote server (%s).', $settings['image_path'], $this->api_url ) );
|
||||
|
||||
$boundary = '--------------------------' . md5( microtime( true ) . wp_rand() );
|
||||
$headers = [
|
||||
'Authorization' => 'Bearer ' . base64_encode( wrio_get_license_key() ),
|
||||
'PluginId' => wrio_get_freemius_plugin_id(),
|
||||
'X-License-Source' => wrio_get_license_source(),
|
||||
'X-Site-Url' => home_url(),
|
||||
'content-type' => 'multipart/form-data; boundary=' . $boundary,
|
||||
];
|
||||
|
||||
$payload = '';
|
||||
|
||||
// First, add the standard POST fields:
|
||||
foreach ( $query_args as $name => $value ) {
|
||||
$payload .= '--' . $boundary;
|
||||
$payload .= "\r\n";
|
||||
$payload .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
|
||||
$payload .= $value;
|
||||
$payload .= "\r\n";
|
||||
}
|
||||
|
||||
// Upload the file
|
||||
if ( $file ) {
|
||||
$payload .= '--' . $boundary;
|
||||
$payload .= "\r\n";
|
||||
$payload .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file ) . '"' . "\r\n";
|
||||
// $payload .= 'Content-Type: image/jpeg' . "\r\n"; // If you know the mime-type
|
||||
$payload .= "\r\n";
|
||||
$payload .= @file_get_contents( $file );
|
||||
$payload .= "\r\n";
|
||||
}
|
||||
|
||||
$payload .= '--' . $boundary . '--';
|
||||
|
||||
$error_message = sprintf( 'Failed to get content of URL: %s as wp_remote_request()', $this->api_url );
|
||||
|
||||
wp_raise_memory_limit( 'image' );
|
||||
|
||||
$response = wp_remote_request(
|
||||
$this->api_url,
|
||||
[
|
||||
'method' => 'POST',
|
||||
'headers' => $headers,
|
||||
'body' => $payload,
|
||||
'timeout' => 150, // it make take some time for large images and slow Internet connections
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s returned error (%s).', $error_message, $response->get_error_message() ) );
|
||||
WRIO_Plugin::app()->logger->debug( var_export( $response, true ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
if ( 200 !== $response_code ) {
|
||||
return $this->log_http_error_response( $error_message, $response_code, wp_remote_retrieve_body( $response ) );
|
||||
}
|
||||
|
||||
$response_text = wp_remote_retrieve_body( $response );
|
||||
$data = @json_decode( $response_text );
|
||||
if ( ! isset( $data->status ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s responded an empty request body.', $error_message ) );
|
||||
|
||||
return new WP_Error( 'http_request_failed', 'Server responded an empty request body.' );
|
||||
}
|
||||
|
||||
if ( $data->status != 'ok' ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Pending status "ok", bot received "%s"', $data->status ) );
|
||||
|
||||
if ( isset( $data->error ) && is_string( $data->error ) ) {
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( $data->error, $response_code ) );
|
||||
}
|
||||
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( 'Server responded with an unexpected status.', $response_code ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $data->response->quota ) ) {
|
||||
$this->set_quota_limit( $data->response->quota );
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'quota_fetched', true );
|
||||
}
|
||||
|
||||
return [
|
||||
'optimized_img_url' => $data->response->dest,
|
||||
'src_size' => $data->response->src_size,
|
||||
'optimized_size' => $data->response->dest_size,
|
||||
'optimized_percent' => $data->response->percent,
|
||||
'not_need_download' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Качество изображения
|
||||
* Метод конвертирует качество из настроек плагина в формат сервиса resmush
|
||||
*
|
||||
* @param mixed $quality качество
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function quality( $quality = 100 ) {
|
||||
if ( is_numeric( $quality ) ) {
|
||||
if ( $quality >= 1 && $quality <= 100 ) {
|
||||
return $quality;
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $quality ) {
|
||||
case 'normal':
|
||||
return 90;
|
||||
|
||||
case 'aggresive':
|
||||
return 75;
|
||||
|
||||
case 'ultra':
|
||||
return 50;
|
||||
|
||||
case 'googlepage':
|
||||
return 30;
|
||||
|
||||
default:
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, существует ли ограничение на квоту.
|
||||
*
|
||||
* @return bool Возвращает true, если ограничения.
|
||||
*/
|
||||
public function has_quota_limit() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Класс для оптимизации изображений через API Robin (beta).
|
||||
*/
|
||||
class WIO_Image_Processor_Robin extends WIO_Image_Processor_Abstract {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $api_url;
|
||||
|
||||
/**
|
||||
* @var string Имя сервера
|
||||
*/
|
||||
protected $server_name = 'server_2';
|
||||
|
||||
/**
|
||||
* Инициализация
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->api_url = $this->get_api_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Оптимизация изображения
|
||||
*
|
||||
* @param array $params входные параметры оптимизации изображения
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* Результаты оптимизации
|
||||
*
|
||||
* {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
|
||||
* {type} int $src_size размер исходного изображения в байтах
|
||||
* {type} int $optimized_size размер оптимизированного изображения в байтах
|
||||
* {type} int $optimized_percent На сколько процентов уменьшилось изображение
|
||||
* }
|
||||
*/
|
||||
public function process( $settings ) {
|
||||
|
||||
$settings = wp_parse_args(
|
||||
$settings,
|
||||
[
|
||||
'image_url' => '',
|
||||
'quality' => 100,
|
||||
'save_exif' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$query_args = [
|
||||
'quality' => $settings['quality'],
|
||||
'progressive' => true,
|
||||
];
|
||||
|
||||
if ( $settings['save_exif'] ) {
|
||||
$query_args['strip-exif'] = true;
|
||||
}
|
||||
|
||||
if ( ! empty( $settings['image_url'] ) ) {
|
||||
$query_args['image_url'] = wrio_encode_image_url( $settings['image_url'] );
|
||||
}
|
||||
|
||||
$file = wp_normalize_path( $settings['image_path'] );
|
||||
|
||||
if ( ! file_exists( $file ) ) {
|
||||
return new WP_Error( 'http_request_failed', sprintf( "File %s isn't exists.", $file ) );
|
||||
}
|
||||
|
||||
WRIO_Plugin::app()->logger->info( sprintf( 'Preparing to upload a file (%s) to a remote server (%s).', $settings['image_path'], $this->api_url ) );
|
||||
|
||||
$max_size_in_bytes = 10 * 1024 * 1024; // 10MB
|
||||
if ( filesize( $file ) > $max_size_in_bytes ) {
|
||||
$error_message = sprintf(
|
||||
// translators: %1$s: max size in MB, %2$s: option name.
|
||||
__( 'Image exceeds the maximum allowed size of %1$sMB! Enable the \'%2$s\' option to reduce the image size or upgrade to a Pro plan.', 'robin-image-optimizer' ),
|
||||
10,
|
||||
__( 'Resizing large images', 'robin-image-optimizer' )
|
||||
);
|
||||
WRIO_Plugin::app()->logger->error( $error_message );
|
||||
|
||||
return new WP_Error( 'image_size_limit_exceeded', $error_message );
|
||||
}
|
||||
|
||||
$boundary = '--------------------------' . md5( microtime( true ) . wp_rand() );
|
||||
$host = get_option( 'siteurl' );
|
||||
$headers = [
|
||||
'Authorization' => 'Bearer ' . base64_encode( $host ),
|
||||
'content-type' => 'multipart/form-data; boundary=' . $boundary,
|
||||
];
|
||||
|
||||
$payload = '';
|
||||
|
||||
// First, add the standard POST fields:
|
||||
foreach ( $query_args as $name => $value ) {
|
||||
$payload .= '--' . $boundary;
|
||||
$payload .= "\r\n";
|
||||
$payload .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
|
||||
$payload .= $value;
|
||||
$payload .= "\r\n";
|
||||
}
|
||||
|
||||
// Upload the file
|
||||
if ( $file ) {
|
||||
$payload .= '--' . $boundary;
|
||||
$payload .= "\r\n";
|
||||
$payload .= 'Content-Disposition: form-data; name="file"; filename="' . basename( $file ) . '"' . "\r\n";
|
||||
// $payload .= 'Content-Type: image/jpeg' . "\r\n"; // If you know the mime-type
|
||||
$payload .= "\r\n";
|
||||
$payload .= @file_get_contents( $file );
|
||||
$payload .= "\r\n";
|
||||
}
|
||||
|
||||
$payload .= '--' . $boundary . '--';
|
||||
|
||||
$error_message = sprintf( 'Failed to get content of URL: %s as wp_remote_request()', $this->api_url );
|
||||
|
||||
wp_raise_memory_limit( 'image' );
|
||||
|
||||
$response = wp_remote_request(
|
||||
$this->api_url,
|
||||
[
|
||||
'method' => 'POST',
|
||||
'headers' => $headers,
|
||||
'body' => $payload,
|
||||
'timeout' => 150, // it make take some time for large images and slow Internet connections
|
||||
]
|
||||
);
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
$ss = $response->get_error_code();
|
||||
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s returned error (%s).', $error_message, $response->get_error_message() ) );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $response );
|
||||
|
||||
if ( 200 !== $response_code ) {
|
||||
return $this->log_http_error_response( $error_message, $response_code, wp_remote_retrieve_body( $response ) );
|
||||
}
|
||||
|
||||
$response_text = wp_remote_retrieve_body( $response );
|
||||
$data = @json_decode( $response_text );
|
||||
|
||||
if ( ! isset( $data->status ) ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( '%s responded an empty request body.', $error_message ) );
|
||||
|
||||
return new WP_Error( 'http_request_failed', 'Server responded an empty request body.' );
|
||||
}
|
||||
|
||||
if ( $data->status != 'ok' ) {
|
||||
WRIO_Plugin::app()->logger->error( sprintf( 'Pending status "ok", bot received "%s"', $data->status ) );
|
||||
|
||||
if ( isset( $data->error ) && is_string( $data->error ) ) {
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( $data->error, $response_code ) );
|
||||
}
|
||||
|
||||
return new WP_Error( 'http_request_failed', $this->append_status_code_to_message( 'Server responded with an unexpected status.', $response_code ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $data->response->quota ) ) {
|
||||
$this->set_quota_limit( $data->response->quota );
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'quota_fetched', true );
|
||||
}
|
||||
|
||||
return [
|
||||
'optimized_img_url' => $data->response->dest,
|
||||
'src_size' => $data->response->src_size,
|
||||
'optimized_size' => $data->response->dest_size,
|
||||
'optimized_percent' => $data->response->percent,
|
||||
'not_need_download' => false,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Качество изображения
|
||||
* Метод конвертирует качество из настроек плагина в формат сервиса resmush
|
||||
*
|
||||
* @param mixed $quality качество
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function quality( $quality = 100 ) {
|
||||
if ( is_numeric( $quality ) ) {
|
||||
if ( $quality >= 1 && $quality <= 100 ) {
|
||||
return $quality;
|
||||
}
|
||||
}
|
||||
|
||||
switch ( $quality ) {
|
||||
case 'normal':
|
||||
return 90;
|
||||
|
||||
case 'aggresive':
|
||||
return 75;
|
||||
|
||||
case 'ultra':
|
||||
case 'googlepage':
|
||||
return 50;
|
||||
|
||||
default:
|
||||
return 100;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет, существует ли ограничение на квоту.
|
||||
*
|
||||
* @return bool Возвращает true, если ограничения.
|
||||
*/
|
||||
public function has_quota_limit() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
662
wp-content/plugins/robin-image-optimizer/includes/functions.php
Normal file
662
wp-content/plugins/robin-image-optimizer/includes/functions.php
Normal file
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
use WRIO\Paths\phpUri;
|
||||
|
||||
/**
|
||||
* Checks if the current request is a WP REST API request.
|
||||
*
|
||||
* Case #1: After WP_REST_Request initialisation
|
||||
* Case #2: Support "plain" permalink settings
|
||||
* Case #3: URL Path begins with wp-json/ (your REST prefix)
|
||||
* Also supports WP installations in subfolders
|
||||
*
|
||||
* @since 1.3.6
|
||||
* @return boolean
|
||||
*/
|
||||
function wrio_doing_rest_api() {
|
||||
$prefix = rest_get_url_prefix();
|
||||
$rest_route = WRIO_Plugin::app()->request->get( 'rest_route', null );
|
||||
if ( ( defined( 'REST_REQUEST' ) && REST_REQUEST ) // (#1)
|
||||
|| ( ! is_null( $rest_route ) // (#2)
|
||||
&& strpos( trim( $rest_route, '\\/' ), $prefix, 0 ) === 0 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// (#3)
|
||||
$rest_url = wp_parse_url( site_url( $prefix ) );
|
||||
$current_url = wp_parse_url( add_query_arg( [] ) );
|
||||
|
||||
return strpos( $current_url['path'] ?? '/', $rest_url['path'], 0 ) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 1.3.6
|
||||
*/
|
||||
function wrio_doing_ajax() {
|
||||
if ( function_exists( 'wp_doing_ajax' ) ) {
|
||||
return wp_doing_ajax();
|
||||
}
|
||||
|
||||
return defined( 'DOING_AJAX' ) && DOING_AJAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 1.3.6
|
||||
*/
|
||||
function wrio_doing_cron() {
|
||||
if ( function_exists( 'wp_doing_cron' ) ) {
|
||||
return wp_doing_cron();
|
||||
}
|
||||
|
||||
return defined( 'DOING_CRON' ) && DOING_CRON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert full URL paths to absolute paths.
|
||||
*
|
||||
* @param string $url abs url https://site.com/wp-conent/uploads/10/05/image.jpeg
|
||||
*
|
||||
* @return string|null abs path var/site.com/www/wp-conent/uploads/10/05/image.jpeg, if failure null
|
||||
* @since 1.4.0
|
||||
*/
|
||||
function wrio_url_to_abs_path( $url ) {
|
||||
if ( empty( $url ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( strpos( $url, '?' ) !== false ) {
|
||||
$url_parts = explode( '?', $url );
|
||||
|
||||
if ( 2 == sizeof( $url_parts ) ) {
|
||||
$url = $url_parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
$url = rtrim( $url, '/' );
|
||||
|
||||
// todo: if the external site, then it will not work
|
||||
return str_replace( get_site_url(), untrailingslashit( wp_normalize_path( ABSPATH ) ), $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert relative urls to absolute
|
||||
*
|
||||
* @param string $url relative url /wp-conent/uploads/10/05/image.jpeg
|
||||
*
|
||||
* @return string abs url https://site.com/wp-conent/uploads/10/05/image.jpeg
|
||||
* @since 1.4.0
|
||||
*/
|
||||
function wrio_rel_to_abs_url( $url ) {
|
||||
require_once WRIO_PLUGIN_DIR . '/libs/class-rio-relative-to-abs-uri.php';
|
||||
|
||||
return WRIO\Paths\phpUri::parse( get_site_url() )->join( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts relative urls to absolute paths
|
||||
*
|
||||
* @param string $url relative url /wp-conent/uploads/10/05/image.jpeg
|
||||
*
|
||||
* @return string abs path var/site.com/www/wp-conent/uploads/10/05/image.jpeg
|
||||
* @since 1.4.0
|
||||
*/
|
||||
function wrio_rel_url_to_abs_path( $url ) {
|
||||
$abs_url = wrio_rel_to_abs_url( $url );
|
||||
|
||||
return wrio_url_to_abs_path( $abs_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $string
|
||||
* @param bool $capitalize_first_character
|
||||
*
|
||||
* @return mixed|string
|
||||
* @since 1.1
|
||||
*/
|
||||
function wrio_dashes_to_camel_case( $string, $capitalize_first_character = false ) {
|
||||
|
||||
$str = str_replace( '-', '_', ucwords( $string, '-' ) );
|
||||
|
||||
if ( ! $capitalize_first_character ) {
|
||||
$str = lcfirst( $str );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternative php functions basename. Our function works with сyrillic file names.
|
||||
*
|
||||
* @param string $str file path
|
||||
*
|
||||
* @return string|string[]|null
|
||||
* @since 1.3.0
|
||||
*/
|
||||
/*
|
||||
function wrio_basename( $str ) {
|
||||
return preg_replace( '/^.+[\\\\\\/]/', '', $str );
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_is_active_nextgen_gallery() {
|
||||
return is_plugin_active( 'nextgen-gallery/nggallery.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $dir
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.1
|
||||
*/
|
||||
function wrio_rmdir( $dir ) {
|
||||
if ( is_dir( $dir ) ) {
|
||||
$scn = scandir( $dir );
|
||||
|
||||
foreach ( $scn as $files ) {
|
||||
if ( $files !== '.' ) {
|
||||
if ( $files !== '..' ) {
|
||||
if ( ! is_dir( $dir . '/' . $files ) ) {
|
||||
@unlink( $dir . '/' . $files );
|
||||
} else {
|
||||
wrio_rmdir( $dir . '/' . $files );
|
||||
if ( is_dir( $dir . '/' . $files ) ) {
|
||||
@rmdir( $dir . '/' . $files );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@rmdir( $dir );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Пересчёт размера файла в байтах на человекопонятный вид
|
||||
*
|
||||
* Пример: вводим 67894 байт, получаем 67.8 KB
|
||||
* Пример: вводим 6789477 байт, получаем 6.7 MB
|
||||
*
|
||||
* @param int $size размер файла в байтах
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wrio_convert_bytes( $size ) {
|
||||
if ( ! $size ) {
|
||||
return 0;
|
||||
}
|
||||
$base = log( $size ) / log( 1024 );
|
||||
$suffix = [ '', 'KB', 'MB', 'GB', 'TB' ];
|
||||
$f_base = intval( floor( $base ) );
|
||||
|
||||
return round( pow( 1024, $base - floor( $base ) ), 2 ) . ' ' . $suffix[ $f_base ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует хеш строку
|
||||
*
|
||||
* @param int $length
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wrio_generate_random_string( $length = 10 ) {
|
||||
$characters = '0123456789abcdefghiklmnopqrstuvwxyz';
|
||||
$charactersLength = strlen( $characters );
|
||||
$randomString = '';
|
||||
for ( $i = 0; $i < $length; $i++ ) {
|
||||
$randomString .= $characters[ rand( 0, $charactersLength - 1 ) ];
|
||||
}
|
||||
|
||||
return $randomString;
|
||||
}
|
||||
|
||||
/**
|
||||
* in priorities checks its license.
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_is_license_activate() {
|
||||
return wrio_is_clearfy_license_activate() || ( WRIO_Plugin::app()->premium->is_activate() && WRIO_Plugin::app()->premium->is_active() );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_is_clearfy_license_activate() {
|
||||
if ( class_exists( 'WCL_Plugin' ) ) {
|
||||
if ( version_compare( WCL_PLUGIN_VERSION, '1.6.3', '>=' ) ) {
|
||||
if ( WCL_Plugin::app()->premium->is_activate() ) {
|
||||
$plan_id = WCL_Plugin::app()->premium->get_license()->get_plan_id();
|
||||
|
||||
// Now for new plans this doesn't work.
|
||||
return '4710' === $plan_id || '3530' === $plan_id;
|
||||
}
|
||||
} else {
|
||||
$current_license = WCL_Licensing::instance()->getStorage()->getLicense();
|
||||
|
||||
if ( ! empty( $current_license ) && ! empty( $current_license->id ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks its license in priorities.
|
||||
*
|
||||
* @return bool
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_is_license_active() {
|
||||
if ( wrio_is_clearfy_license_activate() ) {
|
||||
if ( version_compare( WCL_PLUGIN_VERSION, '1.6.3', '>=' ) ) {
|
||||
return WCL_Plugin::app()->premium->is_active();
|
||||
} else {
|
||||
return WCL_Licensing::instance()->isLicenseValid();
|
||||
}
|
||||
}
|
||||
|
||||
return WRIO_Plugin::app()->premium->is_activate() && WRIO_Plugin::app()->premium->is_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* return it key.
|
||||
*
|
||||
* @return string|null
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_get_license_key() {
|
||||
if ( ! wrio_is_license_activate() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( wrio_is_clearfy_license_activate() ) {
|
||||
if ( version_compare( WCL_PLUGIN_VERSION, '1.6.3', '>=' ) ) {
|
||||
return WCL_Plugin::app()->premium->get_license()->get_key();
|
||||
} else {
|
||||
return WCL_Licensing::instance()->getStorage()->getLicense()->secret_key;
|
||||
}
|
||||
}
|
||||
|
||||
return WRIO_Plugin::app()->premium->get_license()->get_key();
|
||||
/*
|
||||
if ( WRIO_Plugin::app()->premium->is_activate() ) {
|
||||
return WRIO_Plugin::app()->premium->get_license()->get_key();
|
||||
} else {
|
||||
if ( wrio_is_clearfy_license_activate() ) {
|
||||
if ( version_compare( WCL_PLUGIN_VERSION, '1.6.3', '>=' ) ) {
|
||||
return WCL_Plugin::app()->premium->get_license()->get_key();
|
||||
} else {
|
||||
return WCL_Licensing::instance()->getStorage()->getLicense()->secret_key;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the license source (freemius or sdk).
|
||||
*
|
||||
* @return string|null 'freemius', 'sdk', or null if no license.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
function wrio_get_license_source() {
|
||||
if ( ! wrio_is_license_activate() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return WRIO_Plugin::app()->premium->get_license()->get_source();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number|null
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_get_freemius_plugin_id() {
|
||||
if ( wrio_is_clearfy_license_activate() ) {
|
||||
return WCL_Plugin::app()->getPluginInfoAttr( 'freemius_plugin_id' );
|
||||
}
|
||||
|
||||
return WRIO_Plugin::app()->premium->get_setting( 'plugin_id' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size information for all currently-registered image sizes.
|
||||
*
|
||||
* @return array $sizes Data for all currently-registered image sizes.
|
||||
* @uses get_intermediate_image_sizes()
|
||||
* @global $_wp_additional_image_sizes
|
||||
*/
|
||||
function wrio_get_image_sizes() {
|
||||
global $_wp_additional_image_sizes;
|
||||
|
||||
$sizes = [];
|
||||
|
||||
foreach ( get_intermediate_image_sizes() as $_size ) {
|
||||
if ( in_array( $_size, [ 'thumbnail', 'medium', 'medium_large', 'large' ] ) ) {
|
||||
$sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
|
||||
$sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
|
||||
$sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" );
|
||||
} elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
|
||||
$sizes[ $_size ] = [
|
||||
'width' => $_wp_additional_image_sizes[ $_size ]['width'],
|
||||
'height' => $_wp_additional_image_sizes[ $_size ]['height'],
|
||||
'crop' => $_wp_additional_image_sizes[ $_size ]['crop'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает URL сервера оптимизации
|
||||
*
|
||||
* @param string $server_name имя сервера
|
||||
*
|
||||
* @return string
|
||||
* @since 1.2.0
|
||||
*/
|
||||
function wrio_get_server_url( $server_name ) {
|
||||
|
||||
$servers = [
|
||||
'server_2' => 'https://dashboard.robinoptimizer.com/v1/free/image/optimize',
|
||||
'server_5' => 'https://dashboard.robinoptimizer.com/v1/tariff/optimize',
|
||||
];
|
||||
|
||||
$servers = apply_filters( 'wbcr/rio/allow_servers', $servers );
|
||||
|
||||
if ( isset( $servers[ $server_name ] ) ) {
|
||||
return $servers[ $server_name ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the User Agent of the current user, saves it to a WordPress option if not already saved,
|
||||
* and retrieves it from the options table if it exists.
|
||||
*
|
||||
* @return string|null The stored User Agent or null if not available.
|
||||
*/
|
||||
function wrio_get_user_agent() {
|
||||
$saved_user_agent = WRIO_Plugin::app()->getPopulateOption( 'user_agent' );
|
||||
|
||||
if ( $saved_user_agent ) {
|
||||
return $saved_user_agent;
|
||||
}
|
||||
|
||||
$browsers = [
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36',
|
||||
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0',
|
||||
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
|
||||
'Mozilla/5.0 (Android 11; Mobile; rv:94.0) Gecko/94.0 Firefox/94.0',
|
||||
'Mozilla/5.0 (iPad; CPU OS 15_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Mobile/15E148 Safari/604.1',
|
||||
];
|
||||
|
||||
$random_user_agent = $browsers[ array_rand( $browsers ) ];
|
||||
|
||||
$current_user_agent = isset( $_SERVER['HTTP_USER_AGENT'] )
|
||||
? sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] )
|
||||
: $random_user_agent;
|
||||
|
||||
if ( $current_user_agent ) {
|
||||
WRIO_Plugin::app()->updatePopulateOption( 'user_agent', $current_user_agent );
|
||||
}
|
||||
|
||||
return $current_user_agent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are some migrations left to be processed.
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wbcr_rio_has_meta_to_migrate() {
|
||||
|
||||
$db_version = RIO_Process_Queue::get_db_version();
|
||||
|
||||
if ( 2 === $db_version ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Low number to limit resources consumption
|
||||
$attachments = wbcr_rio_get_meta_to_migrate( 5 );
|
||||
|
||||
if ( isset( $attachments->posts ) && count( $attachments->posts ) > 0 ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( 1 === $db_version ) {
|
||||
RIO_Process_Queue::update_db_version( 2 );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of meta to migrate.
|
||||
*
|
||||
* @param int $limit Attachment limit per page.
|
||||
*
|
||||
* @return WP_Query
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wbcr_rio_get_meta_to_migrate( $limit = 0 ) {
|
||||
$args = [
|
||||
'post_type' => 'attachment',
|
||||
'post_status' => 'inherit',
|
||||
'post_mime_type' => [ 'image/jpeg', 'image/gif', 'image/png' ],
|
||||
'posts_per_page' => - 1,
|
||||
'meta_query' => [
|
||||
[
|
||||
'key' => 'wio_optimized',
|
||||
'compare' => 'EXISTS',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if ( $limit ) {
|
||||
$args['posts_per_page'] = $limit;
|
||||
}
|
||||
|
||||
return new WP_Query( $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @since 1.3.0
|
||||
*/
|
||||
function wrio_get_meta_migration_notice_text() {
|
||||
$nonce = wp_create_nonce( 'wrio-meta-migrations' );
|
||||
|
||||
return sprintf(
|
||||
// translators: %1$s is the opening anchor tag, %2$s is the closing anchor tag.
|
||||
__( 'The database schema has changed. %1$sUpgrade now%2$s to the latest version.', 'robin-image-optimizer' ),
|
||||
'<a href="#" id="wbcr-wio-meta-migration-action" class="button button-default" data-nonce="' . esc_attr( $nonce ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scope
|
||||
*
|
||||
* @return WRIO_Folder_Processing|WRIO_Media_Processing|WRIO_Media_Processing_Webp|WRIO_Media_Processing_Avif|WRIO_Nextgen_Processing|null
|
||||
*/
|
||||
function wrio_get_processing_class( $scope ) {
|
||||
$object = null;
|
||||
switch ( $scope ) {
|
||||
case 'media-library':
|
||||
if ( class_exists( 'WRIO_Media_Processing' ) ) {
|
||||
$object = new WRIO_Media_Processing( $scope );
|
||||
}
|
||||
break;
|
||||
case 'media-library_webp':
|
||||
if ( class_exists( 'WRIO_Media_Processing_Webp' ) ) {
|
||||
$object = new WRIO_Media_Processing_Webp( $scope );
|
||||
}
|
||||
break;
|
||||
case 'media-library_avif':
|
||||
if ( class_exists( 'WRIO_Media_Processing_Avif' ) ) {
|
||||
$object = new WRIO_Media_Processing_Avif( $scope );
|
||||
}
|
||||
break;
|
||||
case 'custom-folders':
|
||||
if ( class_exists( 'WRIO_Folder_Processing' ) ) {
|
||||
$object = new WRIO_Folder_Processing( $scope );
|
||||
}
|
||||
break;
|
||||
case 'nextgen':
|
||||
if ( class_exists( 'WRIO_Nextgen_Processing' ) ) {
|
||||
$object = new WRIO_Nextgen_Processing( $scope );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $for_sql
|
||||
*
|
||||
* @return string|array<string>
|
||||
*/
|
||||
function wrio_get_allowed_formats( $for_sql = false ) {
|
||||
$allowed_formats = explode( ',', WRIO_Plugin::app()->getOption( 'allowed_formats', 'image/jpeg,image/png,image/gif' ) );
|
||||
$allowed_formats_sql = [];
|
||||
foreach ( $allowed_formats as $k => $format ) {
|
||||
$format = esc_sql( $format );
|
||||
|
||||
$allowed_formats_sql[ $k ] = "'{$format}'";
|
||||
}
|
||||
$allowed_formats_sql = implode( ',', $allowed_formats_sql );
|
||||
|
||||
return $for_sql ? $allowed_formats_sql : $allowed_formats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently enabled image conversion formats.
|
||||
*
|
||||
* @return string[] Array of enabled format names ('webp', 'avif'). Empty if none enabled.
|
||||
* @since 1.9.0
|
||||
*/
|
||||
function wrio_get_conversion_format() {
|
||||
return WRIO_Format_Converter_Factory::get_enabled_formats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if format conversion is currently enabled.
|
||||
*
|
||||
* @return bool True if WebP or AVIF conversion is enabled, false otherwise.
|
||||
* @since 1.9.0
|
||||
*/
|
||||
function wrio_is_format_conversion_enabled() {
|
||||
return WRIO_Format_Converter_Factory::is_format_conversion_enabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if AVIF format is available (requires premium license).
|
||||
*
|
||||
* @return bool True if AVIF format is available, false otherwise.
|
||||
* @since 1.9.0
|
||||
*/
|
||||
function wrio_is_avif_available() {
|
||||
return wrio_is_license_activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file size reliably within the current request.
|
||||
* PHP caches stat() results; we clear cache for this path.
|
||||
*
|
||||
* @param mixed $file_path The file path.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
function wrio_get_file_size( $file_path ) {
|
||||
if ( empty( $file_path ) || ! is_string( $file_path ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
clearstatcache( true, $file_path );
|
||||
return file_exists( $file_path ) ? (int) filesize( $file_path ) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properly encode image URL for API submission.
|
||||
*
|
||||
* Encodes only the path component of the URL (e.g., filename with special characters)
|
||||
* while preserving the domain and protocol. This ensures non-ASCII characters
|
||||
* (accented letters, spaces, etc.) are converted to percent-encoded form.
|
||||
*
|
||||
* @param string $url The image URL to encode.
|
||||
*
|
||||
* @return string The properly encoded URL safe for API submission.
|
||||
* @since 1.10.0
|
||||
*/
|
||||
function wrio_encode_image_url( $url ) {
|
||||
if ( empty( $url ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Parse the URL into components (without escaping first to avoid double encoding)
|
||||
$parsed = wp_parse_url( $url );
|
||||
|
||||
if ( false === $parsed || empty( $parsed ) ) {
|
||||
return esc_url_raw( $url );
|
||||
}
|
||||
|
||||
$base = '';
|
||||
if ( ! empty( $parsed['scheme'] ) ) {
|
||||
$base .= $parsed['scheme'] . '://';
|
||||
}
|
||||
|
||||
if ( ! empty( $parsed['host'] ) ) {
|
||||
$base .= $parsed['host'];
|
||||
}
|
||||
|
||||
if ( ! empty( $parsed['port'] ) ) {
|
||||
$base .= ':' . $parsed['port'];
|
||||
}
|
||||
|
||||
// Encode path component (filename and directories)
|
||||
$path = '';
|
||||
if ( ! empty( $parsed['path'] ) ) {
|
||||
$segments = array_map( 'rawurlencode', explode( '/', $parsed['path'] ) );
|
||||
$path = implode( '/', $segments );
|
||||
}
|
||||
|
||||
$query = '';
|
||||
if ( ! empty( $parsed['query'] ) ) {
|
||||
$query = '?' . $parsed['query'];
|
||||
}
|
||||
|
||||
$fragment = '';
|
||||
if ( ! empty( $parsed['fragment'] ) ) {
|
||||
$fragment = '#' . $parsed['fragment'];
|
||||
}
|
||||
|
||||
return $base . $path . $query . $fragment;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
2
wp-content/plugins/robin-image-optimizer/index.php
Normal file
2
wp-content/plugins/robin-image-optimizer/index.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// silence is golden
|
||||
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* AJAX обработчик выбора папки
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wriop_browse_dir',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
if ( is_main_site() ) {
|
||||
$base = get_home_path();
|
||||
$root = wp_normalize_path( $base );
|
||||
} else {
|
||||
$up = wp_upload_dir();
|
||||
$root = wp_normalize_path( $up['basedir'] );
|
||||
}
|
||||
|
||||
$dir = trim( WRIO_Plugin::app()->request->post( 'dir', null, 'rawurldecode' ) );
|
||||
|
||||
$multiselect = WRIO_Plugin::app()->request->post( 'multiSelect' );
|
||||
$multiselect = $multiselect == 'true' ? true : false;
|
||||
|
||||
$only_folders = WRIO_Plugin::app()->request->post( 'onlyFolders' );
|
||||
$only_folders = $only_folders == 'true' ? true : false;
|
||||
$only_folders = $dir == '/' || $only_folders;
|
||||
|
||||
$only_files = WRIO_Plugin::app()->request->post( 'onlyFiles' );
|
||||
$only_files = $only_files == 'true' ? true : false;
|
||||
|
||||
$selected_dir = trailingslashit( $root ) . ( $dir == '/' ? '' : $dir );
|
||||
|
||||
// set checkbox if multiSelect set to true
|
||||
$checkbox = $multiselect ? "<input type='checkbox' />" : null;
|
||||
|
||||
$upload_dir = wp_upload_dir();
|
||||
$upload_dir_path = trailingslashit( str_replace( ABSPATH, '', $upload_dir['basedir'] ) );
|
||||
$wp_content_dir = trailingslashit( str_replace( ABSPATH, '', WP_CONTENT_DIR ) );
|
||||
|
||||
$ngg_path = str_replace( wp_normalize_path( ABSPATH ), '', wrio_get_ngg_galleries_path() );
|
||||
$shortpixel_path = str_replace( wp_normalize_path( ABSPATH ), '', wrio_get_shortpixel_path() );
|
||||
$ewww_path = str_replace( wp_normalize_path( ABSPATH ), '', wrio_get_ewww_tools_path() );
|
||||
$wc_path = str_replace( wp_normalize_path( ABSPATH ), '', wrio_get_wc_logs_path() );
|
||||
|
||||
$exclude_dirs = [
|
||||
$ngg_path,
|
||||
$shortpixel_path,
|
||||
$ewww_path,
|
||||
$wc_path,
|
||||
$wp_content_dir . 'backup',
|
||||
$wp_content_dir . 'backups',
|
||||
$wp_content_dir . 'cache',
|
||||
$wp_content_dir . 'lang',
|
||||
$wp_content_dir . 'langs',
|
||||
$wp_content_dir . 'languages',
|
||||
$upload_dir_path . 'wio_backup',
|
||||
$upload_dir_path . 'wrio',
|
||||
$upload_dir_path . 'wrio-webp-uploads',
|
||||
// 'wp-admin',
|
||||
// 'wp-includes'
|
||||
];
|
||||
// исключаем все директории /wp-content/uploads/2019 - они уже оптимизируются в медиабиблиотеке.
|
||||
// с основания WP в 2003 году до текущего года + 1 на всякий случай.
|
||||
$year = date( 'Y' ) + 1;
|
||||
for ( $i = 2003; $i <= $year; $i++ ) {
|
||||
$exclude_dirs[] = $upload_dir_path . $i;
|
||||
}
|
||||
|
||||
if ( file_exists( $selected_dir ) && is_dir( $selected_dir ) ) {
|
||||
|
||||
$files = scandir( $selected_dir );
|
||||
$return_dir = substr( $selected_dir, strlen( $root ) );
|
||||
|
||||
natcasesort( $files );
|
||||
|
||||
if ( count( $files ) > 2 ) { // The 2 accounts for . and ..
|
||||
echo "<ul class='jqueryFileTree'>";
|
||||
$counter = 0;
|
||||
foreach ( $files as $file ) {
|
||||
// если в папке очень много файлов, то показываем не все.
|
||||
if ( $counter++ > 200 ) {
|
||||
break;
|
||||
}
|
||||
// если это папка бекап или другое исключение - пропускаем
|
||||
if ( in_array( $return_dir . $file, $exclude_dirs ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$htmlRel = str_replace( "'", ''', $return_dir . $file );
|
||||
$htmlName = htmlentities( $file );
|
||||
$ext = preg_replace( '/^.*\./', '', $file );
|
||||
|
||||
if ( file_exists( $selected_dir . $file ) && $file != '.' && $file != '..' ) {
|
||||
// KEEP the spaces in front of the rel values - it's a trick to make WP Hide not replace the wp-content path
|
||||
if ( is_dir( $selected_dir . $file ) && ( ! $only_files || $only_folders ) ) {
|
||||
echo "<li class='directory collapsed'>{$checkbox}<a rel=' " . $htmlRel . "/'>" . $htmlName . '</a></li>';
|
||||
} elseif ( ! $only_folders || $only_files ) {
|
||||
echo "<li class='file ext_{$ext}'>{$checkbox}<a rel=' " . $htmlRel . "'>" . $htmlName . '</a></li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo '</ul>';
|
||||
}
|
||||
}
|
||||
die();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX обработчик добавления папки
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wrio-add-custom-folder',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$path = WRIO_Plugin::app()->request->request( 'path', null, true );
|
||||
|
||||
if ( empty( $path ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$folder = $cf->addFolder( $path );
|
||||
|
||||
if ( is_wp_error( $folder ) ) {
|
||||
wp_send_json_error(
|
||||
[
|
||||
'error_message' => $folder->get_error_message(),
|
||||
'error_code' => $folder->get_error_code(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success( $folder->toArray() );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX индексация папки
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wrio-scan-folder',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$uid = WRIO_Plugin::app()->request->request( 'uid', null, true );
|
||||
$offset = WRIO_Plugin::app()->request->request( 'offset', 0, true );
|
||||
$total = WRIO_Plugin::app()->request->request( 'total', 0, true );
|
||||
|
||||
if ( empty( $uid ) ) {
|
||||
wp_die( - 1 );
|
||||
}
|
||||
|
||||
$max_process_elements = 100; // сколько элементов за итерацию индексирования
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$folder = $cf->getFolder( $uid );
|
||||
|
||||
if ( ! $total ) {
|
||||
$total = $folder->reCountFiles();
|
||||
$cf->saveFolders();
|
||||
}
|
||||
|
||||
$processed_count = $folder->indexing( $offset, $max_process_elements );
|
||||
$offset = $offset + $processed_count;
|
||||
|
||||
$results = [
|
||||
'offset' => $offset,
|
||||
'total' => $total,
|
||||
'complete' => false,
|
||||
'percent' => 0,
|
||||
];
|
||||
|
||||
if ( $total ) {
|
||||
$results['percent'] = 100 - ( ( $total - $offset ) * 100 / $total );
|
||||
/**
|
||||
* Операция индексирования состоит из двух этапов. Проверка существующих файлов и поиск новых файлов.
|
||||
* Поэтому процент делим на 2 и добавляем 50% т.к. это вторая часть.
|
||||
*/
|
||||
$results['percent'] = $results['percent'] / 2 + 50;
|
||||
}
|
||||
|
||||
if ( $offset >= $total ) {
|
||||
$results['percent'] = 100;
|
||||
$results['complete'] = true;
|
||||
}
|
||||
|
||||
wp_send_json_success( $results );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX обработчик удаления папки
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wriop_remove_folder',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
|
||||
if ( ! $uid ) {
|
||||
die();
|
||||
}
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$cf->removeFolder( $uid );
|
||||
$cf->saveFolders();
|
||||
|
||||
die();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX проверка проиндексированных файлов
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wriop_folder_sync_index',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
|
||||
$offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0;
|
||||
$total = isset( $_POST['total'] ) ? intval( $_POST['total'] ) : 0;
|
||||
$max_process_elements = 20; // сколько элементов за итерацию индексирования
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$folder = $cf->getFolder( $uid );
|
||||
$total = $folder->countIndexedFiles();
|
||||
$processed_count = $folder->syncIndex( $offset, $max_process_elements );
|
||||
$offset = $offset + $processed_count;
|
||||
$results = [
|
||||
'offset' => $offset,
|
||||
'total' => $total,
|
||||
'complete' => false,
|
||||
'percent' => 0,
|
||||
];
|
||||
if ( $total ) {
|
||||
$results['percent'] = 100 - ( ( $total - $offset ) * 100 / $total );
|
||||
/**
|
||||
* Операция индексирования состоит из двух этапов. Проверка существующих файлов и поиск новых файлов.
|
||||
* Поэтому процент делим на 2. Это первая часть.
|
||||
*/
|
||||
$results['percent'] = $results['percent'] / 2;
|
||||
}
|
||||
|
||||
if ( $offset >= $total ) {
|
||||
$results['percent'] = 100;
|
||||
$results['complete'] = true;
|
||||
}
|
||||
|
||||
wp_send_json( $results );
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX массовая оптимизация
|
||||
*/
|
||||
/*
|
||||
add_action( 'wp_ajax_wriop_process_cf_images', function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
$reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
|
||||
|
||||
// в ajax запросе мы не знаем, получен ли он из мультиадминки или из обычной. Поэтому проверяем параметр, полученный из frontend
|
||||
if ( isset( $_POST['multisite'] ) and $_POST['multisite'] ) {
|
||||
$multisite = new WIO_Multisite;
|
||||
$multisite->initHooks();
|
||||
}
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
|
||||
/*$folders = $cf->getFolders();
|
||||
|
||||
if ( ! empty( $folders ) ) {
|
||||
foreach ( (array) $folders as $folder ) {
|
||||
$folder = $cf->getFolder( $folder->get( 'uid' ) );
|
||||
$count_files = $folder->countFiles();
|
||||
$count_indexed_files = $folder->countIndexedFiles();
|
||||
$test = 'fsdf';
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
if ( $reset_current_error ) {
|
||||
// сбрасываем текущие ошибки оптимизации
|
||||
$cf->resetCurrentErrors();
|
||||
}
|
||||
|
||||
$max_process_per_request = 1;
|
||||
$optimized_data = $cf->processUnoptimizedImages( $max_process_per_request );
|
||||
|
||||
// если изображения закончились - посылаем команду завершения
|
||||
if ( $optimized_data['remain'] <= 0 ) {
|
||||
$optimized_data['end'] = true;
|
||||
}
|
||||
wp_send_json( $optimized_data );
|
||||
} );*/
|
||||
|
||||
/**
|
||||
* AJAX массовая оптимизация выбранной папки
|
||||
*/
|
||||
/*
|
||||
add_action( 'wp_ajax_wriop_process_cf_folder_images', function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
|
||||
// в ajax запросе мы не знаем, получен ли он из мультиадминки или из обычной. Поэтому проверяем параметр, полученный из frontend
|
||||
if ( isset( $_POST['multisite'] ) and $_POST['multisite'] ) {
|
||||
$multisite = new WIO_Multisite;
|
||||
$multisite->initHooks();
|
||||
}
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$max_process_per_request = 1;
|
||||
|
||||
add_filter( 'wriop_cf_current_folder', function ( $folder_uid ) {
|
||||
$folder_uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
|
||||
|
||||
return $folder_uid;
|
||||
} );
|
||||
|
||||
$optimized_data = $cf->processUnoptimizedImages( $max_process_per_request );
|
||||
|
||||
// если изображения закончились - посылаем команду завершения
|
||||
if ( $optimized_data['remain'] <= 0 ) {
|
||||
$optimized_data['end'] = true;
|
||||
}
|
||||
wp_send_json( $optimized_data );
|
||||
} );*/
|
||||
|
||||
/**
|
||||
* Переоптимизация cf_image. AJAX
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_cf_reoptimize_image',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'reoptimize', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$image_id = (int) $_POST['id'];
|
||||
$backup = WRIOP_Backup::get_instance();
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
|
||||
if ( $backup_origin_images and ! $backup->isBackupWritable() ) {
|
||||
echo $cf->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
|
||||
wp_suspend_cache_addition( true );
|
||||
|
||||
$default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
|
||||
$level = isset( $_POST['level'] ) ? sanitize_text_field( $_POST['level'] ) : $default_level;
|
||||
|
||||
$optimized_data = $cf->optimizeImage( $image_id, $level );
|
||||
|
||||
if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
|
||||
echo 'processing'; // эту строку не локализировать!
|
||||
die();
|
||||
}
|
||||
|
||||
echo $cf->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Восстановление
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_cf_restore_image',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'restore', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
wp_suspend_cache_addition( true );
|
||||
$image_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
|
||||
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
$cf_image = $cf->getImage( $image_id );
|
||||
$image_statistics = WRIO_Image_Statistic_Folders::get_instance();
|
||||
if ( $cf_image->isOptimized() ) {
|
||||
$restored = $cf_image->restore();
|
||||
if ( ! is_wp_error( $restored ) ) {
|
||||
$optimization_data = $cf_image->getOptimizationData();
|
||||
$optimized_size = $optimization_data->get_final_size();
|
||||
$original_size = $optimization_data->get_original_size();
|
||||
$webp_optimized_size = $optimization_data->get_extra_data()->get_webp_main_size();
|
||||
$image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size );
|
||||
$image_statistics->deductFromField( 'optimized_size', $optimized_size );
|
||||
$image_statistics->deductFromField( 'original_size', $original_size );
|
||||
$image_statistics->save();
|
||||
|
||||
$folder = $cf->getFolder( $cf_image->get( 'folder_uid' ) );
|
||||
$folder->reCountOptimizedFiles();
|
||||
$cf->saveFolders();
|
||||
}
|
||||
}
|
||||
|
||||
echo $cf->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* AJAX обработчик восстановления из резервной копии
|
||||
*/
|
||||
/*
|
||||
add_action( 'wp_ajax_wio_cf_restore_backup', function () {
|
||||
check_admin_referer( 'wio-iph' );
|
||||
$max_process_per_request = 10; // сколько картинок восстанавливаем за 1 запрос
|
||||
$total = sanitize_text_field( $_POST['total'] );
|
||||
if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] ) {
|
||||
switch_to_blog( intval( $_POST['blog_id'] ) );
|
||||
}
|
||||
$folder_uid = isset( $_POST['uid'] ) ? sanitize_text_field( $_POST['uid'] ) : false;
|
||||
$cf = WRIO_Custom_Folders::get_instance();
|
||||
if ( $total == '?' ) {
|
||||
$total = RIO_Process_Queue::count_by_type_status( 'cf_image', 'success' );
|
||||
}
|
||||
$restored_data = $cf->restoreFolderFromBackup( $folder_uid, $max_process_per_request );
|
||||
if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] ) {
|
||||
restore_current_blog();
|
||||
}
|
||||
$restored_data['total'] = $total;
|
||||
if ( $total ) {
|
||||
$restored_data['percent'] = 100 - ( $restored_data['remain'] * 100 / $total );
|
||||
} else {
|
||||
$restored_data['percent'] = 0;
|
||||
}
|
||||
// если изображения закончились - посылаем команду завершения
|
||||
if ( $restored_data['remain'] <= 0 ) {
|
||||
$restored_data['end'] = true;
|
||||
}
|
||||
wp_send_json( $restored_data );
|
||||
} );*/
|
||||
|
||||
/**
|
||||
* Загружает шаблон для всплывающий окон
|
||||
*/
|
||||
/*
|
||||
add_action( 'wp_ajax_wio_cf_get_template_part', function () {
|
||||
$template = sanitize_text_field( $_POST['template'] );
|
||||
$templates = [
|
||||
'select_folder' => 'select-folder.php',
|
||||
'restore_folder' => 'restore-folder.php',
|
||||
'sync_folder' => 'sync-folder.php',
|
||||
'optimize_folder' => 'optimize-folder.php',
|
||||
'sync_all_folders' => 'sync-all-folders.php',
|
||||
'folders_table_body' => 'folders-table-body.php',
|
||||
];
|
||||
if ( isset( $templates[ $template ] ) ) {
|
||||
$template_file = WRIOP_PLUGIN_DIR . '/admin/pages/parts/' . $templates[ $template ];
|
||||
if ( file_exists( $template_file ) ) {
|
||||
include( $template_file );
|
||||
}
|
||||
}
|
||||
die();
|
||||
} );*/
|
||||
|
||||
/**
|
||||
* Загружает шаблон для всплывающий окон
|
||||
*/
|
||||
add_action(
|
||||
'wp_ajax_wio_cf_reload_ui',
|
||||
function () {
|
||||
if ( ! check_ajax_referer( 'bulk_optimization', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$template_file = WRIOP_PLUGIN_DIR . '/admin/pages/parts/folders-table-body.php';
|
||||
$folders_table = '';
|
||||
if ( is_file( $template_file ) ) {
|
||||
ob_start();
|
||||
include $template_file;
|
||||
$folders_table = ob_get_contents();
|
||||
ob_end_clean();
|
||||
}
|
||||
$image_statistics = WRIO_Image_Statistic_Folders::get_instance();
|
||||
$responce = [
|
||||
'folders_table' => $folders_table,
|
||||
'statistic' => $image_statistics->load(),
|
||||
];
|
||||
wp_send_json( $responce );
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Переоптимизация аттачмента
|
||||
*/
|
||||
function wbcr_riop_reoptimizeImage() {
|
||||
if ( ! check_ajax_referer( 'reoptimize', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
$image_id = (int) $_POST['id'];
|
||||
$backup = WRIOP_Backup::get_instance();
|
||||
$backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
|
||||
echo $nextgen_gallery->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
wp_suspend_cache_addition( true );
|
||||
$default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
|
||||
$level = isset( $_POST['level'] ) ? sanitize_text_field( $_POST['level'] ) : $default_level;
|
||||
|
||||
$optimized_data = $nextgen_gallery->optimizeNextgenImage( $image_id, $level );
|
||||
|
||||
if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
|
||||
echo 'processing';
|
||||
die();
|
||||
}
|
||||
|
||||
echo $nextgen_gallery->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстановление
|
||||
*/
|
||||
function wbcr_riop_restoreImage() {
|
||||
if ( ! check_ajax_referer( 'restore', false, false ) || ! current_user_can( 'manage_options' ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );
|
||||
}
|
||||
|
||||
wp_suspend_cache_addition( true );
|
||||
$image_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
|
||||
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
$nextgen_image = $nextgen_gallery->getNextgenImage( $image_id );
|
||||
$image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
|
||||
if ( $nextgen_image->isOptimized() ) {
|
||||
$restored = $nextgen_image->restore();
|
||||
|
||||
if ( ! is_wp_error( $restored ) ) {
|
||||
$optimization_data = $nextgen_image->getOptimizationData();
|
||||
$optimized_size = $optimization_data->get_final_size();
|
||||
$original_size = $optimization_data->get_original_size();
|
||||
$webp_optimized_size = $optimization_data->get_extra_data()->get_webp_main_size();
|
||||
$image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size );
|
||||
$image_statistics->deductFromField( 'optimized_size', $optimized_size );
|
||||
$image_statistics->deductFromField( 'original_size', $original_size );
|
||||
$image_statistics->save();
|
||||
}
|
||||
}
|
||||
|
||||
$nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
|
||||
echo $nextgen_gallery->getMediaColumnContent( $image_id );
|
||||
die();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Bulk optimization
|
||||
*/
|
||||
/*# sourceMappingURL=custom-folders.css.map */
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":[],"names":[],"mappings":"","file":"custom-folders.css"}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Bulk optimization
|
||||
*/
|
||||
|
||||
#WBCR {
|
||||
// less code
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user