Robin image optimizer — save money on image compression - Version 1.3.7

Version Description

  • Fixed: Minor errors
Download this release

Release Info

Developer webcraftic
Plugin Icon 128x128 Robin image optimizer — save money on image compression
Version 1.3.7
Comparing to
See all releases

Code changes from version 1.1.5 to 1.3.7

Files changed (186) hide show
  1. admin/activation.php +52 -29
  2. admin/ajax/backup.php +94 -57
  3. admin/ajax/bulk-optimization.php +268 -0
  4. admin/ajax/check-servers-status.php +0 -56
  5. admin/ajax/index.php +2 -0
  6. admin/ajax/logs.php +24 -0
  7. admin/ajax/meta-migrations.php +174 -0
  8. admin/ajax/multisite-update-current-blog.php +29 -0
  9. admin/ajax/optimization.php +0 -46
  10. admin/ajax/select-server.php +0 -31
  11. admin/ajax/settings.php +37 -0
  12. admin/assets/css/base-statistic.css +657 -436
  13. admin/assets/css/base-statistic.css.map +1 -0
  14. admin/assets/css/base-statistic.less +353 -79
  15. admin/assets/css/index.php +2 -0
  16. admin/assets/css/statistic.css +204 -166
  17. admin/assets/css/sweetalert-custom.css +156 -0
  18. admin/assets/css/sweetalert-custom.css.map +1 -0
  19. admin/assets/css/sweetalert-custom.less +185 -0
  20. admin/assets/css/sweetalert2.css +716 -0
  21. admin/assets/img/index.php +2 -0
  22. admin/assets/index.php +2 -0
  23. admin/assets/js/bulk-optimization.js +490 -0
  24. admin/assets/js/bulk-optimozation.js +0 -121
  25. admin/assets/js/check-servers-status.js +0 -82
  26. admin/assets/js/index.php +2 -0
  27. admin/assets/js/meta-migrations.js +52 -0
  28. admin/assets/js/modals.js +59 -0
  29. admin/assets/js/restore-backup.js +115 -0
  30. admin/assets/js/single-optimization.js +12 -4
  31. admin/assets/js/sweetalert2.js +1641 -0
  32. admin/boot.php +267 -94
  33. admin/includes/classes/class-rio-nextgen-landing.php +61 -0
  34. admin/includes/classes/class-rio-optimize-template.php +226 -0
  35. admin/includes/classes/index.php +2 -0
  36. admin/includes/index.php +2 -0
  37. admin/includes/sidebar-widgets.php +83 -0
  38. admin/index.php +1 -0
  39. admin/pages/class-rio-license.php +91 -0
  40. admin/pages/class-rio-log.php +164 -0
  41. admin/pages/class-rio-page.php +85 -0
  42. admin/pages/class-rio-settings.php +485 -0
  43. admin/pages/class-rio-statistic.php +252 -0
  44. admin/pages/index.php +2 -0
  45. admin/pages/log.php +0 -139
  46. admin/pages/settings.php +0 -421
  47. admin/pages/statistic.php +0 -479
  48. includes/class-rio-plugin.php +158 -0
  49. includes/class.plugin.php +0 -193
  50. includes/classes/class-rio-attachment.php +893 -0
  51. includes/classes/class-rio-backup.php +537 -0
  52. includes/classes/class-rio-cron.php +183 -0
  53. includes/classes/class-rio-image-statistic.php +323 -0
  54. includes/classes/class-rio-media-library.php +709 -0
  55. includes/classes/class-rio-multisite.php +54 -0
  56. includes/classes/class-rio-optimization-tools.php +57 -0
  57. includes/classes/class-rio-views.php +100 -0
  58. includes/classes/class.attachment.php +0 -488
  59. includes/classes/class.backup.php +0 -348
  60. includes/classes/class.cron.php +0 -115
  61. includes/classes/class.image-processor-abstract.php +0 -78
  62. includes/classes/class.image-processor-clearfy1.php +0 -139
  63. includes/classes/class.image-processor-resmush.php +0 -96
  64. includes/classes/class.image-processor-smushpro.php +0 -85
  65. includes/classes/class.image-processor-webcraftic.php +0 -110
  66. includes/classes/class.image-statistic.php +0 -165
  67. includes/classes/class.logger.php +0 -88
  68. includes/classes/class.media-library.php +0 -533
  69. includes/classes/class.optimization-tools.php +0 -47
  70. includes/classes/index.php +2 -0
  71. includes/classes/logger/class-rio-log-export.php +251 -0
  72. includes/classes/logger/class-rio-log-reader.php +72 -0
  73. includes/classes/logger/class-rio-logger.php +411 -0
  74. includes/classes/models/class-rio-attachment-extra-data.php +81 -0
  75. includes/classes/models/class-rio-base-active-record.php +97 -0
  76. includes/classes/models/class-rio-base-extra-data.php +53 -0
  77. includes/classes/models/class-rio-base-helper.php +24 -0
  78. includes/classes/models/class-rio-base-object.php +104 -0
  79. includes/classes/models/class-rio-process-queue-table.php +968 -0
  80. includes/classes/models/class-rio-server-smushit-extra-data.php +21 -0
  81. includes/classes/processors/class-rio-server-abstract.php +176 -0
  82. includes/classes/processors/class-rio-server-clearfy1.php +266 -0
  83. includes/classes/processors/class-rio-server-resmush.php +144 -0
  84. includes/classes/processors/class-rio-server-smushpro.php +142 -0
  85. includes/classes/processors/class-rio-server-webcraftic.php +133 -0
  86. includes/classes/processors/index.php +2 -0
  87. includes/functions.php +398 -79
  88. includes/index.php +3 -0
  89. index.php +2 -0
  90. languages/index.php +2 -0
  91. languages/robin-image-optimizer-ru_RU.mo +0 -0
  92. languages/robin-image-optimizer-ru_RU.po +475 -285
  93. libs/addons/admin/ajax/folders.php +434 -0
  94. libs/addons/admin/ajax/optimization.php +61 -0
  95. libs/addons/admin/assets/css/custom-folders.css +6 -0
  96. libs/addons/admin/assets/css/custom-folders.css.map +1 -0
  97. libs/addons/admin/assets/css/custom-folders.less +11 -0
  98. {updates → libs/addons/admin/assets/css}/index.php +0 -0
  99. libs/addons/admin/assets/css/jquery-file-tree.css +318 -0
  100. libs/addons/admin/assets/css/other-media.css +210 -0
  101. libs/addons/admin/assets/img/file-tree/application.png +0 -0
  102. libs/addons/admin/assets/img/file-tree/code.png +0 -0
  103. libs/addons/admin/assets/img/file-tree/css.png +0 -0
  104. libs/addons/admin/assets/img/file-tree/db.png +0 -0
  105. libs/addons/admin/assets/img/file-tree/directory-lock.png +0 -0
  106. libs/addons/admin/assets/img/file-tree/directory.png +0 -0
  107. libs/addons/admin/assets/img/file-tree/doc.png +0 -0
  108. libs/addons/admin/assets/img/file-tree/file-lock.png +0 -0
  109. libs/addons/admin/assets/img/file-tree/file.png +0 -0
  110. libs/addons/admin/assets/img/file-tree/film.png +0 -0
  111. libs/addons/admin/assets/img/file-tree/flash.png +0 -0
  112. libs/addons/admin/assets/img/file-tree/folder_open.png +0 -0
  113. libs/addons/admin/assets/img/file-tree/html.png +0 -0
  114. libs/addons/admin/assets/img/file-tree/java.png +0 -0
  115. libs/addons/admin/assets/img/file-tree/linux.png +0 -0
  116. libs/addons/admin/assets/img/file-tree/music.png +0 -0
  117. libs/addons/admin/assets/img/file-tree/pdf.png +0 -0
  118. libs/addons/admin/assets/img/file-tree/php.png +0 -0
  119. libs/addons/admin/assets/img/file-tree/picture.png +0 -0
  120. libs/addons/admin/assets/img/file-tree/ppt.png +0 -0
  121. libs/addons/admin/assets/img/file-tree/psd.png +0 -0
  122. libs/addons/admin/assets/img/file-tree/ruby.png +0 -0
  123. libs/addons/admin/assets/img/file-tree/script.png +0 -0
  124. libs/addons/admin/assets/img/file-tree/spinner.gif +0 -0
  125. libs/addons/admin/assets/img/file-tree/txt.png +0 -0
  126. libs/addons/admin/assets/img/file-tree/xls.png +0 -0
  127. libs/addons/admin/assets/img/file-tree/zip.png +0 -0
  128. libs/addons/admin/assets/img/quick-start-loader.gif +0 -0
  129. libs/addons/admin/assets/index.php +0 -0
  130. libs/addons/admin/assets/js/custom-folders.js +403 -0
  131. libs/addons/admin/assets/js/general.js +14 -0
  132. libs/addons/admin/assets/js/index.php +0 -0
  133. libs/addons/admin/assets/js/jquery-file-tree.js +213 -0
  134. libs/addons/admin/boot.php +48 -0
  135. libs/addons/admin/filters/backup.php +69 -0
  136. libs/addons/admin/filters/settings-page.php +66 -0
  137. libs/addons/admin/index.php +0 -0
  138. libs/addons/admin/pages/class-rio-statistic-folders-page.php +176 -0
  139. libs/addons/admin/pages/class-rio-statistic-nextgen-page.php +102 -0
  140. libs/addons/admin/pages/index.php +0 -0
  141. libs/addons/assets/img/.htaccess +37 -0
  142. libs/addons/assets/img/index.php +2 -0
  143. libs/addons/assets/img/test.jpg +0 -0
  144. libs/addons/assets/img/test.jpg.webp +0 -0
  145. libs/addons/assets/img/test.webp +0 -0
  146. libs/addons/assets/index.php +2 -0
  147. libs/addons/assets/js/index.php +2 -0
  148. libs/addons/assets/js/picturefill.min.js +5 -0
  149. libs/addons/includes/classes/class.backup.php +364 -0
  150. libs/addons/includes/classes/class.custom-folders.php +659 -0
  151. libs/addons/includes/classes/class.folder-image.php +479 -0
  152. libs/addons/includes/classes/class.folder.php +330 -0
  153. libs/addons/includes/classes/class.folders-list-table.php +260 -0
  154. libs/addons/includes/classes/class.gallery-nextgen.php +573 -0
  155. libs/addons/includes/classes/class.image-nextgen.php +532 -0
  156. libs/addons/includes/classes/class.image-statistic-folders.php +332 -0
  157. libs/addons/includes/classes/class.image-statistic-nextgen.php +237 -0
  158. libs/addons/includes/classes/helpers/class.url.php +49 -0
  159. libs/addons/includes/classes/index.php +0 -0
  160. libs/addons/includes/classes/models/class.folders-extra-data.php +259 -0
  161. libs/addons/includes/classes/models/class.nextgen-extra-data.php +273 -0
  162. libs/addons/includes/classes/models/class.webp-extra-data.php +157 -0
  163. libs/addons/includes/classes/webp/class-webp-api.php +339 -0
  164. libs/addons/includes/classes/webp/class-webp-delivery.php +292 -0
  165. libs/addons/includes/classes/webp/class-webp-html-image-urls-replacer.php +23 -0
  166. libs/addons/includes/classes/webp/class-webp-html-picture-tags.php +17 -0
  167. libs/addons/includes/classes/webp/class-webp-listener.php +511 -0
  168. libs/addons/includes/classes/webp/class-webp-server.php +338 -0
  169. libs/addons/includes/classes/webp/composer.json +5 -0
  170. libs/addons/includes/classes/webp/composer.lock +73 -0
  171. libs/addons/includes/classes/webp/vendor/autoload.php +7 -0
  172. libs/addons/includes/classes/webp/vendor/composer/ClassLoader.php +445 -0
  173. libs/addons/includes/classes/webp/vendor/composer/LICENSE +21 -0
  174. libs/addons/includes/classes/webp/vendor/composer/autoload_classmap.php +9 -0
  175. libs/addons/includes/classes/webp/vendor/composer/autoload_namespaces.php +9 -0
  176. libs/addons/includes/classes/webp/vendor/composer/autoload_psr4.php +10 -0
  177. libs/addons/includes/classes/webp/vendor/composer/autoload_real.php +52 -0
  178. libs/addons/includes/classes/webp/vendor/composer/autoload_static.php +31 -0
  179. libs/addons/includes/classes/webp/vendor/composer/installed.json +59 -0
  180. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/.php_cs.dist +19 -0
  181. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/README.md +156 -0
  182. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/composer.json +58 -0
  183. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/phpunit.xml.dist +30 -0
  184. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/ImageUrlReplacer.php +212 -0
  185. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/PictureTags.php +202 -0
  186. libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/simple_html_dom/simple_html_dom.inc +1490 -0
admin/activation.php CHANGED
@@ -1,42 +1,65 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  /**
4
- * Activator for the Robin image optimizer
5
  *
6
- * @author Webcraftic <wordpress.webraftic@gmail.com>
7
- * @copyright (c) 09.09.2017, Webcraftic
8
- * @see Factory409_Activator
9
- * @version 1.0
10
  */
 
 
11
 
12
- // Exit if accessed directly
13
- if( !defined('ABSPATH') ) {
14
- exit;
15
- }
 
 
 
 
 
 
16
 
17
- class WIO_Activation extends Wbcr_Factory409_Activator {
18
 
19
- /**
20
- * Runs activation actions.
21
- *
22
- * @since 1.0.0
23
- */
24
- public function activate()
25
- {
26
- WIO_Plugin::app()->updateOption('image_optimization_server', 'server_1');
27
- WIO_Plugin::app()->updateOption('backup_origin_images', 1);
28
- WIO_Plugin::app()->updateOption('save_exif_data', 1);
29
 
30
- WIO_Cron::check();
 
31
  }
32
 
33
- /**
34
- * Runs activation actions.
35
- *
36
- * @since 1.0.0
37
- */
38
- public function deactivate()
39
- {
40
- WIO_Cron::stop();
41
  }
 
 
42
  }
 
1
  <?php
2
 
3
+ /**
4
+ * Activator for the Robin image optimizer
5
+ *
6
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
7
+ * @copyright (c) 09.09.2017, Webcraftic
8
+ * @see Factory412_Activator
9
+ * @version 1.0
10
+ */
11
+
12
+ // Exit if accessed directly
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ class WIO_Activation extends Wbcr_Factory412_Activator {
18
+
19
  /**
20
+ * Runs activation actions.
21
  *
22
+ * @since 1.0.0
23
+ * @throws \Exception
 
 
24
  */
25
+ public function activate() {
26
+ WRIO_Logger::info( 'Parent plugin start installation!' );
27
 
28
+ WRIO_Plugin::app()->updatePopulateOption( 'image_optimization_server', 'server_1' );
29
+ WRIO_Plugin::app()->updatePopulateOption( 'backup_origin_images', 1 );
30
+ WRIO_Plugin::app()->updatePopulateOption( 'save_exif_data', 1 );
31
+
32
+ if ( function_exists( 'wrio_is_license_activate' ) && wrio_is_license_activate() ) {
33
+ WRIO_Logger::info( 'Premium plugin start installation!' );
34
+ require_once( WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php' );
35
+ wrio_premium_activate();
36
+ WRIO_Logger::info( 'Premium plugin installation complete!' );
37
+ }
38
 
39
+ RIO_Process_Queue::try_create_plugin_tables();
40
 
41
+ WRIO_Logger::info( 'Parent plugin installation complete!' );
42
+ }
43
+
44
+ /**
45
+ * Runs activation actions.
46
+ *
47
+ * @since 1.0.0
48
+ */
49
+ public function deactivate() {
50
+ WRIO_Logger::info( 'Parent plugin start deactivation!' );
51
 
52
+ if ( class_exists( 'WRIO_Cron' ) ) {
53
+ WRIO_Cron::stop();
54
  }
55
 
56
+ if ( function_exists( 'wrio_is_license_activate' ) && wrio_is_license_activate() ) {
57
+ WRIO_Logger::info( 'Premium plugin start deactivation!' );
58
+ require_once( WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php' );
59
+ wrio_premium_deactivate();
60
+ WRIO_Logger::info( 'Premium plugin deactivation complete!' );
 
 
 
61
  }
62
+
63
+ WRIO_Logger::info( 'Parent plugin deactivation complete!' );
64
  }
65
+ }
admin/ajax/backup.php CHANGED
@@ -1,66 +1,103 @@
1
  <?php
2
- /**
3
- * Backup
4
- * @author Webcraftic <wordpress.webraftic@gmail.com>
5
- * @copyright (c) 22.10.2018, Webcraftic
6
- * @version 1.0
7
- */
8
-
9
- /**
10
- * AJAX обработчик массовой оптимизации изображений со страницы статистики
11
- */
12
- add_action('wp_ajax_wio_restore_backup', function () {
13
- check_admin_referer('wio-iph');
14
-
15
- $max_process_per_request = 5;
16
- $total = $_POST['total'];
17
-
18
- $media_library = new WIO_MediaLibrary();
19
-
20
- if( $total == '?' ) {
21
- $total = $media_library->getOptimizedCount();
22
- }
23
- $restored_data = $media_library->restoreAllFromBackup($max_process_per_request);
24
 
25
- $restored_data['total'] = $total;
26
- if( $total ) {
27
- $restored_data['percent'] = 100 - ($restored_data['remain'] * 100 / $total);
28
- } else {
29
- $restored_data['percent'] = 0;
30
- }
31
 
32
- // если изображения закончились - посылаем команду завершения
33
- if( $restored_data['remain'] <= 0 ) {
34
- $restored_data['end'] = true;
35
- }
36
- wp_send_json($restored_data);
37
- });
38
 
39
- /**
40
- * AJAX обработчик очистки папки с бекапами
41
- */
42
- add_action('wp_ajax_wio_clear_backup', function () {
43
- check_admin_referer('wio-iph');
44
 
45
- $backup = new WIO_Backup();
46
- $backup->removeBackupDir();
47
- wp_send_json(true);
48
- });
49
 
50
- /**
51
- * AJAX обработчик массовой сохранения уровня сжатия
52
- */
53
- add_action('wp_ajax_wio_settings_update_level', function () {
54
- check_admin_referer('wio-iph');
55
 
56
- $level = $_POST['level'];
 
 
57
 
58
- if( !$level ) {
59
- die();
60
- }
61
- if( !in_array($level, array('normal', 'aggresive', 'ultra')) ) {
62
- die();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
- WIO_Plugin::app()->updateOption('image_optimization_level', $level);
65
- die();
66
- });
 
 
 
 
1
  <?php
2
+ /**
3
+ * Back-up related filters.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 2018 Webraftic Ltd
7
+ * @version 1.0
8
+ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
 
 
14
 
15
+ /**
16
+ * AJAX обработчик восстановления из резервной копии
17
+ */
18
+ add_action( 'wp_ajax_wio_restore_backup', function () {
19
+ check_admin_referer( 'wio-iph' );
 
20
 
21
+ if ( ! current_user_can( 'manage_options' ) ) {
22
+ wp_die( - 1 );
23
+ }
 
 
24
 
25
+ $max_process_per_request = 25;
 
 
 
26
 
27
+ //$blog_id = WRIO_Plugin::app()->request->post( 'blog_id', null, true );
 
 
 
 
28
 
29
+ /*if ( $blog_id !== null ) {
30
+ switch_to_blog( $blog_id );
31
+ }*/
32
 
33
+ // Total number of remained images to restore
34
+ $remane_count = 0;
35
+
36
+ $total = 0;
37
+
38
+ $filter_results = apply_filters( 'wbcr/rio/backup/restore_filter', $max_process_per_request );
39
+
40
+ if ( isset( $filter_results['remane'] ) ) {
41
+ $remane_count += $filter_results['remane'];
42
+ }
43
+
44
+ if ( isset( $filter_results['total'] ) ) {
45
+ $total += $filter_results['total'];
46
+ }
47
+
48
+ $media_library = WRIO_Media_Library::get_instance();
49
+
50
+ $total += $media_library->getOptimizedCount();
51
+
52
+ $restored_data = $media_library->restoreAllFromBackup( $max_process_per_request );
53
+
54
+ if ( isset( $restored_data['remain'] ) ) {
55
+ $remane_count += $restored_data['remain'];
56
+ }
57
+
58
+ /*if ( $blog_id !== null ) {
59
+ restore_current_blog();
60
+ }*/
61
+
62
+ $restored_data['total'] = $total;
63
+
64
+ if ( $total > 0 ) {
65
+ $restored_data['percent'] = 100 - ( $remane_count * 100 / $total );
66
+ } else {
67
+ $restored_data['percent'] = 0;
68
+ }
69
+
70
+ // если изображения закончились - посылаем команду завершения
71
+ if ( $remane_count <= 0 ) {
72
+ $restored_data['end'] = true;
73
+ }
74
+
75
+ wp_send_json( $restored_data );
76
+ } );
77
+
78
+ /**
79
+ * AJAX обработчик очистки папки с бекапами
80
+ */
81
+ add_action( 'wp_ajax_wio_clear_backup', function () {
82
+ check_admin_referer( 'wio-iph' );
83
+
84
+ if ( ! current_user_can( 'manage_options' ) ) {
85
+ wp_die( - 1 );
86
+ }
87
+
88
+ $backup = WIO_Backup::get_instance();
89
+ $blogs = WRIO_Plugin::app()->request->post( 'blogs', [], true );
90
+
91
+ if ( ! empty( $blogs ) ) {
92
+ foreach ( $blogs as $blog_id ) {
93
+ switch_to_blog( intval( $blog_id ) );
94
+ $backup->removeBlogBackupDir();
95
+ restore_current_blog();
96
  }
97
+ } else {
98
+ $backup->removeBackupDir();
99
+ }
100
+
101
+ wp_send_json( true );
102
+ } );
103
+
admin/ajax/bulk-optimization.php ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
4
+ * @copyright (c) 2018 Webraftic Ltd
5
+ * @version 1.0
6
+ */
7
+
8
+ // Exit if accessed directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Cron start
15
+ */
16
+ add_action( 'wp_ajax_wrio-cron-start', function () {
17
+ check_ajax_referer( 'bulk_optimization' );
18
+
19
+ if ( ! current_user_can( 'manage_options' ) ) {
20
+ wp_die( - 1 );
21
+ }
22
+
23
+ $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
24
+
25
+ if ( empty( $scope ) ) {
26
+ wp_die( - 1 );
27
+ }
28
+
29
+ // where was runned cron
30
+ $cron_running_place = WRIO_Plugin::app()->getPopulateOption( 'cron_running', false );
31
+
32
+ if ( $scope == $cron_running_place ) {
33
+ wp_send_json_success();
34
+ }
35
+
36
+ WRIO_Plugin::app()->updatePopulateOption( 'cron_running', $scope );
37
+ WRIO_Cron::start();
38
+
39
+ wp_send_json_success();
40
+ } );
41
+
42
+ /**
43
+ * Cron stop
44
+ */
45
+ add_action( 'wp_ajax_wrio-cron-stop', function () {
46
+ check_ajax_referer( 'bulk_optimization' );
47
+
48
+ if ( ! current_user_can( 'manage_options' ) ) {
49
+ wp_die( - 1 );
50
+ }
51
+
52
+ WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false );
53
+ WRIO_Cron::stop();
54
+
55
+ wp_send_json_success();
56
+ } );
57
+
58
+ /**
59
+ * AJAX обработчик массовой оптимизации изображений со страницы статистики
60
+ */
61
+ add_action( 'wp_ajax_wrio-bulk-optimization-process', function () {
62
+ check_admin_referer( 'bulk_optimization' );
63
+
64
+ if ( ! current_user_can( 'manage_options' ) ) {
65
+ wp_die( - 1 );
66
+ }
67
+
68
+ $reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
69
+ $scope = WRIO_Plugin::app()->request->request( 'scope', null, true );
70
+
71
+ WRIO_Logger::info( sprintf( 'Start bulk optimization process! Scope: %s', $scope ) );
72
+
73
+ if ( empty( $scope ) ) {
74
+ wp_die( - 1 );
75
+ }
76
+
77
+ // Context class name. If plugin expands with add-ons
78
+ $class_name = 'WRIO_' . wrio_dashes_to_camel_case( $scope, true );
79
+
80
+ if ( ! class_exists( $class_name ) ) {
81
+ WRIO_Logger::error( sprintf( 'Bulk optimization error: Context class (%s) not found.', $class_name ) );
82
+
83
+ //todo: Temporary bug fix.
84
+ if ( 'media-library' === $scope ) {
85
+ $class_name = 'WRIO_Media_Library';
86
+ } else if ( 'custom-folders' === $scope ) {
87
+ $class_name = 'WRIO_Custom_Folders';
88
+ } else if ( 'nextgen-gallery' == $scope ) {
89
+ $class_name = 'WRIO_Nextgen_Gallery';
90
+ }
91
+
92
+ if ( ! class_exists( $class_name ) ) {
93
+ wp_send_json_error( [ 'error_message' => 'Context class not found.' ] );
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Create an instance of the class depending on the context in which scope user
99
+ * has runned optimization.
100
+ *
101
+ * @see WRIO_Media_Library
102
+ * @see WRIO_Custom_Folders
103
+ * @see WRIO_Nextgen_Gallery
104
+ */
105
+ $optimizer = new $class_name();
106
+
107
+ // в ajax запросе мы не знаем, получен ли он из мультиадминки или из обычной. Поэтому проверяем параметр, полученный из frontend
108
+ /*if ( isset( $_POST['multisite'] ) && (bool) $_POST['multisite'] ) {
109
+ $multisite = new WIO_Multisite;
110
+ $multisite->initHooks();
111
+ }*/
112
+
113
+ if ( $reset_current_error ) {
114
+ $optimizer->resetCurrentErrors(); // сбрасываем текущие ошибки оптимизации
115
+ }
116
+
117
+ $result = $optimizer->processUnoptimizedImages( 1 );
118
+
119
+ if ( is_wp_error( $result ) ) {
120
+ $error_massage = $result->get_error_message();
121
+
122
+ if ( empty( $error ) ) {
123
+ $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" );
124
+ }
125
+
126
+ WRIO_Logger::error( sprintf( 'Bulk optimization error: %s.', $result->get_error_message() ) );
127
+
128
+ wp_send_json_error( [ 'error_message' => $error_massage ] );
129
+ }
130
+
131
+ // если изображения закончились - посылаем команду завершения
132
+ if ( $result['remain'] <= 0 ) {
133
+ $result['end'] = true;
134
+ }
135
+
136
+ WRIO_Logger::info( sprintf( 'End bulk optimization process! Scope: %s. Remain: %d', $scope, $result['remain'] ) );
137
+
138
+ wp_send_json_success( $result );
139
+ } );
140
+
141
+ /**
142
+ * Переоптимизация аттачмента
143
+ */
144
+ add_action( 'wp_ajax_wio_reoptimize_image', function () {
145
+
146
+ if ( ! current_user_can( 'manage_options' ) ) {
147
+ wp_die( - 1 );
148
+ }
149
+
150
+ $default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
151
+
152
+ $attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
153
+ $level = WRIO_Plugin::app()->request->post( 'level', $default_level, true );
154
+
155
+ $backup = WIO_Backup::get_instance();
156
+ $media_library = WRIO_Media_Library::get_instance();
157
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
158
+
159
+ if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
160
+ echo $media_library->getMediaColumnContent( $attachment_id );
161
+ die();
162
+ }
163
+
164
+ $optimized_data = $media_library->optimizeAttachment( $attachment_id, $level );
165
+
166
+ if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
167
+ echo 'processing';
168
+ die();
169
+ }
170
+
171
+ echo $media_library->getMediaColumnContent( $attachment_id );
172
+ die();
173
+ } );
174
+
175
+ /**
176
+ * Восстановление аттачмента из резервной копии
177
+ */
178
+ add_action( 'wp_ajax_wio_restore_image', function () {
179
+
180
+ if ( ! current_user_can( 'manage_options' ) ) {
181
+ wp_die( - 1 );
182
+ }
183
+
184
+ $attachment_id = (int) WRIO_Plugin::app()->request->post( 'id' );
185
+
186
+ $media_library = WRIO_Media_Library::get_instance();
187
+ $wio_attachment = $media_library->getAttachment( $attachment_id );
188
+
189
+ if ( $wio_attachment->isOptimized() ) {
190
+ $media_library->restoreAttachment( $attachment_id );
191
+ }
192
+
193
+ echo $media_library->getMediaColumnContent( $attachment_id );
194
+ die();
195
+ } );
196
+
197
+ /**
198
+ * На странице массовой оптмизации есть поле для выбора сервера. Когда пользователь
199
+ * выберет какой-то сервер, выполняется этот ajax обработчик. Обработчик пингует выбранный
200
+ * пользователем сервер и возвращает статус пинга (если пинг успешен, то сервер переход в
201
+ * статус выбранный).
202
+ */
203
+ add_action( 'wp_ajax_wbcr-rio-check-servers-status', function () {
204
+
205
+ check_ajax_referer( 'bulk_optimization' );
206
+
207
+ if ( ! current_user_can( 'manage_options' ) ) {
208
+ wp_die( - 1 );
209
+ }
210
+
211
+ $server_name = WRIO_Plugin::app()->request->post( 'server_name' );
212
+
213
+ if ( empty( $server_name ) || ! in_array( $server_name, [
214
+ 'server_1',
215
+ 'server_2',
216
+ 'server_3',
217
+ 'server_4'
218
+ ] ) ) {
219
+ wp_send_json_error( [ 'error' => __( 'Server name is empty!', 'robin-image-optimizer' ) ] );
220
+ }
221
+
222
+ // Позволяем выбрать сервер, даже если он недоступен.
223
+ WRIO_Plugin::app()->updatePopulateOption( 'image_optimization_server', $server_name );
224
+
225
+ // Проверяем доступность сервер
226
+ // --------------------------------------------------------------------
227
+ $return_data = [ 'server_name' => $server_name ];
228
+
229
+ $server_url = wrio_get_server_url( $server_name );
230
+
231
+ $method = 'POST';
232
+ if ( $server_name == 'server_4' ) {
233
+ $api_url = $server_url . '/upload/' . wrio_generate_random_string( 16 ) . '/';
234
+ } else if ( $server_name == 'server_3' ) {
235
+ $api_url = $server_url . '/s.w.org/images/home/screen-themes.png';
236
+ $method = 'GET';
237
+ } else {
238
+ $api_url = $server_url;
239
+ }
240
+
241
+ $request = wp_remote_request( $api_url, [
242
+ 'method' => $method
243
+ ] );
244
+
245
+ if ( is_wp_error( $request ) ) {
246
+ $er_msg = $request->get_error_message();
247
+
248
+ if ( "server_2" == $server_name ) {
249
+ // Hostgator Issue.
250
+ if ( ! empty( $er_msg ) && strpos( $er_msg, 'SSL CA cert' ) !== false ) {
251
+ // Update DB for using http protocol.
252
+ WRIO_Plugin::app()->updatePopulateOption( 'use_http', 1 );
253
+ }
254
+ }
255
+
256
+ $return_data['error'] = $er_msg;
257
+ wp_send_json_error( $return_data );
258
+ }
259
+
260
+ $response_code = wp_remote_retrieve_response_code( $request );
261
+
262
+ if ( $response_code != 200 ) {
263
+ $return_data['error'] = 'Server response ' . $response_code;
264
+ wp_send_json_error( $return_data );
265
+ }
266
+
267
+ wp_send_json_success( $return_data );
268
+ } );
admin/ajax/check-servers-status.php DELETED
@@ -1,56 +0,0 @@
1
- <?php
2
- /**
3
- * Ajax действие, которое выполняется для проверки статуса серверов
4
- *
5
- * @author Webcraftic <wordpress.webraftic@gmail.com>
6
- * @copyright (c) 2018 Webraftic Ltd
7
- * @version 1.0
8
- */
9
-
10
- // Exit if accessed directly
11
- if( !defined('ABSPATH') ) {
12
- exit;
13
- }
14
-
15
- function wbcr_rio_check_servers_status()
16
- {
17
- $server_name = WIO_Plugin::app()->request->post('server_name');
18
-
19
- if( empty($server_name) || !in_array($server_name, array('server_1', 'server_2', 'server_3', 'server_4')) ) {
20
- wp_send_json_error(array('error' => 'Server name is empty!'));
21
- }
22
-
23
- $return_data = array('server_name' => $server_name);
24
-
25
- $server_url = wbcr_rio_get_server_url($server_name);
26
-
27
- $method = 'POST';
28
- if( $server_name == 'server_4' ) {
29
- $api_url = $server_url . '/upload/' . wbcr_rio_generate_random_string(16) . '/';
30
- } else if($server_name == 'server_3') {
31
- $api_url = $server_url . '/s.w.org/images/home/screen-themes.png';
32
- $method = 'GET';
33
- } else {
34
- $api_url = $server_url;
35
- }
36
-
37
- $request = wp_remote_request($api_url, array(
38
- 'method' => $method
39
- ));
40
-
41
- if( is_wp_error($request) ) {
42
- $return_data['error'] = $request->get_error_message();
43
- wp_send_json_error($return_data);
44
- }
45
-
46
- $response_code = wp_remote_retrieve_response_code($request);
47
-
48
- if( $response_code != 200 ) {
49
- $return_data['error'] = 'Server response ' . $response_code;
50
- wp_send_json_error($return_data);
51
- }
52
-
53
- wp_send_json_success($return_data);
54
- }
55
-
56
- add_action('wp_ajax_wbcr_rio_check_servers_status', 'wbcr_rio_check_servers_status');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/ajax/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/ajax/logs.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Used to clean-up logs.
5
+ */
6
+ add_action( 'wp_ajax_wrio_logs_cleanup', function () {
7
+ check_admin_referer( 'wrio_clean_logs', 'nonce' );
8
+
9
+ if ( ! current_user_can( 'manage_options' ) ) {
10
+ wp_die( - 1 );
11
+ }
12
+
13
+ if ( ! WRIO_Logger::clean_up() ) {
14
+ wp_send_json_error( [
15
+ 'message' => esc_html__( 'Failed to clean-up logs. Please try again later.', 'robin-image-optimizer' ),
16
+ 'type' => 'danger',
17
+ ] );
18
+ }
19
+
20
+ wp_send_json( [
21
+ 'message' => esc_html__( 'Logs clean-up successfully', 'robin-image-optimizer' ),
22
+ 'type' => 'success',
23
+ ] );
24
+ } );
admin/ajax/meta-migrations.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Ajax action to migrate old architecture based on post meta into new table.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
7
+ * @see RIO_Process_Queue for further information.
8
+ *
9
+ * @copyright (c) 2018 Webraftic Ltd
10
+ * @version 1.0
11
+ */
12
+
13
+ // Exit if accessed directly
14
+ if ( ! defined( 'ABSPATH' ) ) {
15
+ exit;
16
+ }
17
+
18
+ add_action( 'wp_ajax_wrio_meta_migrations', 'wbcr_rio_migrate_postmeta_to_process_queue' );
19
+
20
+ /**
21
+ * Migrating postmeta to newly created table.
22
+ *
23
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
24
+ * @since 1.3.0
25
+ * @see RIO_Process_Queue as referce for new table.
26
+ */
27
+ function wbcr_rio_migrate_postmeta_to_process_queue() {
28
+ global $wpdb;
29
+
30
+ check_admin_referer( 'wrio-meta-migrations' );
31
+
32
+ if ( ! current_user_can( 'manage_options' ) ) {
33
+ wp_die( - 1 );
34
+ }
35
+
36
+ $error = (int) WRIO_Plugin::app()->request->post( 'error', 0 );
37
+
38
+ if ( $error ) {
39
+ WRIO_Logger::error( 'Previous migration was not completed due to an error.' );
40
+ }
41
+
42
+ $limit = (int) WRIO_Plugin::app()->request->post( 'limit', 150 );
43
+
44
+ $processed_items = 0;
45
+
46
+ WRIO_Logger::info( 'Start meta migration. Limit ' . $limit );
47
+ WRIO_Logger::memory_usage();
48
+
49
+ $attachments = wbcr_rio_get_meta_to_migrate();
50
+
51
+ if ( isset( $attachments->posts ) && ( $attachments_total = count( $attachments->posts ) ) > 0 ) {
52
+
53
+ if ( $attachments_total < $limit ) {
54
+ $limit = $attachments_total;
55
+ }
56
+
57
+ WRIO_Logger::info( 'Finded ' . $attachments_total . ' attachments for migration.' );
58
+
59
+ if ( function_exists( 'wp_raise_memory_limit' ) ) {
60
+ wp_raise_memory_limit( 'image' );
61
+ }
62
+
63
+ WRIO_Logger::memory_usage();
64
+
65
+ /**
66
+ * @var WP_Post $attachment
67
+ */
68
+ for ( $i = 0; $i < $limit; $i ++ ) {
69
+ $attachment = $attachments->posts[ $i ];
70
+ $post_meta = get_post_custom( $attachment->ID );
71
+
72
+ $extra_data = new RIO_Attachment_Extra_Data();
73
+
74
+ $is_backed_up = false;
75
+ $original_size = 0;
76
+ $final_size = 0;
77
+
78
+ if ( isset( $post_meta['wio_backuped'][0] ) && $post_meta['wio_backuped'][0] ) {
79
+ $is_backed_up = true;
80
+ }
81
+
82
+ if ( isset( $post_meta['wio_thumbnails_count'][0] ) ) {
83
+ $extra_data->set_thumbnails_count( intval( $post_meta['wio_thumbnails_count'][0] ) );
84
+ }
85
+
86
+ if ( isset( $post_meta['wio_original_size'][0] ) ) {
87
+ $original_size = intval( $post_meta['wio_original_size'][0] );
88
+ }
89
+
90
+ if ( isset( $post_meta['wio_optimized_size'][0] ) ) {
91
+ $final_size = intval( $post_meta['wio_optimized_size'][0] );
92
+ }
93
+
94
+ if ( isset( $post_meta['wio_original_main_size'][0] ) ) {
95
+ $extra_data->set_original_main_size( intval( $post_meta['wio_original_main_size'][0] ) );
96
+ }
97
+
98
+ if ( isset( $post_meta['wio_error'][0] ) ) {
99
+ $extra_data->set_error( 'optimization' );
100
+ $extra_data->set_error_msg( $post_meta['wio_error'][0] );
101
+ }
102
+
103
+ $level = 'normal';
104
+
105
+ if ( isset( $post_meta['wio_optimization_level'][0] ) && ! empty( $post_meta['wio_optimization_level'][0] ) ) {
106
+ $level = $post_meta['wio_optimization_level'][0];
107
+ }
108
+
109
+ $data = [
110
+ 'server_id' => null,
111
+ 'object_id' => $attachment->ID,
112
+ 'object_name' => $wpdb->posts,
113
+ 'item_type' => 'attachment',
114
+ 'result_status' => ! $final_size ? 'error' : 'success',
115
+ 'processing_level' => $level,
116
+ 'is_backed_up' => $is_backed_up,
117
+ 'original_size' => $original_size,
118
+ 'final_size' => $final_size,
119
+ 'original_mime_type' => $attachment->post_mime_type,
120
+ 'final_mime_type' => $attachment->post_mime_type,
121
+ 'extra_data' => (string) $extra_data,
122
+ 'created_at' => time(),
123
+ ];
124
+
125
+ $format = [
126
+ '%s',
127
+ '%d',
128
+ '%s',
129
+ '%s',
130
+ '%s',
131
+ '%s',
132
+ '%d',
133
+ '%d',
134
+ '%d',
135
+ '%s',
136
+ '%s',
137
+ '%s',
138
+ '%d',
139
+ ];
140
+
141
+ $rows_inserted = $wpdb->insert( RIO_Process_Queue::table_name(), $data, $format );
142
+
143
+ if ( $rows_inserted > 0 ) {
144
+ $processed_items ++;
145
+
146
+ $attachment_id = absint( $attachment->ID );
147
+ $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id='{$attachment_id}' AND meta_key LIKE 'wio_%'" );
148
+ }
149
+ }
150
+
151
+ $left_items = $attachments_total - $processed_items;
152
+ $message = sprintf( __( 'left to migrate: %s items', 'robin-image-optimizer' ), $left_items );
153
+ $need_more_time = true;
154
+
155
+ WRIO_Logger::info( 'Succefull migrated ' . $processed_items . ' items.' );
156
+ } else {
157
+ WRIO_Logger::info( 'Succefull migrated all items. Finishing-up...' );
158
+
159
+ // Assumed to be 2 after 010105.php migration
160
+ WRIO_Plugin::app()->updateOption( 'db_version', 2 );
161
+
162
+ $need_more_time = false;
163
+ $message = __( 'Finishing-up...', 'robin-image-optimizer' );
164
+ }
165
+
166
+ WRIO_Logger::memory_usage();
167
+
168
+ wp_send_json_success( [
169
+ 'need_more_time' => $need_more_time,
170
+ 'message' => $message,
171
+ ] );
172
+ }
173
+
174
+
admin/ajax/multisite-update-current-blog.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Ajax действие, которое выполняется для смены текущего multisite блога
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 2018 Webraftic Ltd
7
+ * @version 1.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /*add_action( 'wp_ajax_wbcr_rio_update_current_blog', function () {
16
+ check_ajax_referer( 'update_blog_id', 'wpnonce' );
17
+ $blog_id = (int) WRIO_Plugin::app()->request->post( 'current_blog_id' );
18
+ $context = sanitize_text_field( WRIO_Plugin::app()->request->post( 'context' ) );
19
+ WRIO_Plugin::app()->updatePopulateOption( 'current_blog', $blog_id );
20
+ $image_statistics = WIO_OptimizationTools::getImageStatistics( $context );
21
+
22
+ switch_to_blog( $blog_id );
23
+ $statistic_data = $image_statistics->load();
24
+ restore_current_blog();
25
+
26
+ wp_send_json_success( array(
27
+ 'statistic' => $statistic_data,
28
+ ) );
29
+ } );*/
admin/ajax/optimization.php DELETED
@@ -1,46 +0,0 @@
1
- <?php
2
- /**
3
- * Optimization
4
- *
5
- * @author Webcraftic <wordpress.webraftic@gmail.com>
6
- * @copyright (c) 22.10.2018, Webcraftic
7
- * @version 1.0
8
- */
9
-
10
- /**
11
- * AJAX обработчик массовой оптимизации изображений со страницы статистики
12
- */
13
- add_action('wp_ajax_wio_process_images', function () {
14
- check_admin_referer('wio-iph');
15
-
16
- $cron_mode = (bool)WIO_Plugin::app()->request->request('cron_mode');
17
- $reset_current_error = (bool)WIO_Plugin::app()->request->request('reset_current_error');
18
-
19
- $media_library = new WIO_MediaLibrary();
20
- if ( $reset_current_error ) {
21
- $media_library->resetCurrentErrors(); // сбрасываем текущие ошибки оптимизации
22
- }
23
-
24
- if( $cron_mode ) {
25
- $cron_running = WIO_Plugin::app()->getOption('cron_running', false);
26
- if( $cron_running ) {
27
- WIO_Plugin::app()->updateOption('cron_running', false);
28
- WIO_Cron::stop();
29
- } else {
30
- WIO_Plugin::app()->updateOption('cron_running', true);
31
- WIO_Cron::start();
32
- }
33
-
34
- wp_send_json(true);
35
- die();
36
- }
37
- $max_process_per_request = 1;
38
- $optimized_data = $media_library->processUnoptimizedAttachments($max_process_per_request);
39
-
40
- // если изображения закончились - посылаем команду завершения
41
- if( $optimized_data['remain'] <= 0 ) {
42
- $optimized_data['end'] = true;
43
- }
44
-
45
- wp_send_json($optimized_data);
46
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/ajax/select-server.php DELETED
@@ -1,31 +0,0 @@
1
- <?php
2
- /**
3
- * Ajax действие, которое выполняется для выбора нового сервера оптимизации
4
- *
5
- * @author Webcraftic <wordpress.webraftic@gmail.com>
6
- * @copyright (c) 2018 Webraftic Ltd
7
- * @version 1.0
8
- */
9
-
10
- // Exit if accessed directly
11
- if( !defined('ABSPATH') ) {
12
- exit;
13
- }
14
-
15
- function wbcr_rio_select_server()
16
- {
17
- $server_name = WIO_Plugin::app()->request->post('server_name');
18
-
19
- if( empty($server_name) ) {
20
- wp_send_json_error(array('error' => 'Server name is empty!'));
21
- }
22
-
23
- check_ajax_referer('wbcr_rio_select_' . $server_name);
24
-
25
- WIO_Plugin::app()->updateOption('image_optimization_server', $server_name);
26
-
27
- wp_send_json_success();
28
- }
29
-
30
- add_action('wp_ajax_wbcr_rio_select_server', 'wbcr_rio_select_server');
31
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/ajax/settings.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Ajax действие, которое выполняется при сохранении настроек
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 2018 Webraftic Ltd
7
+ * @version 1.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * AJAX обработчик массовой сохранения уровня сжатия
17
+ */
18
+ add_action( 'wp_ajax_wio_settings_update_level', function () {
19
+ check_admin_referer( 'wio-iph' );
20
+
21
+ if ( ! current_user_can( 'manage_options' ) ) {
22
+ wp_die( - 1 );
23
+ }
24
+
25
+ $level = sanitize_text_field( $_POST['level'] );
26
+
27
+ if ( ! $level ) {
28
+ die();
29
+ }
30
+
31
+ if ( ! in_array( $level, [ 'normal', 'aggresive', 'ultra' ] ) ) {
32
+ die();
33
+ }
34
+
35
+ WRIO_Plugin::app()->updatePopulateOption( 'image_optimization_level', $level );
36
+ die();
37
+ } );
admin/assets/css/base-statistic.css CHANGED
@@ -1,436 +1,657 @@
1
- /**
2
- * Styles for the Widget to be displayed in the Clearfy plugin
3
-
4
- * @author Alex Kovalev <alex.kovalevv@gmail.com>
5
- * @copyright Webcraftic 13.06.2018
6
- */
7
- #WBCR {
8
- /* Number display */
9
- /* Number and bars */
10
- /* Doughnut */
11
- /* Widget */
12
- /*@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
13
- .wio-overview-chart-container {
14
- float: none;
15
- margin-right: 0;
16
- }
17
- }
18
-
19
- @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
20
- .wio-overview-chart-container {
21
- float: none;
22
- margin-right: 0;
23
- }
24
-
25
- .wio-doughnut-legend {
26
- margin-top: 18px;
27
- }
28
-
29
- .wio-global-optim-phrase {
30
- padding-top: 0;
31
- width: auto;
32
- }
33
- }*/
34
- }
35
- #WBCR .wio-clear {
36
- clear: both;
37
- }
38
- #WBCR .wbcr-rio-servers {
39
- margin-top: 30px;
40
- background: none;
41
- padding: 0;
42
- }
43
- #WBCR .wbcr-rio-servers h4 {
44
- font-size: 15px;
45
- font-weight: 700;
46
- }
47
- #WBCR .wbcr-rio-servers button {
48
- padding: 5px 10px;
49
- border: 0;
50
- font-size: 11px;
51
- text-transform: uppercase !important;
52
- font-weight: bold;
53
- border-radius: 4px;
54
- outline: none;
55
- background: #f3f3f3;
56
- color: #656565;
57
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
58
- }
59
- #WBCR .wbcr-rio-servers button:active {
60
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
61
- }
62
- #WBCR .wbcr-rio-servers button.wbcr-rio-loading {
63
- width: 56px;
64
- font-size: 0;
65
- background: #f3f3f3 url("../img/quick-start-loader.gif") center no-repeat;
66
- }
67
- #WBCR .wbcr-rio-servers button.wbcr-rio-selected {
68
- background: #f3efe2;
69
- color: #d8d8d8;
70
- }
71
- #WBCR .wbcr-rio-servers button.wbcr-rio-selected:active {
72
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
73
- }
74
- #WBCR .wbcr-rio-servers table {
75
- width: 100%;
76
- box-sizing: border-box;
77
- border-spacing: 3px;
78
- background: #fff;
79
- border: 2px dashed #cac9c9;
80
- border-left: 0;
81
- border-right: 0;
82
- }
83
- #WBCR .wbcr-rio-servers table th,
84
- #WBCR .wbcr-rio-servers table td {
85
- padding: 16px 10px;
86
- text-align: center;
87
- }
88
- #WBCR .wbcr-rio-servers table th {
89
- background: #f3f3f3;
90
- color: #777777;
91
- box-shadow: 0 1px 0 #d8d8d8;
92
- }
93
- #WBCR .wbcr-rio-servers table th:nth-child(2n+1) {
94
- background: #f9f9f9;
95
- }
96
- #WBCR .wbcr-rio-servers table .wbcr-rio-server-check-proccess {
97
- display: inline-block;
98
- height: 10px;
99
- width: 30px;
100
- background: url("../img/quick-start-loader.gif") center no-repeat;
101
- }
102
- #WBCR .wbcr-rio-servers table .wbcr-rio-servers-list-item-selected {
103
- background: #fffbed;
104
- color: #cec5a8;
105
- font-weight: 600;
106
- }
107
- #WBCR .wbcr-rio-servers table .wbcr-rio-server-success {
108
- color: #8CC152;
109
- }
110
- #WBCR .wbcr-rio-servers table .wbcr-rio-server-error {
111
- color: #fb5d49;
112
- }
113
- #WBCR .wbcr-rio-servers table .wbcr-rio-server-warning {
114
- color: #ffb635;
115
- }
116
- #WBCR .wbcr-rio-servers .wbcr-rio-warning-message {
117
- padding: 20px;
118
- background: #efefef;
119
- font-size: 15px;
120
- color: #b7b7b7;
121
- font-style: italic;
122
- }
123
- #WBCR .wio-columns {
124
- overflow: hidden;
125
- padding: 15px 0;
126
- counter-reset: cols;
127
- }
128
- #WBCR .wio-columns [class^="col-"] {
129
- float: left;
130
- -webkit-box-sizing: border-box;
131
- -moz-box-sizing: border-box;
132
- box-sizing: border-box;
133
- }
134
- #WBCR .wio-columns .col-1-3 {
135
- width: 33.333%;
136
- padding-left: 28px;
137
- }
138
- #WBCR .wio-columns .col-2-3 {
139
- width: 66.666%;
140
- padding-left: 28px;
141
- }
142
- #WBCR .wio-columns .col-1-2 {
143
- width: 50%;
144
- padding: 0 20px;
145
- }
146
- #WBCR .wio-columns .col-statistics.col-statistics {
147
- width: 60%;
148
- }
149
- #WBCR .wio-columns .col-chart.col-chart {
150
- width: 40%;
151
- position: relative;
152
- padding: 20px;
153
- font-size: 12px;
154
- text-transform: uppercase;
155
- background: #f1f1f1b3;
156
- color: #abacaf;
157
- font-weight: bold;
158
- border-radius: 5px;
159
- margin-top: 10px;
160
- text-align: left;
161
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
162
- }
163
- #WBCR .wio-col {
164
- float: left;
165
- width: 50%;
166
- box-sizing: border-box;
167
- -webkit-flex-basis: 50%;
168
- -ms-flex-preferred-size: 50%;
169
- flex-basis: 50%;
170
- }
171
- #WBCR .wio-col {
172
- padding-right: 20px;
173
- }
174
- #WBCR .wio-col + .wio-col {
175
- padding-right: 0;
176
- padding-left: 50px;
177
- }
178
- #WBCR .wio-col:target {
179
- animation: wiohello 1s 3 linear backwards;
180
- }
181
- #WBCR .wio-number-you-optimized {
182
- margin-bottom: 1.35em;
183
- overflow: hidden;
184
- }
185
- #WBCR .wio-number-you-optimized #wio-total-optimized-attachments-pct {
186
- color: #828282;
187
- }
188
- #WBCR .wio-number-you-optimized .wio-number {
189
- display: table-cell;
190
- padding-right: 15px;
191
- font-size: 48px;
192
- font-weight: bold;
193
- line-height: 1;
194
- vertical-align: middle;
195
- white-space: nowrap;
196
- color: #828282;
197
- }
198
- #WBCR .wio-number-you-optimized .wio-text {
199
- display: table-cell;
200
- vertical-align: middle;
201
- overflow: hidden;
202
- font-size: 12px;
203
- color: #828282;
204
- }
205
- #WBCR .wio-number-you-optimized > p {
206
- display: table;
207
- }
208
- #WBCR .wio-bars {
209
- padding-right: 15px;
210
- }
211
- #WBCR .wio-bars p {
212
- font-size: 12px;
213
- margin-bottom: 5px;
214
- }
215
- #WBCR .wio-bars + .wio-number-you-optimized {
216
- border-bottom: 0;
217
- padding-top: 0.85em;
218
- }
219
- #WBCR .wio-bars + .wio-number-you-optimized p {
220
- color: #72a53b;
221
- }
222
- #WBCR .wio-bar-negative .wio-progress {
223
- background: #D2D3D6;
224
- }
225
- #WBCR .wio-bar-negative .wio-barnb {
226
- color: #9d9fa5;
227
- }
228
- #WBCR .wio-progress {
229
- height: 8px;
230
- transition: width .3s;
231
- /*.wio-bar-negative {
232
- width: 92% !important;
233
- }*/
234
- }
235
- #WBCR .wio-bar-positive .wio-progress {
236
- background: #8CC152;
237
- }
238
- #WBCR .wio-bar-positive .wio-barnb {
239
- color: #72a53b;
240
- }
241
- #WBCR .wio-bar-primary .wio-progress {
242
- background: #8CC152;
243
- }
244
- #WBCR .wio-bar-primary .wio-barnb {
245
- color: #72a53b;
246
- }
247
- #WBCR .wio-right-outside-number .wio-barnb {
248
- display: block;
249
- margin-right: -5.25em;
250
- text-align: right;
251
- font-weight: bold;
252
- line-height: .8;
253
- }
254
- #WBCR .wio-chart {
255
- position: relative;
256
- top: 1px;
257
- display: inline-block;
258
- vertical-align: middle;
259
- }
260
- #WBCR .wio-chart-container {
261
- position: relative;
262
- display: inline-block;
263
- margin-right: 5px;
264
- }
265
- #WBCR .wio-chart-container canvas {
266
- display: block;
267
- }
268
- #WBCR .wio-overview-chart-container {
269
- float: left;
270
- margin-right: 20px;
271
- }
272
- #WBCR .wio-chart-percent {
273
- position: absolute;
274
- left: 0;
275
- right: 0;
276
- top: 50%;
277
- margin-top: -0.5em;
278
- line-height: 0.8;
279
- text-align: center;
280
- font-size: 54px;
281
- font-weight: bold;
282
- color: #afafaf;
283
- }
284
- #WBCR .wio-chart-percent span {
285
- font-size: 20px;
286
- vertical-align: super;
287
- }
288
- #WBCR #wio-overview-chart-legend {
289
- overflow: hidden;
290
- }
291
- #WBCR .wio-doughnut-legend li {
292
- display: inline-block;
293
- position: relative;
294
- margin-bottom: 15px;
295
- border-radius: 5px;
296
- padding: 3px 8px 2px 31px;
297
- font-size: 9px;
298
- cursor: default;
299
- -webkit-transition: background-color 200ms ease-in-out;
300
- -moz-transition: background-color 200ms ease-in-out;
301
- -o-transition: background-color 200ms ease-in-out;
302
- transition: background-color 200ms ease-in-out;
303
- }
304
- #WBCR .wio-doughnut-legend li span {
305
- display: block;
306
- position: absolute;
307
- left: 0;
308
- top: 0;
309
- width: 25px;
310
- height: 25px;
311
- border-radius: 50%;
312
- }
313
- #WBCR .wio-optimize-button {
314
- width: 180px;
315
- padding: 12px 30px;
316
- background: #c9deb2;
317
- color: #586549;
318
- border: 0;
319
- box-shadow: none;
320
- font-size: 14px;
321
- text-transform: uppercase !important;
322
- font-weight: bold;
323
- border-radius: 4px;
324
- outline: none;
325
- }
326
- #WBCR .wio-optimize-button:active {
327
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
328
- }
329
- #WBCR .wio-optimize-button.wio-running {
330
- color: #a57b3c;
331
- background: #fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
332
- }
333
- #WBCR .wio-global-optim-phrase {
334
- width: 180px;
335
- padding-top: 20px;
336
- font-size: 14px;
337
- text-align: center;
338
- }
339
- #WBCR .wio-total-percent {
340
- color: #587f2e;
341
- }
342
- #WBCR #wio-start-msg-top,
343
- #WBCR #wio-start-msg-right,
344
- #WBCR #wio-start-msg-complete {
345
- display: none;
346
- }
347
- #WBCR .wio-text-left {
348
- text-align: left;
349
- }
350
- #WBCR span.wio-num {
351
- display: inline !important;
352
- position: inherit !important;
353
- }
354
- #WBCR .wio-image-optimize-board {
355
- padding-bottom: 0 !important;
356
- }
357
- #WBCR .wio-page-statistic {
358
- padding-left: 40px;
359
- }
360
- #WBCR .wio-page-statistic .wio-chart-percent {
361
- margin-top: -1.1em;
362
- }
363
- #WBCR .wio-widget {
364
- padding: 0 !important;
365
- }
366
- #WBCR .wio-widget .wio-chart-percent {
367
- font-size: 44px;
368
- line-height: 1;
369
- }
370
- #WBCR .wio-widget .wio-bars {
371
- width: 60%;
372
- margin-left: 155px;
373
- }
374
- #WBCR .wio-widget .col-chart.col-chart {
375
- width: 100%;
376
- }
377
- #WBCR .wio-widget .col-controls {
378
- width: 45%;
379
- padding-left: 5px;
380
- padding-top: 110px;
381
- }
382
- #WBCR .wio-widget .wio-doughnut-legend {
383
- /*padding-top:30px;*/
384
- text-align: left;
385
- }
386
- #WBCR .wio-widget .wio-widget-bottom {
387
- display: table;
388
- padding-top: 20px !important;
389
- width: 100%;
390
- text-align: right;
391
- }
392
- #WBCR .wio-widget .wio-widget-bottom li {
393
- display: table-cell;
394
- }
395
- #WBCR .wio-widget .wio-widget-bottom li:first-child {
396
- text-align: left;
397
- }
398
- @media (max-width: 830px) {
399
- #WBCR .wio [class^="col-"] {
400
- float: none;
401
- margin-bottom: 1.5em;
402
- }
403
- #WBCR .wio .col-1-3,
404
- #WBCR .wio .col-1-2 {
405
- width: auto;
406
- padding: 0 28px;
407
- clear: both;
408
- padding-top: 1em;
409
- }
410
- }
411
- @keyframes wiohello {
412
- 0%,
413
- 100% {
414
- background: #FFF;
415
- }
416
- 50% {
417
- background: #F4F7F9;
418
- }
419
- }
420
- @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
421
- #WBCR .wio-columns .col-statistics.col-statistics,
422
- #WBCR .wio-columns .col-chart.col-chart {
423
- width: 50%;
424
- }
425
- }
426
- @media (max-width: 808px) {
427
- #WBCR .wio-columns .col-statistics.col-statistics,
428
- #WBCR .wio-columns .col-chart.col-chart {
429
- width: auto;
430
- float: none;
431
- padding: 0;
432
- }
433
- #WBCR .wio-columns .col-chart.col-chart {
434
- margin-top: 3em;
435
- }
436
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Styles for the Widget to be displayed in the Clearfy plugin
3
+
4
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>
5
+ * @copyright Webcraftic 13.06.2018
6
+ */
7
+ #WBCR {
8
+ /* Doughnut */
9
+ /*@media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
10
+ .wio-overview-chart-container {
11
+ float: none;
12
+ margin-right: 0;
13
+ }
14
+ }
15
+
16
+ @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
17
+ .wio-overview-chart-container {
18
+ float: none;
19
+ margin-right: 0;
20
+ }
21
+
22
+ .wio-doughnut-legend {
23
+ margin-top: 18px;
24
+ }
25
+
26
+ .wio-global-optim-phrase {
27
+ padding-top: 0;
28
+ width: auto;
29
+ }
30
+ }*/
31
+ }
32
+ #WBCR .wio-clear {
33
+ clear: both;
34
+ }
35
+ #WBCR #io_folders_statistic-wbcr_clearfy-tab,
36
+ #WBCR #io_nextgen_gallery_statistic-wbcr_clearfy-tab {
37
+ display: none !important;
38
+ }
39
+ #WBCR .wrio-statistic-nav {
40
+ margin: 0;
41
+ background: #efefef;
42
+ }
43
+ #WBCR .wrio-statistic-nav ul {
44
+ margin: 0;
45
+ }
46
+ #WBCR .wrio-statistic-nav ul li {
47
+ position: relative;
48
+ display: inline-block;
49
+ margin: 0 0 0 0;
50
+ background: #ffffff;
51
+ box-shadow: 0 -2px 0 #eaeaea;
52
+ }
53
+ #WBCR .wrio-statistic-nav ul li:hover {
54
+ background: #f7f7f7;
55
+ }
56
+ #WBCR .wrio-statistic-nav ul li.active {
57
+ background: #f7f7f7;
58
+ border-top: 1px solid #d4d4d4;
59
+ border-left: 1px solid #d4d4d4;
60
+ border-right: 1px solid #d4d4d4;
61
+ border-bottom: 1px solid #f7f7f7;
62
+ margin-bottom: -1px;
63
+ }
64
+ #WBCR .wrio-statistic-nav ul li.active a {
65
+ color: #222;
66
+ }
67
+ #WBCR .wrio-statistic-nav ul li.active a .wrio-statistic-tab-percent {
68
+ border: 2px dashed #8bc34a;
69
+ color: #5e8237;
70
+ }
71
+ #WBCR .wrio-statistic-nav ul li.active .dashicons,
72
+ #WBCR .wrio-statistic-nav ul li.active .dashicons-before:before {
73
+ color: #ff8b66;
74
+ }
75
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab {
76
+ display: block;
77
+ padding: 10px 20px 10px 20px;
78
+ text-decoration: none;
79
+ color: #d4d4d4;
80
+ font-size: 22px;
81
+ line-height: 2;
82
+ }
83
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab:active,
84
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab:focus {
85
+ background: 0;
86
+ box-shadow: none;
87
+ outline: none;
88
+ }
89
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .dashicons,
90
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .dashicons-before:before {
91
+ display: inline-block;
92
+ width: 30px;
93
+ height: 30px;
94
+ font-size: 30px;
95
+ line-height: 1.5;
96
+ margin-right: 15px;
97
+ color: #d4d4d4;
98
+ }
99
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab .wrio-statistic-tab-percent {
100
+ display: inline-block;
101
+ width: 42px;
102
+ height: 42px;
103
+ border-radius: 100px;
104
+ border: 2px dashed #e4e4e4;
105
+ padding: 5px;
106
+ margin-left: 30px;
107
+ font-size: 14px;
108
+ font-weight: 600;
109
+ text-align: center;
110
+ color: #bdbdbd;
111
+ }
112
+ #WBCR .wrio-statistic-nav ul li .wrio-statistic-tab-premium-label:after {
113
+ display: inline-block;
114
+ position: absolute;
115
+ content: 'PRO';
116
+ background: #ff5722;
117
+ border-radius: 4px;
118
+ color: #fff;
119
+ font-size: 10px;
120
+ line-height: 1;
121
+ font-style: normal;
122
+ padding: 4px 6px;
123
+ margin-left: 4px;
124
+ vertical-align: top;
125
+ top: 10px;
126
+ left: auto;
127
+ right: 10px;
128
+ z-index: 11;
129
+ }
130
+ #WBCR .wrio-table {
131
+ width: 100%;
132
+ table-layout: fixed;
133
+ box-sizing: border-box;
134
+ border-spacing: 3px;
135
+ background: #fff;
136
+ border-top: 2px dashed #cac9c9;
137
+ }
138
+ #WBCR .wrio-table th,
139
+ #WBCR .wrio-table td {
140
+ padding: 16px 10px;
141
+ text-align: center;
142
+ }
143
+ #WBCR .wrio-table th {
144
+ background: #f3f3f3;
145
+ color: #777777;
146
+ box-shadow: 0 1px 0 #d8d8d8;
147
+ }
148
+ #WBCR .wrio-table th:nth-child(2n+1) {
149
+ background: #f9f9f9;
150
+ }
151
+ #WBCR .wrio-table tr.wrio-error {
152
+ background-color: #ffe9e9 !important;
153
+ }
154
+ #WBCR .wrio-table .wrio-table-spinner {
155
+ background: url("../img/quick-start-loader.gif") center center no-repeat;
156
+ }
157
+ #WBCR .wrio-table .wrio-table-highlighter {
158
+ display: inline-block;
159
+ padding: 3px 7px;
160
+ background: #f3f3f3;
161
+ }
162
+ #WBCR .wrio-table .wbcr-rio-server-success {
163
+ color: #8CC152;
164
+ }
165
+ #WBCR .wrio-table .wbcr-rio-server-error {
166
+ color: #fb5d49;
167
+ }
168
+ #WBCR .wrio-table .wbcr-rio-server-warning {
169
+ color: #ffb635;
170
+ }
171
+ #WBCR .wrio-table.wbcr-rio-folders-table td:nth-child(3) {
172
+ text-align: left;
173
+ }
174
+ #WBCR .wrio-servers {
175
+ padding: 40px 20px;
176
+ }
177
+ #WBCR .wrio-servers label span {
178
+ display: block;
179
+ font-weight: normal;
180
+ font-size: 12px;
181
+ color: #b7b2b2;
182
+ }
183
+ #WBCR .wrio-servers #wrio-change-optimization-server {
184
+ position: relative;
185
+ max-width: 400px;
186
+ margin-right: 15px;
187
+ margin-bottom: 0;
188
+ border: 1px solid #d2d0d0;
189
+ background: #efefef;
190
+ }
191
+ #WBCR .wrio-servers .wrio-servers-info {
192
+ margin: 0 0 0;
193
+ padding: 20px;
194
+ background: #fff;
195
+ }
196
+ #WBCR .wrio-servers .wrio-server-status-wrap {
197
+ margin-top: 8px;
198
+ }
199
+ #WBCR .wrio-servers .wrio-server-status-wrap .wrio-server-status {
200
+ background: transparent;
201
+ color: #fff;
202
+ padding: 3px 5px;
203
+ border-radius: 4px;
204
+ }
205
+ #WBCR .wrio-servers .wrio-server-status-wrap .wrio-server-status.wrio-down {
206
+ background: #ff5722;
207
+ }
208
+ #WBCR .wrio-servers .wrio-server-status-wrap .wrio-server-status.wrio-stable {
209
+ background: #8bc34a;
210
+ }
211
+ #WBCR .wrio-servers .wrio-server-status-wrap .wrio-server-status.wrio-server-check-proccess {
212
+ display: inline-block;
213
+ height: 10px;
214
+ width: 30px;
215
+ background: url("../img/quick-start-loader.gif") center no-repeat;
216
+ }
217
+ #WBCR .wio-columns {
218
+ overflow: hidden;
219
+ padding: 15px 0;
220
+ counter-reset: cols;
221
+ }
222
+ #WBCR .wio-columns [class^="col-"] {
223
+ float: left;
224
+ -webkit-box-sizing: border-box;
225
+ -moz-box-sizing: border-box;
226
+ box-sizing: border-box;
227
+ }
228
+ #WBCR .wio-columns .col-1-3 {
229
+ width: 33.333%;
230
+ padding-left: 28px;
231
+ }
232
+ #WBCR .wio-columns .col-2-3 {
233
+ width: 66.666%;
234
+ padding-left: 28px;
235
+ }
236
+ #WBCR .wio-columns .col-1-2 {
237
+ width: 50%;
238
+ padding: 0 20px;
239
+ }
240
+ #WBCR .wio-columns .col-statistics.col-statistics {
241
+ width: 60%;
242
+ }
243
+ #WBCR .wio-columns .col-chart.col-chart {
244
+ width: 40%;
245
+ position: relative;
246
+ padding: 20px;
247
+ font-size: 12px;
248
+ text-transform: uppercase;
249
+ background: #f1f1f1b3;
250
+ color: #abacaf;
251
+ font-weight: bold;
252
+ border-radius: 5px;
253
+ margin-top: 10px;
254
+ text-align: left;
255
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
256
+ }
257
+ #WBCR .wio-col {
258
+ float: left;
259
+ width: 50%;
260
+ box-sizing: border-box;
261
+ -webkit-flex-basis: 50%;
262
+ -ms-flex-preferred-size: 50%;
263
+ flex-basis: 50%;
264
+ }
265
+ #WBCR .wio-col {
266
+ padding-right: 20px;
267
+ }
268
+ #WBCR .wio-col + .wio-col {
269
+ padding-right: 0;
270
+ padding-left: 50px;
271
+ }
272
+ #WBCR .wio-col:target {
273
+ animation: wiohello 1s 3 linear backwards;
274
+ }
275
+ #WBCR .wio-number-you-optimized {
276
+ margin-bottom: 1.35em;
277
+ overflow: hidden;
278
+ }
279
+ #WBCR .wio-number-you-optimized #wio-total-optimized-attachments-pct {
280
+ color: #828282;
281
+ }
282
+ #WBCR .wio-number-you-optimized .wio-number {
283
+ display: table-cell;
284
+ padding-right: 15px;
285
+ font-size: 48px;
286
+ font-weight: bold;
287
+ line-height: 1;
288
+ vertical-align: middle;
289
+ white-space: nowrap;
290
+ color: #828282;
291
+ }
292
+ #WBCR .wio-number-you-optimized .wio-text {
293
+ display: table-cell;
294
+ vertical-align: middle;
295
+ overflow: hidden;
296
+ font-size: 12px;
297
+ color: #828282;
298
+ }
299
+ #WBCR .wio-number-you-optimized > p {
300
+ display: table;
301
+ }
302
+ #WBCR .wio-bars {
303
+ padding-right: 15px;
304
+ }
305
+ #WBCR .wio-bars p {
306
+ font-size: 12px;
307
+ margin-bottom: 5px;
308
+ }
309
+ #WBCR .wio-bars + .wio-number-you-optimized {
310
+ border-bottom: 0;
311
+ padding-top: 0.85em;
312
+ }
313
+ #WBCR .wio-bars + .wio-number-you-optimized p {
314
+ color: #72a53b;
315
+ }
316
+ #WBCR .wio-bar-negative .wio-progress {
317
+ background: #D2D3D6;
318
+ }
319
+ #WBCR .wio-bar-negative .wio-barnb {
320
+ color: #9d9fa5;
321
+ }
322
+ #WBCR .wio-progress {
323
+ height: 8px;
324
+ transition: width .3s;
325
+ /*.wio-bar-negative {
326
+ width: 92% !important;
327
+ }*/
328
+ }
329
+ #WBCR .wio-bar-positive .wio-progress {
330
+ background: #8CC152;
331
+ }
332
+ #WBCR .wio-bar-positive .wio-barnb {
333
+ color: #72a53b;
334
+ }
335
+ #WBCR .wio-bar-primary .wio-progress {
336
+ background: #8CC152;
337
+ }
338
+ #WBCR .wio-bar-primary .wio-barnb {
339
+ color: #72a53b;
340
+ }
341
+ #WBCR .wio-right-outside-number .wio-barnb {
342
+ display: block;
343
+ margin-right: -5.25em;
344
+ text-align: right;
345
+ font-weight: bold;
346
+ line-height: .8;
347
+ }
348
+ #WBCR .wio-chart {
349
+ position: relative;
350
+ top: 1px;
351
+ display: inline-block;
352
+ vertical-align: middle;
353
+ }
354
+ #WBCR .wio-chart-container {
355
+ position: relative;
356
+ display: inline-block;
357
+ margin-right: 5px;
358
+ }
359
+ #WBCR .wio-chart-container canvas {
360
+ display: block;
361
+ }
362
+ #WBCR .wio-overview-chart-container {
363
+ float: left;
364
+ margin-right: 20px;
365
+ }
366
+ #WBCR .wio-chart-percent {
367
+ position: absolute;
368
+ left: 0;
369
+ right: 0;
370
+ top: 50%;
371
+ margin-top: -0.5em;
372
+ line-height: 0.8;
373
+ text-align: center;
374
+ font-size: 54px;
375
+ font-weight: bold;
376
+ color: #afafaf;
377
+ }
378
+ #WBCR .wio-chart-percent span {
379
+ font-size: 20px;
380
+ vertical-align: super;
381
+ }
382
+ #WBCR #wio-overview-chart-legend {
383
+ overflow: hidden;
384
+ }
385
+ #WBCR .wio-doughnut-legend li {
386
+ display: inline-block;
387
+ position: relative;
388
+ margin-bottom: 15px;
389
+ border-radius: 5px;
390
+ padding: 3px 8px 2px 31px;
391
+ font-size: 9px;
392
+ cursor: default;
393
+ -webkit-transition: background-color 200ms ease-in-out;
394
+ -moz-transition: background-color 200ms ease-in-out;
395
+ -o-transition: background-color 200ms ease-in-out;
396
+ transition: background-color 200ms ease-in-out;
397
+ }
398
+ #WBCR .wio-doughnut-legend li span {
399
+ display: block;
400
+ position: absolute;
401
+ left: 0;
402
+ top: 0;
403
+ width: 25px;
404
+ height: 25px;
405
+ border-radius: 50%;
406
+ }
407
+ #WBCR .wio-optimize-button {
408
+ min-width: 180px;
409
+ padding: 12px 30px;
410
+ background: #c9deb2;
411
+ color: #586549;
412
+ border: 0;
413
+ box-shadow: none;
414
+ font-size: 14px;
415
+ text-transform: uppercase !important;
416
+ font-weight: bold;
417
+ border-radius: 4px;
418
+ outline: none;
419
+ }
420
+ #WBCR .wio-optimize-button:active {
421
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
422
+ }
423
+ #WBCR .wio-optimize-button:disabled {
424
+ background: #e2edd6;
425
+ color: #a5b295;
426
+ }
427
+ #WBCR .wio-optimize-button.wio-running {
428
+ color: #a57b3c;
429
+ background: #fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
430
+ padding-left: 50px;
431
+ }
432
+ #WBCR .wio-global-optim-phrase {
433
+ width: 180px;
434
+ padding-top: 20px;
435
+ font-size: 14px;
436
+ text-align: center;
437
+ }
438
+ #WBCR .wio-total-percent {
439
+ color: #587f2e;
440
+ }
441
+ #WBCR #wio-start-msg-top,
442
+ #WBCR #wio-start-msg-right,
443
+ #WBCR #wio-start-msg-complete {
444
+ display: none;
445
+ }
446
+ #WBCR .wio-text-left {
447
+ text-align: left;
448
+ }
449
+ #WBCR span.wio-num {
450
+ display: inline !important;
451
+ position: inherit !important;
452
+ }
453
+ #WBCR .wio-image-optimize-board {
454
+ padding-bottom: 0 !important;
455
+ }
456
+ #WBCR .wio-page-statistic {
457
+ padding-left: 40px;
458
+ }
459
+ #WBCR .wio-page-statistic .wio-chart-percent {
460
+ margin-top: -1.1em;
461
+ }
462
+ #WBCR .wrio-optimization-progress {
463
+ background: none;
464
+ padding: 0;
465
+ /*button {
466
+ padding: 5px 10px;
467
+ border: 0;
468
+ font-size: 11px;
469
+ text-transform: uppercase !important;
470
+ font-weight: bold;
471
+ border-radius: 4px;
472
+ outline: none;
473
+ background: @greyButtonBg;
474
+ color: @greyButtonColor;
475
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
476
+
477
+ &:active {
478
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
479
+ }
480
+
481
+ &.wbcr-rio-loading {
482
+ width: 56px;
483
+ font-size: 0;
484
+ background: @greyButtonBg url("../img/quick-start-loader.gif") center no-repeat;
485
+ }
486
+
487
+ &.wbcr-rio-selected {
488
+ background: #f3efe2;
489
+ color: #d8d8d8;
490
+
491
+ &:active {
492
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
493
+ }
494
+ }
495
+ }*/
496
+ }
497
+ #WBCR .wrio-optimization-progress h4 {
498
+ font-size: 15px;
499
+ font-weight: 700;
500
+ }
501
+ #WBCR .wrio-optimization-progress .wbcr-rio-warning-message {
502
+ padding: 20px;
503
+ background: #efefef;
504
+ font-size: 15px;
505
+ color: #b7b7b7;
506
+ font-style: italic;
507
+ }
508
+ #WBCR .wio-widget {
509
+ padding: 0 !important;
510
+ }
511
+ #WBCR .wio-widget .wio-chart-percent {
512
+ font-size: 44px;
513
+ line-height: 1;
514
+ }
515
+ #WBCR .wio-widget .wio-bars {
516
+ width: 60%;
517
+ margin-left: 155px;
518
+ }
519
+ #WBCR .wio-widget .col-chart.col-chart {
520
+ width: 100%;
521
+ }
522
+ #WBCR .wio-widget .col-controls {
523
+ width: 45%;
524
+ padding-left: 5px;
525
+ padding-top: 110px;
526
+ }
527
+ #WBCR .wio-widget .wio-doughnut-legend {
528
+ /*padding-top:30px;*/
529
+ text-align: left;
530
+ }
531
+ #WBCR .wio-widget .wio-widget-bottom {
532
+ display: table;
533
+ padding-top: 20px !important;
534
+ width: 100%;
535
+ text-align: right;
536
+ }
537
+ #WBCR .wio-widget .wio-widget-bottom li {
538
+ display: table-cell;
539
+ }
540
+ #WBCR .wio-widget .wio-widget-bottom li:first-child {
541
+ text-align: left;
542
+ }
543
+ #WBCR .factory-checkbox.wrio-checkbox-premium-label:after {
544
+ display: inline-block;
545
+ position: relative;
546
+ content: 'PRO';
547
+ background: #ff5722;
548
+ border-radius: 4px;
549
+ color: #fff;
550
+ font-size: 10px;
551
+ line-height: 1;
552
+ font-style: normal;
553
+ padding: 4px 6px;
554
+ margin-left: 4px;
555
+ vertical-align: top;
556
+ top: -8px;
557
+ left: -10px;
558
+ right: auto;
559
+ z-index: 11;
560
+ }
561
+ #WBCR .factory-checkbox-disabled input,
562
+ #WBCR .factory-checkbox-disabled button {
563
+ pointer-events: none;
564
+ cursor: not-allowed;
565
+ opacity: .65;
566
+ filter: alpha(opacity=65);
567
+ -webkit-box-shadow: none;
568
+ box-shadow: none;
569
+ }
570
+ #WBCR #wrio-webp-options h3,
571
+ #WBCR #wrio-error-log-options h3 {
572
+ font-size: 14px;
573
+ margin: 0 0 10px 0;
574
+ font-weight: 600;
575
+ color: #565656;
576
+ }
577
+ #WBCR #wrio-webp-options .wrio-webp-options-info,
578
+ #WBCR #wrio-error-log-options .wrio-webp-options-info {
579
+ color: #8a8787;
580
+ font-size: 12px;
581
+ }
582
+ #WBCR #wrio-webp-options ul,
583
+ #WBCR #wrio-error-log-options ul {
584
+ padding-left: 0;
585
+ }
586
+ #WBCR #wrio-webp-options ul li:after,
587
+ #WBCR #wrio-error-log-options ul li:after {
588
+ content: '';
589
+ display: block;
590
+ clear: both;
591
+ }
592
+ #WBCR #wrio-webp-options ul li label,
593
+ #WBCR #wrio-error-log-options ul li label {
594
+ font-weight: 600;
595
+ }
596
+ #WBCR #wrio-webp-options ul li .wrio-webp-options-radio,
597
+ #WBCR #wrio-error-log-options ul li .wrio-webp-options-radio,
598
+ #WBCR #wrio-webp-options ul li .wrio-error-log-options-checkbox,
599
+ #WBCR #wrio-error-log-options ul li .wrio-error-log-options-checkbox {
600
+ display: block;
601
+ float: left;
602
+ margin-top: 2px;
603
+ margin-right: 8px;
604
+ }
605
+ #WBCR #wrio-webp-options ul li .wrio-webp-options-radio:focus,
606
+ #WBCR #wrio-error-log-options ul li .wrio-webp-options-radio:focus,
607
+ #WBCR #wrio-webp-options ul li .wrio-error-log-options-checkbox:focus,
608
+ #WBCR #wrio-error-log-options ul li .wrio-error-log-options-checkbox:focus {
609
+ outline: none;
610
+ box-shadow: none;
611
+ }
612
+ #WBCR #wrio-webp-options ul li .wrio-webp-options-info,
613
+ #WBCR #wrio-error-log-options ul li .wrio-webp-options-info,
614
+ #WBCR #wrio-webp-options ul li .wrio-error-log-options-info,
615
+ #WBCR #wrio-error-log-options ul li .wrio-error-log-options-info {
616
+ padding-left: 25px;
617
+ }
618
+ @media (max-width: 830px) {
619
+ #WBCR .wio [class^="col-"] {
620
+ float: none;
621
+ margin-bottom: 1.5em;
622
+ }
623
+ #WBCR .wio .col-1-3,
624
+ #WBCR .wio .col-1-2 {
625
+ width: auto;
626
+ padding: 0 28px;
627
+ clear: both;
628
+ padding-top: 1em;
629
+ }
630
+ }
631
+ @keyframes wiohello {
632
+ 0%,
633
+ 100% {
634
+ background: #FFF;
635
+ }
636
+ 50% {
637
+ background: #F4F7F9;
638
+ }
639
+ }
640
+ @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
641
+ #WBCR .wio-columns .col-statistics.col-statistics,
642
+ #WBCR .wio-columns .col-chart.col-chart {
643
+ width: 50%;
644
+ }
645
+ }
646
+ @media (max-width: 808px) {
647
+ #WBCR .wio-columns .col-statistics.col-statistics,
648
+ #WBCR .wio-columns .col-chart.col-chart {
649
+ width: auto;
650
+ float: none;
651
+ padding: 0;
652
+ }
653
+ #WBCR .wio-columns .col-chart.col-chart {
654
+ margin-top: 3em;
655
+ }
656
+ }
657
+ /*# sourceMappingURL=base-statistic.css.map */
admin/assets/css/base-statistic.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sources":["base-statistic.less"],"names":[],"mappings":";;;;;;AAuBA;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,KAoBE;EACE,WAAA;;AArBJ,KA2BE;AA3BF,KA2B0C;EACtC,wBAAA;;AA5BJ,KAiCE;EACE,SAAA;EAEA,mBAAA;;AApCJ,KAiCE,oBAKE;EACE,SAAA;;AAvCN,KAiCE,oBAKE,GAGE;EACE,kBAAA;EACA,qBAAA;EAEA,eAAA;EACA,mBAAA;EACA,4BAAA;;AAEA,KAhBN,oBAKE,GAGE,GAQG;EACC,mBAAA;;AAGF,KApBN,oBAKE,GAGE,GAYG;EAEC,mBAAA;EACA,6BAAA;EACA,8BAAA;EACA,+BAAA;EACA,gCAAA;EACA,mBAAA;;AAPF,KApBN,oBAKE,GAGE,GAYG,OASC;EACE,WAAA;;AAVJ,KApBN,oBAKE,GAGE,GAYG,OASC,EAGE;EACE,0BAAA;EACA,cAAA;;AAdN,KApBN,oBAKE,GAGE,GAYG,OAkBC;AAlBF,KApBN,oBAKE,GAGE,GAYG,OAkBa,kBAAiB;EAC3B,cAAA;;AAxEZ,KAiCE,oBAKE,GAGE,GAmCE;EACE,cAAA;EACA,4BAAA;EACA,qBAAA;EACA,cAAA;EACA,eAAA;EACA,cAAA;;AAEA,KAnDR,oBAKE,GAGE,GAmCE,oBAQG;AAAS,KAnDlB,oBAKE,GAGE,GAmCE,oBAQa;EACT,aAAA;EACA,gBAAA;EACA,aAAA;;AAvFZ,KAiCE,oBAKE,GAGE,GAmCE,oBAcE;AA1FV,KAiCE,oBAKE,GAGE,GAmCE,oBAcc,kBAAiB;EAC3B,qBAAA;EACA,WAAA;EACA,YAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;EACA,cAAA;;AAjGZ,KAiCE,oBAKE,GAGE,GAmCE,oBAyBE;EACE,qBAAA;EACA,WAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,kBAAA;EACA,cAAA;;AAhHZ,KAiCE,oBAKE,GAGE,GA2EE,kCAAiC;EAlHrC,qBAAA;EACA,kBAAA;EACA,SAAS,KAAT;EACA,mBAAA;EACA,kBAAA;EACA,WAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,SAAA;EACA,UAAA;EACA,WAAA;EACA,WAAA;;AAjBJ,KA4HE;EACE,WAAA;EACA,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,gBAAA;EACA,8BAAA;;AAlIJ,KA4HE,YAQE;AApIJ,KA4HE,YAQM;EACF,kBAAA;EACA,kBAAA;;AAtIN,KA4HE,YAaE;EACE,mBAAA;EACA,cAAA;EACA,2BAAA;;AAEA,KAlBJ,YAaE,GAKG,UAAU;EACT,mBAAA;;AA/IR,KA4HE,YAuBE,GAAE;EACA,oCAAA;;AApJN,KA4HE,YA2BE;EACE,gBAAgB,wDAAhB;;AAxJN,KA4HE,YA+BE;EACE,qBAAA;EACA,gBAAA;EACA,mBAAA;;AA9JN,KA4HE,YAqCE;EACE,cAAA;;AAlKN,KA4HE,YAyCE;EACE,cAAA;;AAtKN,KA4HE,YA6CE;EACE,cAAA;;AAGF,KAjDF,YAiDG,uBACC,GAAE,UAAU;EACV,gBAAA;;AA/KR,KAoLE;EACE,kBAAA;;AArLJ,KAoLE,cAGE,MACE;EACE,cAAA;EACA,mBAAA;EACA,eAAA;EACA,cAAA;;AA5LR,KAoLE,cAYE;EACE,kBAAA;EACA,gBAAA;EACA,kBAAA;EACA,gBAAA;EACA,yBAAA;EACA,mBAAA;;AAtMN,KAoLE,cAqBE;EACE,aAAA;EACA,aAAA;EACA,gBAAA;;AA5MN,KAoLE,cA2BE;EACE,eAAA;;AAhNN,KAoLE,cA2BE,yBAGE;EACE,uBAAA;EACA,WAAA;EACA,gBAAA;EACA,kBAAA;;AAEA,KApCN,cA2BE,yBAGE,oBAMG;EACC,mBAAA;;AAGF,KAxCN,cA2BE,yBAGE,oBAUG;EACC,mBAAA;;AAGF,KA5CN,cA2BE,yBAGE,oBAcG;EACC,qBAAA;EACA,YAAA;EACA,WAAA;EACA,gBAAgB,iDAAhB;;AApOV,KA0OE;EACE,gBAAA;EACA,eAAA;EACA,mBAAA;;AA7OJ,KA0OE,aAKE;EACE,WAAA;EACA,8BAAA;EACA,2BAAA;EACA,sBAAA;;AAnPN,KA0OE,aAYE;EACE,cAAA;EACA,kBAAA;;AAxPN,KA0OE,aAiBE;EACE,cAAA;EACA,kBAAA;;AA7PN,KA0OE,aAsBE;EACE,UAAA;EACA,eAAA;;AAlQN,KA0OE,aA2BE,gBAAe;EACb,UAAA;;AAtQN,KA0OE,aA+BE,WAAU;EACR,UAAA;EACA,kBAAA;EACA,aAAA;EACA,eAAA;EACA,yBAAA;EACA,qBAAA;EACA,cAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,wCAAA;;AArRN,KAyRE;EACE,WAAA;EACA,UAAA;EACA,sBAAA;EACA,uBAAA;EACA,4BAAA;EACA,eAAA;;AA/RJ,KAkSE;EACE,mBAAA;;AAnSJ,KAsSE,SAAS;EACP,gBAAA;EACA,kBAAA;;AAxSJ,KA2SE,SAAQ;EACN,yCAAA;;AA5SJ,KA+SE;EACE,qBAAA;EACA,gBAAA;;AAjTJ,KA+SE,0BAIE;EACE,cAAA;;AApTN,KA+SE,0BAQE;EACE,mBAAA;EACA,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,cAAA;EACA,sBAAA;EACA,mBAAA;EACA,cAAA;;AA/TN,KA+SE,0BAmBE;EACE,mBAAA;EACA,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,cAAA;;AAGF,KA3BF,0BA2BI;EACA,cAAA;;AA3UN,KA+UE;EACE,mBAAA;;AAhVJ,KAmVE,UAAU;EACR,eAAA;EACA,kBAAA;;AArVJ,KAwVE,UAAU;EACR,gBAAA;EACA,mBAAA;;AA1VJ,KA6VE,UAAU,4BAA4B;EACpC,cAAA;;AA9VJ,KAiWE,kBACE;EACE,mBAAA;;AAnWN,KAiWE,kBAKE;EACE,cAAA;;AAvWN,KA2WE;EACE,WAAA;EACA,qBAAA;;;;;AA7WJ,KAoXE,kBACE;EACE,mBAAA;;AAtXN,KAoXE,kBAKE;EACE,cAAA;;AA1XN,KA8XE,iBACE;EACE,mBAAA;;AAhYN,KA8XE,iBAKE;EACE,cAAA;;AApYN,KAwYE,0BAA0B;EACxB,cAAA;EACA,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;;AA7YJ,KAkZE;EACE,kBAAA;EACA,QAAA;EACA,qBAAA;EACA,sBAAA;;AAtZJ,KAyZE;EACE,kBAAA;EACA,qBAAA;EACA,iBAAA;;AA5ZJ,KA+ZE,qBAAqB;EACnB,cAAA;;AAhaJ,KAmaE;EACE,WAAA;EACA,kBAAA;;AAraJ,KAwaE;EACE,kBAAA;EACA,OAAA;EACA,QAAA;EACA,QAAA;EACA,kBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,iBAAA;EACA,cAAA;;AAlbJ,KAqbE,mBAAmB;EACjB,eAAA;EACA,qBAAA;;AAvbJ,KA0bE;EACE,gBAAA;;AA3bJ,KA8bE,qBAAqB;EACnB,qBAAA;EACA,kBAAA;EACA,mBAAA;EACA,kBAAA;EACA,yBAAA;EACA,cAAA;EACA,eAAA;EACA,sDAAA;EACA,mDAAA;EACA,iDAAA;EACA,8CAAA;;AAzcJ,KA4cE,qBAAqB,GAAG;EACtB,cAAA;EACA,kBAAA;EACA,OAAA;EACA,MAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;;AAndJ,KAsdE;EACE,gBAAA;EACA,kBAAA;EACA,mBAAA;EACA,cAAA;EACA,SAAA;EACA,gBAAA;EACA,eAAA;EACA,oCAAA;EACA,iBAAA;EACA,kBAAA;EACA,aAAA;;AAEA,KAbF,qBAaG;EACC,8CAAA;;AAGF,KAjBF,qBAiBG;EACC,mBAAA;EACA,cAAA;;AAGF,KAtBF,qBAsBG;EACC,cAAA;EACA,wBAAgC,mDAAhC;EACA,kBAAA;;AA/eN,KAmfE;EACE,YAAA;EACA,iBAAA;EACA,eAAA;EACA,kBAAA;;AAvfJ,KA0fE;EACE,cAAA;;AA3fJ,KA8fE;AA9fF,KA8fsB;AA9ftB,KA8f4C;EACxC,aAAA;;AA/fJ,KAkgBE;EACE,gBAAA;;AAngBJ,KAsgBE,KAAI;EACF,0BAAA;EACA,4BAAA;;AAxgBJ,KA8gBE;EACE,4BAAA;;AA/gBJ,KAkhBE;EACE,kBAAA;;AAnhBJ,KAkhBE,oBAGE;EACE,kBAAA;;AAthBN,KA0hBE;EAEE,gBAAA;EACA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA7hBJ,KA0hBE,4BAKE;EACE,eAAA;EACA,gBAAA;;AAjiBN,KA0hBE,4BA0CE;EACE,aAAA;EACA,mBAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;;AAzkBN,KA6kBE;EACE,qBAAA;;AA9kBJ,KA6kBE,YAGE;EACE,eAAA;EACA,cAAA;;AAllBN,KA6kBE,YAQE;EACE,UAAA;EACA,kBAAA;;AAvlBN,KA6kBE,YAaE,WAAU;EACR,WAAA;;AA3lBN,KA6kBE,YAiBE;EACE,UAAA;EACA,iBAAA;EACA,kBAAA;;AAjmBN,KA6kBE,YAuBE;;EAEE,gBAAA;;AAtmBN,KA6kBE,YA4BE;EACE,cAAA;EACA,4BAAA;EACA,WAAA;EACA,iBAAA;;AA7mBN,KA6kBE,YAmCE,mBAAmB;EACjB,mBAAA;;AAjnBN,KA6kBE,YAuCE,mBAAmB,GAAE;EACnB,gBAAA;;AAOF,KADF,kBACG,4BAA4B;EA1nB7B,qBAAA;EACA,kBAAA;EACA,SAAS,KAAT;EACA,mBAAA;EACA,kBAAA;EACA,WAAA;EACA,eAAA;EACA,cAAA;EACA,kBAAA;EACA,gBAAA;EACA,gBAAA;EACA,mBAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,WAAA;;AAjBJ,KAioBE,2BACE;AAloBJ,KAioBE,2BACS;EACL,oBAAA;EACA,mBAAA;EACA,YAAA;EACA,yBAAA;EACA,wBAAA;EACA,gBAAA;;AAxoBN,KA4oBE,mBACE;AA7oBJ,KA4oBsB,wBAClB;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,cAAA;;AAjpBN,KA4oBE,mBAQE;AAppBJ,KA4oBsB,wBAQlB;EACE,cAAA;EACA,eAAA;;AAtpBN,KA4oBE,mBAaE;AAzpBJ,KA4oBsB,wBAalB;EACE,eAAA;;AAGE,KAjBN,mBAaE,GAGE,GACG;AAAD,KAjBc,wBAalB,GAGE,GACG;EACC,SAAS,EAAT;EACA,cAAA;EACA,WAAA;;AAhqBV,KA4oBE,mBAaE,GAGE,GAOE;AAnqBR,KA4oBsB,wBAalB,GAGE,GAOE;EACE,gBAAA;;AApqBV,KA4oBE,mBAaE,GAGE,GAWE;AAvqBR,KA4oBsB,wBAalB,GAGE,GAWE;AAvqBR,KA4oBE,mBAaE,GAGE,GAW4B;AAvqBlC,KA4oBsB,wBAalB,GAGE,GAW4B;EACxB,cAAA;EACA,WAAA;EACA,eAAA;EACA,iBAAA;;AAEA,KAjCR,mBAaE,GAGE,GAWE,yBAMG;AAAD,KAjCY,wBAalB,GAGE,GAWE,yBAMG;AAAD,KAjCR,mBAaE,GAGE,GAW4B,iCAMvB;AAAD,KAjCY,wBAalB,GAGE,GAW4B,iCAMvB;EACC,aAAA;EACA,gBAAA;;AA/qBZ,KA4oBE,mBAaE,GAGE,GAuBE;AAnrBR,KA4oBsB,wBAalB,GAGE,GAuBE;AAnrBR,KA4oBE,mBAaE,GAGE,GAuB2B;AAnrBjC,KA4oBsB,wBAalB,GAGE,GAuB2B;EACvB,kBAAA;;AAWR,QAA0B;EAqE5B,KApEI,KAAK;IACH,WAAA;IACA,oBAAA;;EAkEN,KA/DI,KAAK;EA+DT,KA9DI,KAAK;IACH,WAAA;IACA,eAAA;IACA,WAAA;IACA,gBAAA;;;AAIJ;EACE;EAAI;IACF,gBAAA;;EAEF;IACE,mBAAA;;;AAIJ,QAA2B,wBAAuB,qBAAsB;EA6C1E,KA5CI,aACE,gBAAe;EA2CrB,KA5CI,aACkC,WAAU;IACxC,UAAA;;;AAMN,QAA0B;EAoC5B,KAnCI,aACE,gBAAe;EAkCrB,KAnCI,aACkC,WAAU;IACxC,WAAA;IACA,WAAA;IACA,UAAA;;EA+BR,KAnCI,aAOE,WAAU;IACR,eAAA","file":"base-statistic.css"}
admin/assets/css/base-statistic.less CHANGED
@@ -22,102 +22,236 @@
22
  @orangeButtonColor: #a57b3c;
23
 
24
  #WBCR {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  .wio-clear {
26
  clear: both;
27
  }
28
 
29
- // Right sidebar
 
 
 
 
 
30
 
31
- .wbcr-rio-servers {
32
- margin-top: 30px;
33
- background: none;
34
- padding: 0;
 
 
35
 
36
- h4 {
37
- font-size: 15px;
38
- font-weight: 700;
39
- }
40
 
41
- button {
42
- padding: 5px 10px;
43
- border: 0;
44
- font-size: 11px;
45
- text-transform: uppercase !important;
46
- font-weight: bold;
47
- border-radius: 4px;
48
- outline: none;
49
- background: @greyButtonBg;
50
- color: @greyButtonColor;
51
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
52
 
53
- &:active {
54
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
55
- }
56
 
57
- &.wbcr-rio-loading {
58
- width: 56px;
59
- font-size: 0;
60
- background: @greyButtonBg url("../img/quick-start-loader.gif") center no-repeat;
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- &.wbcr-rio-selected {
64
- background: #f3efe2;
65
- color: #d8d8d8;
66
- &:active {
67
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
69
  }
70
  }
 
71
 
72
- table {
73
- width: 100%;
74
- box-sizing: border-box;
75
- border-spacing: 3px;
76
- background: #fff;
77
- border: 2px dashed #cac9c9;
78
- border-left: 0;
79
- border-right: 0;
80
 
81
- th, td {
82
- padding: 16px 10px;
83
- text-align: center;
84
- }
85
- th {
86
- background: #f3f3f3;
87
- color: #777777;
88
- box-shadow: 0 1px 0 rgb(216, 216, 216);
89
- &:nth-child(2n+1) {
90
- background: #f9f9f9
91
- }
92
- }
93
- .wbcr-rio-server-check-proccess {
94
- display: inline-block;
95
- height: 10px;
96
- width: 30px;
97
- background: url("../img/quick-start-loader.gif") center no-repeat;
98
- }
99
- .wbcr-rio-servers-list-item-selected {
100
- background: #fffbed;
101
- color: #cec5a8;
102
- font-weight: 600;
103
- }
104
- .wbcr-rio-server-success {
105
- color: @positiveColor;
106
  }
107
- .wbcr-rio-server-error {
108
- color: @errorColor;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  }
110
- .wbcr-rio-server-warning {
111
- color: @waringColor;
 
 
 
 
 
 
 
 
 
 
112
  }
113
  }
114
 
115
- .wbcr-rio-warning-message {
116
- padding: 20px;
 
 
 
 
117
  background: #efefef;
118
- font-size: 15px;
119
- color: #b7b7b7;
120
- font-style: italic;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
  }
123
 
@@ -190,7 +324,6 @@
190
  animation: wiohello 1s 3 linear backwards;
191
  }
192
 
193
- /* Number display */
194
  .wio-number-you-optimized {
195
  margin-bottom: 1.35em;
196
  overflow: hidden;
@@ -223,7 +356,6 @@
223
  }
224
  }
225
 
226
- /* Number and bars */
227
  .wio-bars {
228
  padding-right: 15px;
229
  }
@@ -246,6 +378,7 @@
246
  .wio-progress {
247
  background: @negativeColor;
248
  }
 
249
  .wio-barnb {
250
  color: darken(@negativeColor, 20);
251
  }
@@ -264,6 +397,7 @@
264
  .wio-progress {
265
  background: @positiveColor;
266
  }
 
267
  .wio-barnb {
268
  color: darken(@positiveColor, 10);
269
  }
@@ -273,6 +407,7 @@
273
  .wio-progress {
274
  background: @positiveColor;
275
  }
 
276
  .wio-barnb {
277
  color: darken(@positiveColor, 10);
278
  }
@@ -287,6 +422,7 @@
287
  }
288
 
289
  /* Doughnut */
 
290
  .wio-chart {
291
  position: relative;
292
  top: 1px;
@@ -356,7 +492,7 @@
356
  }
357
 
358
  .wio-optimize-button {
359
- width: 180px;
360
  padding: 12px 30px;
361
  background: @greenButtonBg;
362
  color: @greenButtonColor;
@@ -371,9 +507,16 @@
371
  &:active {
372
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
373
  }
 
 
 
 
 
 
374
  &.wio-running {
375
  color: @orangeButtonColor;
376
  background: @orangeButtonBg url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
 
377
  }
378
  }
379
 
@@ -401,7 +544,9 @@
401
  position: inherit !important;
402
  }
403
 
404
- /* Widget */
 
 
405
  .wio-image-optimize-board {
406
  padding-bottom: 0 !important;
407
  }
@@ -414,6 +559,57 @@
414
  }
415
  }
416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  .wio-widget {
418
  padding: 0 !important;
419
 
@@ -421,36 +617,113 @@
421
  font-size: 44px;
422
  line-height: 1;
423
  }
 
424
  .wio-bars {
425
  width: 60%;
426
  margin-left: 155px;
427
  }
 
428
  .col-chart.col-chart {
429
  width: 100%;
430
  }
 
431
  .col-controls {
432
  width: 45%;
433
  padding-left: 5px;
434
  padding-top: 110px
435
  }
 
436
  .wio-doughnut-legend {
437
  /*padding-top:30px;*/
438
  text-align: left;
439
  }
 
440
  .wio-widget-bottom {
441
  display: table;
442
  padding-top: 20px !important;
443
  width: 100%;
444
  text-align: right;
445
  }
 
446
  .wio-widget-bottom li {
447
  display: table-cell;
448
  }
 
449
  .wio-widget-bottom li:first-child {
450
  text-align: left;
451
  }
452
  }
453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  @media (max-width: 830px) {
455
  .wio [class^="col-"] {
456
  float: none;
@@ -491,6 +764,7 @@
491
  float: none;
492
  padding: 0;
493
  }
 
494
  .col-chart.col-chart {
495
  margin-top: 3em;
496
  }
22
  @orangeButtonColor: #a57b3c;
23
 
24
  #WBCR {
25
+ .premium-label(@position: relative, @positionTop:-8px, @positionLeft:-10px, @positionRight:auto) {
26
+ display: inline-block;
27
+ position: @position;
28
+ content: 'PRO';
29
+ background: #ff5722;
30
+ border-radius: 4px;
31
+ color: #fff;
32
+ font-size: 10px;
33
+ line-height: 1;
34
+ font-style: normal;
35
+ padding: 4px 6px;
36
+ margin-left: 4px;
37
+ vertical-align: top;
38
+ top: @positionTop;
39
+ left: @positionLeft;
40
+ right: @positionRight;
41
+ z-index: 11;
42
+ }
43
+
44
  .wio-clear {
45
  clear: both;
46
  }
47
 
48
+ // Fix for Clearfy
49
+ // This code hides tabs custom folders and nextgen gallery
50
+ // -----------------------------------------------
51
+ #io_folders_statistic-wbcr_clearfy-tab, #io_nextgen_gallery_statistic-wbcr_clearfy-tab {
52
+ display: none !important;
53
+ }
54
 
55
+ // Styling tabs on statistics pages
56
+ // -----------------------------------------------
57
+ .wrio-statistic-nav {
58
+ margin: 0;
59
+ //box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
60
+ background: #efefef;
61
 
62
+ ul {
63
+ margin: 0;
 
 
64
 
65
+ li {
66
+ position: relative;
67
+ display: inline-block;
68
+ //width: 300px;
69
+ margin: 0 0 0 0;
70
+ background: #ffffff;
71
+ box-shadow: 0 -2px 0 #eaeaea;
 
 
 
 
72
 
73
+ &:hover {
74
+ background: #f7f7f7;
75
+ }
76
 
77
+ &.active {
78
+ //box-shadow: 0 -2px 0 #c9deb2;
79
+ background: #f7f7f7;
80
+ border-top: 1px solid #d4d4d4;
81
+ border-left: 1px solid #d4d4d4;
82
+ border-right: 1px solid #d4d4d4;
83
+ border-bottom: 1px solid #f7f7f7;
84
+ margin-bottom: -1px;
85
+
86
+ a {
87
+ color: #222;
88
+
89
+ .wrio-statistic-tab-percent {
90
+ border: 2px dashed #8bc34a;
91
+ color: #5e8237;
92
+ }
93
+ }
94
+
95
+ .dashicons, .dashicons-before:before {
96
+ color: #ff8b66;
97
+ }
98
+ }
99
 
100
+ .wrio-statistic-tab {
101
+ display: block;
102
+ padding: 10px 20px 10px 20px;
103
+ text-decoration: none;
104
+ color: #d4d4d4;
105
+ font-size: 22px;
106
+ line-height: 2;
107
+
108
+ &:active, &:focus {
109
+ background: 0;
110
+ box-shadow: none;
111
+ outline: none;
112
+ }
113
+
114
+ .dashicons, .dashicons-before:before {
115
+ display: inline-block;
116
+ width: 30px;
117
+ height: 30px;
118
+ font-size: 30px;
119
+ line-height: 1.5;
120
+ margin-right: 15px;
121
+ color: #d4d4d4;
122
+ }
123
+
124
+
125
+ .wrio-statistic-tab-percent {
126
+ display: inline-block;
127
+ width: 42px;
128
+ height: 42px;
129
+ border-radius: 100px;
130
+ border: 2px dashed #e4e4e4;
131
+ padding: 5px;
132
+ margin-left: 30px;
133
+ font-size: 14px;
134
+ font-weight: 600;
135
+ text-align: center;
136
+ color: #bdbdbd;
137
+ }
138
+ }
139
+
140
+ .wrio-statistic-tab-premium-label:after {
141
+ .premium-label(absolute, 10px, auto, 10px);
142
  }
143
  }
144
  }
145
+ }
146
 
147
+ // Table style. The table is used for optimization log
148
+ .wrio-table {
149
+ width: 100%;
150
+ table-layout: fixed;
151
+ box-sizing: border-box;
152
+ border-spacing: 3px;
153
+ background: #fff;
154
+ border-top: 2px dashed #cac9c9;
155
 
156
+ th, td {
157
+ padding: 16px 10px;
158
+ text-align: center;
159
+ }
160
+
161
+ th {
162
+ background: #f3f3f3;
163
+ color: #777777;
164
+ box-shadow: 0 1px 0 rgb(216, 216, 216);
165
+
166
+ &:nth-child(2n+1) {
167
+ background: #f9f9f9
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
+ }
170
+
171
+ tr.wrio-error {
172
+ background-color: #ffe9e9 !important;
173
+ }
174
+
175
+ .wrio-table-spinner {
176
+ background: url("../img/quick-start-loader.gif") center center no-repeat;
177
+ }
178
+
179
+ .wrio-table-highlighter {
180
+ display: inline-block;
181
+ padding: 3px 7px;
182
+ background: @greyButtonBg;
183
+ }
184
+
185
+ .wbcr-rio-server-success {
186
+ color: @positiveColor;
187
+ }
188
+
189
+ .wbcr-rio-server-error {
190
+ color: @errorColor;
191
+ }
192
+
193
+ .wbcr-rio-server-warning {
194
+ color: @waringColor;
195
+ }
196
+
197
+ &.wbcr-rio-folders-table {
198
+ td:nth-child(3) {
199
+ text-align: left;
200
  }
201
+ }
202
+ }
203
+
204
+ .wrio-servers {
205
+ padding: 40px 20px;
206
+
207
+ label {
208
+ span {
209
+ display: block;
210
+ font-weight: normal;
211
+ font-size: 12px;
212
+ color: #b7b2b2;
213
  }
214
  }
215
 
216
+ #wrio-change-optimization-server {
217
+ position: relative;
218
+ max-width: 400px;
219
+ margin-right: 15px;
220
+ margin-bottom: 0;
221
+ border: 1px solid #d2d0d0;
222
  background: #efefef;
223
+ }
224
+
225
+ .wrio-servers-info {
226
+ margin: 0 0 0;
227
+ padding: 20px;
228
+ background: #fff;
229
+ }
230
+
231
+ .wrio-server-status-wrap {
232
+ margin-top: 8px;
233
+
234
+ .wrio-server-status {
235
+ background: transparent;
236
+ color: #fff;
237
+ padding: 3px 5px;
238
+ border-radius: 4px;
239
+
240
+ &.wrio-down {
241
+ background: #ff5722;
242
+ }
243
+
244
+ &.wrio-stable {
245
+ background: #8bc34a;
246
+ }
247
+
248
+ &.wrio-server-check-proccess {
249
+ display: inline-block;
250
+ height: 10px;
251
+ width: 30px;
252
+ background: url("../img/quick-start-loader.gif") center no-repeat;
253
+ }
254
+ }
255
  }
256
  }
257
 
324
  animation: wiohello 1s 3 linear backwards;
325
  }
326
 
 
327
  .wio-number-you-optimized {
328
  margin-bottom: 1.35em;
329
  overflow: hidden;
356
  }
357
  }
358
 
 
359
  .wio-bars {
360
  padding-right: 15px;
361
  }
378
  .wio-progress {
379
  background: @negativeColor;
380
  }
381
+
382
  .wio-barnb {
383
  color: darken(@negativeColor, 20);
384
  }
397
  .wio-progress {
398
  background: @positiveColor;
399
  }
400
+
401
  .wio-barnb {
402
  color: darken(@positiveColor, 10);
403
  }
407
  .wio-progress {
408
  background: @positiveColor;
409
  }
410
+
411
  .wio-barnb {
412
  color: darken(@positiveColor, 10);
413
  }
422
  }
423
 
424
  /* Doughnut */
425
+
426
  .wio-chart {
427
  position: relative;
428
  top: 1px;
492
  }
493
 
494
  .wio-optimize-button {
495
+ min-width: 180px;
496
  padding: 12px 30px;
497
  background: @greenButtonBg;
498
  color: @greenButtonColor;
507
  &:active {
508
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
509
  }
510
+
511
+ &:disabled {
512
+ background: lighten(@greenButtonBg, 10%);
513
+ color: lighten(@greenButtonColor, 30%);
514
+ }
515
+
516
  &.wio-running {
517
  color: @orangeButtonColor;
518
  background: @orangeButtonBg url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
519
+ padding-left: 50px;
520
  }
521
  }
522
 
544
  position: inherit !important;
545
  }
546
 
547
+
548
+ // WIDGETS SPACE
549
+ // -----------------------------------------------
550
  .wio-image-optimize-board {
551
  padding-bottom: 0 !important;
552
  }
559
  }
560
  }
561
 
562
+ .wrio-optimization-progress {
563
+ //margin-top: 30px;
564
+ background: none;
565
+ padding: 0;
566
+
567
+ h4 {
568
+ font-size: 15px;
569
+ font-weight: 700;
570
+ }
571
+
572
+ /*button {
573
+ padding: 5px 10px;
574
+ border: 0;
575
+ font-size: 11px;
576
+ text-transform: uppercase !important;
577
+ font-weight: bold;
578
+ border-radius: 4px;
579
+ outline: none;
580
+ background: @greyButtonBg;
581
+ color: @greyButtonColor;
582
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
583
+
584
+ &:active {
585
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.5);
586
+ }
587
+
588
+ &.wbcr-rio-loading {
589
+ width: 56px;
590
+ font-size: 0;
591
+ background: @greyButtonBg url("../img/quick-start-loader.gif") center no-repeat;
592
+ }
593
+
594
+ &.wbcr-rio-selected {
595
+ background: #f3efe2;
596
+ color: #d8d8d8;
597
+
598
+ &:active {
599
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
600
+ }
601
+ }
602
+ }*/
603
+
604
+ .wbcr-rio-warning-message {
605
+ padding: 20px;
606
+ background: #efefef;
607
+ font-size: 15px;
608
+ color: #b7b7b7;
609
+ font-style: italic;
610
+ }
611
+ }
612
+
613
  .wio-widget {
614
  padding: 0 !important;
615
 
617
  font-size: 44px;
618
  line-height: 1;
619
  }
620
+
621
  .wio-bars {
622
  width: 60%;
623
  margin-left: 155px;
624
  }
625
+
626
  .col-chart.col-chart {
627
  width: 100%;
628
  }
629
+
630
  .col-controls {
631
  width: 45%;
632
  padding-left: 5px;
633
  padding-top: 110px
634
  }
635
+
636
  .wio-doughnut-legend {
637
  /*padding-top:30px;*/
638
  text-align: left;
639
  }
640
+
641
  .wio-widget-bottom {
642
  display: table;
643
  padding-top: 20px !important;
644
  width: 100%;
645
  text-align: right;
646
  }
647
+
648
  .wio-widget-bottom li {
649
  display: table-cell;
650
  }
651
+
652
  .wio-widget-bottom li:first-child {
653
  text-align: left;
654
  }
655
  }
656
 
657
+ // FORMS SPACE
658
+ // -----------------------------------------------
659
+ .factory-checkbox {
660
+ &.wrio-checkbox-premium-label:after {
661
+ .premium-label();
662
+ }
663
+ }
664
+
665
+ .factory-checkbox-disabled {
666
+ input, button {
667
+ pointer-events: none;
668
+ cursor: not-allowed;
669
+ opacity: .65;
670
+ filter: alpha(opacity=65);
671
+ -webkit-box-shadow: none;
672
+ box-shadow: none;
673
+ }
674
+ }
675
+
676
+ #wrio-webp-options, #wrio-error-log-options {
677
+ h3 {
678
+ font-size: 14px;
679
+ margin: 0 0 10px 0;
680
+ font-weight: 600;
681
+ color: #565656;
682
+ }
683
+
684
+ .wrio-webp-options-info {
685
+ color: #8a8787;
686
+ font-size: 12px;
687
+ }
688
+
689
+ ul {
690
+ padding-left: 0;
691
+
692
+ li {
693
+ &:after {
694
+ content: '';
695
+ display: block;
696
+ clear: both;;
697
+ }
698
+
699
+ label {
700
+ font-weight: 600;
701
+ }
702
+
703
+ .wrio-webp-options-radio, .wrio-error-log-options-checkbox {
704
+ display: block;
705
+ float: left;
706
+ margin-top: 2px;
707
+ margin-right: 8px;
708
+
709
+ &:focus {
710
+ outline: none;
711
+ box-shadow: none;
712
+ }
713
+ }
714
+
715
+ .wrio-webp-options-info, .wrio-error-log-options-info {
716
+ padding-left: 25px;
717
+ }
718
+ }
719
+ }
720
+
721
+
722
+ }
723
+
724
+ // MEDIA SPACE
725
+ // -----------------------------------------------
726
+
727
  @media (max-width: 830px) {
728
  .wio [class^="col-"] {
729
  float: none;
764
  float: none;
765
  padding: 0;
766
  }
767
+
768
  .col-chart.col-chart {
769
  margin-top: 3em;
770
  }
admin/assets/css/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/assets/css/statistic.css CHANGED
@@ -1,283 +1,318 @@
 
1
  /**
2
  * == Columns
3
  */
4
  .wio-columns {
5
- overflow: hidden;
6
- padding: 15px 0;
7
- counter-reset: cols;
8
  }
 
9
  .wio-columns [class^="col-"] {
10
- float: left;
11
- -webkit-box-sizing: border-box;
12
- -moz-box-sizing: border-box;
13
- box-sizing: border-box;
14
  }
 
15
  .wio-columns .col-1-3 {
16
- width: 33.333%;
17
- padding-left: 28px;
18
  }
 
19
  .wio .col-2-3 {
20
- width: 66.666%;
21
- padding-left: 28px
22
  }
 
23
  .wio .col-1-2 {
24
- width: 50%;
25
- padding: 0 20px;
26
  }
27
 
28
  @media (max-width: 830px) {
29
- .wio [class^="col-"] {
30
- float: none;
31
- margin-bottom: 1.5em;
32
- }
33
- .wio .col-1-3,
34
- .wio .col-1-2 {
35
- width: auto;
36
- padding: 0 28px;
37
- clear: both;
38
- padding-top: 1em;
39
- }
 
40
  }
41
 
42
  /* Col, behavior depending on parent */
43
  .wio-col {
44
- float: left;
45
- width: 50%;
46
- box-sizing: border-box;
47
- -webkit-flex-basis: 50%;
48
- -ms-flex-preferred-size: 50%;
49
- flex-basis: 50%;
50
  }
 
51
  .wio-col {
52
- padding-right: 20px;
53
  }
 
54
  .wio-col + .wio-col {
55
- padding-right: 0;
56
- padding-left: 50px;
57
  }
58
 
59
  .wio-col:target {
60
- animation: wiohello 1s 3 linear backwards;
61
  }
62
 
63
  @keyframes wiohello {
64
- 0%, 100% {
65
- background: #FFF;
66
- }
67
- 50% {
68
- background: #F4F7F9;
69
- }
70
  }
71
 
72
  .wio-columns .col-statistics.col-statistics {
73
- width: 60%;
74
  }
 
75
  @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
76
- .wio-columns .col-statistics.col-statistics,
77
- .wio-columns .col-chart.col-chart {
78
- width: 50%;
79
- }
80
  }
 
81
  @media (max-width: 808px) {
82
- .wio-columns .col-statistics.col-statistics,
83
- .wio-columns .col-chart.col-chart {
84
- width: auto;
85
- float: none;
86
- padding: 0;
87
- }
88
- .wio-columns .col-chart.col-chart {
89
- margin-top: 3em;
90
- }
 
91
  }
92
 
93
  /* Number display */
94
  .wio-number-you-optimized {
95
- margin-bottom: 1.35em;
96
- overflow: hidden;
97
  }
 
98
  .wio-number-you-optimized .number {
99
- display: table-cell;
100
- padding-right: 15px;
101
- font-size: 48px;
102
- font-weight: bold;
103
- line-height: 1;
104
- vertical-align: middle;
105
- white-space: nowrap;
106
- color: #000;
107
  }
 
108
  .wio-number-you-optimized [id="wio-total-optimized-attachments-pct"] {
109
- color: #40B1D0;
110
  }
 
111
  .wio-number-you-optimized .text {
112
- display: table-cell;
113
- vertical-align: middle;
114
- overflow: hidden;
115
- font-size: 12px;
116
- color: #626E7B;
117
  }
 
118
  .wio-number-you-optimized > p {
119
- display: table;
120
  }
121
 
122
  /* Number and bars */
123
  .wio-bars {
124
- padding-right: 15px;
125
  }
 
126
  .wio-bars p {
127
- font-size: 12px;
128
- margin-bottom: 5px;
129
  }
 
130
  .wio-bars + .wio-number-you-optimized {
131
- border-bottom: 0;
132
- padding-top: 0.85em;
133
  }
 
134
  .wio-bars + .wio-number-you-optimized p {
135
- color: #46b1ce;
136
  }
137
 
138
  .wio-bar-negative .wio-progress {
139
- background: #D2D3D6;
140
  }
 
141
  .wio-bar-negative .wio-barnb {
142
- color: #7A8996;
143
  }
 
144
  .wio-bar-neutral .wio-progress {
145
- background: #F5A623;
146
  }
 
147
  .wio-space-left .wio-bar-negative .wio-progress {
148
- background: #C51162;
149
  }
150
 
151
  .wio-progress {
152
- height: 8px;
153
  }
 
154
  .wio-progress {
155
- transition: width .3s;
156
  }
 
157
  .wio-bar-positive .wio-progress {
158
- background: #8CC152;
159
  }
 
160
  .wio-bar-positive .wio-barnb {
161
- color: #8CC152;
162
  }
 
163
  .wio-bar-primary .wio-progress {
164
- background: #8bc34a;
165
  }
 
166
  .wio-bar-primary .wio-barnb {
167
- color: #8bc34a;
168
  }
169
 
170
  .right-outside-number .wio-barnb {
171
- display: block;
172
- margin-right: -5.25em;
173
- text-align: right;
174
- font-weight: bold;
175
- line-height: .8;
176
  }
177
 
178
 
179
  /* Doughnut */
180
  .wio-chart {
181
- position: relative;
182
- top: 1px;
183
- display: inline-block;
184
- vertical-align: middle;
185
  }
 
186
  .wio-chart-container {
187
- position: relative;
188
- display: inline-block;
189
- margin-right: 5px;
190
  }
 
191
  .wio-chart-container canvas {
192
- display: block;
193
  }
194
 
195
  .wio-overview-chart-container {
196
- float: left;
197
- margin-right: 20px;
198
  }
199
 
200
  @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
201
- .wio-overview-chart-container {
202
- float: none;
203
- margin-right: 0;
204
- }
205
  }
206
 
207
  .wio-chart-percent {
208
- position: absolute;
209
- left: 0; right: 0;
210
- top: 50%;
211
- margin-top: -.5em;
212
- line-height: 1;
213
- text-align: center;
214
- font-size: 55px;
215
- font-weight: bold;
216
- color: #afafaf;
 
217
  }
 
218
  .wio-chart-percent span {
219
- font-size: 20px;
220
- vertical-align: super;
221
  }
222
 
223
  #wio-overview-chart-legend {
224
- overflow: hidden;
225
  }
226
 
227
  .imagify-doughnut-legend {
228
- margin-top: 38px;
229
- list-style: none;
230
  }
231
 
232
  .wio-doughnut-legend li {
233
- display: inline-block;
234
- padding-left: 30px;
235
- position: relative;
236
- margin-bottom: 15px;
237
- border-radius: 5px;
238
- padding: 3px 8px 2px 31px;
239
- font-size: 13px;
240
- cursor: default;
241
- -webkit-transition: background-color 200ms ease-in-out;
242
- -moz-transition: background-color 200ms ease-in-out;
243
- -o-transition: background-color 200ms ease-in-out;
244
- transition: background-color 200ms ease-in-out;
245
  }
246
 
247
  .wio-doughnut-legend li span {
248
- display: block;
249
- position: absolute;
250
- left: 0; top: 0;
251
- width: 25px;
252
- height: 25px;
253
- border-radius: 50%;
 
254
  }
 
255
  @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
256
- .wio-overview-chart-container {
257
- float: none;
258
- margin-right: 0;
259
- }
260
- .wio-doughnut-legend {
261
- margin-top: 18px;
262
- }
263
- .wio-global-optim-phrase {
264
- padding-top: 0;
265
- width: auto;
266
- }
 
 
267
  }
 
268
  .wio-global-optim-phrase {
269
- width: 180px;
270
- padding-top: 20px;
271
- font-size: 14px;
272
- text-align: center;
273
  }
274
 
275
  .wio-clear {
276
- clear: both;
277
  }
278
 
279
  .wio-total-percent {
280
- color: #46b1ce;
281
  }
282
 
283
  .wio-columns .col-chart.col-chart {
@@ -319,18 +354,20 @@
319
 
320
  .wio-optimize-button.running {
321
  color: #a57b3c;
322
- background:#fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
323
  }
324
 
325
  .wio-widget-bottom {
326
  display: table;
327
- padding-top:20px !important;
328
  width: 100%;
329
  text-align: right;
330
  }
 
331
  .wio-widget-bottom li {
332
- display: table-cell;
333
  }
 
334
  .wio-widget-bottom li:first-child {
335
  text-align: left;
336
  }
@@ -347,26 +384,27 @@
347
  .wio-imagify-widget .col-chart.col-chart {
348
  width: 100%;
349
  }
 
350
  .wio-imagify-widget .col-controls {
351
  width: 45%;
352
- padding-left:5px;
353
- padding-top:110px
354
  }
355
 
356
  .wio-imagify-widget .wio-doughnut-legend {
357
- /*padding-top:30px;*/
358
- text-align:left;
359
  }
360
 
361
  #wio-start-msg-top, #wio-start-msg-right, #wio-start-msg-complete {
362
- display:none;
363
  }
364
 
365
  .wio-text-left {
366
- text-align:left;
367
  }
368
 
369
  span.wio-num {
370
- display:inline !important;
371
- position:inherit !important;
372
  }
1
+
2
  /**
3
  * == Columns
4
  */
5
  .wio-columns {
6
+ overflow: hidden;
7
+ padding: 15px 0;
8
+ counter-reset: cols;
9
  }
10
+
11
  .wio-columns [class^="col-"] {
12
+ float: left;
13
+ -webkit-box-sizing: border-box;
14
+ -moz-box-sizing: border-box;
15
+ box-sizing: border-box;
16
  }
17
+
18
  .wio-columns .col-1-3 {
19
+ width: 33.333%;
20
+ padding-left: 28px;
21
  }
22
+
23
  .wio .col-2-3 {
24
+ width: 66.666%;
25
+ padding-left: 28px
26
  }
27
+
28
  .wio .col-1-2 {
29
+ width: 50%;
30
+ padding: 0 20px;
31
  }
32
 
33
  @media (max-width: 830px) {
34
+ .wio [class^="col-"] {
35
+ float: none;
36
+ margin-bottom: 1.5em;
37
+ }
38
+
39
+ .wio .col-1-3,
40
+ .wio .col-1-2 {
41
+ width: auto;
42
+ padding: 0 28px;
43
+ clear: both;
44
+ padding-top: 1em;
45
+ }
46
  }
47
 
48
  /* Col, behavior depending on parent */
49
  .wio-col {
50
+ float: left;
51
+ width: 50%;
52
+ box-sizing: border-box;
53
+ -webkit-flex-basis: 50%;
54
+ -ms-flex-preferred-size: 50%;
55
+ flex-basis: 50%;
56
  }
57
+
58
  .wio-col {
59
+ padding-right: 20px;
60
  }
61
+
62
  .wio-col + .wio-col {
63
+ padding-right: 0;
64
+ padding-left: 50px;
65
  }
66
 
67
  .wio-col:target {
68
+ animation: wiohello 1s 3 linear backwards;
69
  }
70
 
71
  @keyframes wiohello {
72
+ 0%, 100% {
73
+ background: #FFF;
74
+ }
75
+ 50% {
76
+ background: #F4F7F9;
77
+ }
78
  }
79
 
80
  .wio-columns .col-statistics.col-statistics {
81
+ width: 60%;
82
  }
83
+
84
  @media (max-width: 1520px) and (min-width: 1381px), (max-width: 1086px) {
85
+ .wio-columns .col-statistics.col-statistics,
86
+ .wio-columns .col-chart.col-chart {
87
+ width: 50%;
88
+ }
89
  }
90
+
91
  @media (max-width: 808px) {
92
+ .wio-columns .col-statistics.col-statistics,
93
+ .wio-columns .col-chart.col-chart {
94
+ width: auto;
95
+ float: none;
96
+ padding: 0;
97
+ }
98
+
99
+ .wio-columns .col-chart.col-chart {
100
+ margin-top: 3em;
101
+ }
102
  }
103
 
104
  /* Number display */
105
  .wio-number-you-optimized {
106
+ margin-bottom: 1.35em;
107
+ overflow: hidden;
108
  }
109
+
110
  .wio-number-you-optimized .number {
111
+ display: table-cell;
112
+ padding-right: 15px;
113
+ font-size: 48px;
114
+ font-weight: bold;
115
+ line-height: 1;
116
+ vertical-align: middle;
117
+ white-space: nowrap;
118
+ color: #000;
119
  }
120
+
121
  .wio-number-you-optimized [id="wio-total-optimized-attachments-pct"] {
122
+ color: #40B1D0;
123
  }
124
+
125
  .wio-number-you-optimized .text {
126
+ display: table-cell;
127
+ vertical-align: middle;
128
+ overflow: hidden;
129
+ font-size: 12px;
130
+ color: #626E7B;
131
  }
132
+
133
  .wio-number-you-optimized > p {
134
+ display: table;
135
  }
136
 
137
  /* Number and bars */
138
  .wio-bars {
139
+ padding-right: 15px;
140
  }
141
+
142
  .wio-bars p {
143
+ font-size: 12px;
144
+ margin-bottom: 5px;
145
  }
146
+
147
  .wio-bars + .wio-number-you-optimized {
148
+ border-bottom: 0;
149
+ padding-top: 0.85em;
150
  }
151
+
152
  .wio-bars + .wio-number-you-optimized p {
153
+ color: #46b1ce;
154
  }
155
 
156
  .wio-bar-negative .wio-progress {
157
+ background: #D2D3D6;
158
  }
159
+
160
  .wio-bar-negative .wio-barnb {
161
+ color: #7A8996;
162
  }
163
+
164
  .wio-bar-neutral .wio-progress {
165
+ background: #F5A623;
166
  }
167
+
168
  .wio-space-left .wio-bar-negative .wio-progress {
169
+ background: #C51162;
170
  }
171
 
172
  .wio-progress {
173
+ height: 8px;
174
  }
175
+
176
  .wio-progress {
177
+ transition: width .3s;
178
  }
179
+
180
  .wio-bar-positive .wio-progress {
181
+ background: #8CC152;
182
  }
183
+
184
  .wio-bar-positive .wio-barnb {
185
+ color: #8CC152;
186
  }
187
+
188
  .wio-bar-primary .wio-progress {
189
+ background: #8bc34a;
190
  }
191
+
192
  .wio-bar-primary .wio-barnb {
193
+ color: #8bc34a;
194
  }
195
 
196
  .right-outside-number .wio-barnb {
197
+ display: block;
198
+ margin-right: -5.25em;
199
+ text-align: right;
200
+ font-weight: bold;
201
+ line-height: .8;
202
  }
203
 
204
 
205
  /* Doughnut */
206
  .wio-chart {
207
+ position: relative;
208
+ top: 1px;
209
+ display: inline-block;
210
+ vertical-align: middle;
211
  }
212
+
213
  .wio-chart-container {
214
+ position: relative;
215
+ display: inline-block;
216
+ margin-right: 5px;
217
  }
218
+
219
  .wio-chart-container canvas {
220
+ display: block;
221
  }
222
 
223
  .wio-overview-chart-container {
224
+ float: left;
225
+ margin-right: 20px;
226
  }
227
 
228
  @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
229
+ .wio-overview-chart-container {
230
+ float: none;
231
+ margin-right: 0;
232
+ }
233
  }
234
 
235
  .wio-chart-percent {
236
+ position: absolute;
237
+ left: 0;
238
+ right: 0;
239
+ top: 50%;
240
+ margin-top: -.5em;
241
+ line-height: 1;
242
+ text-align: center;
243
+ font-size: 55px;
244
+ font-weight: bold;
245
+ color: #afafaf;
246
  }
247
+
248
  .wio-chart-percent span {
249
+ font-size: 20px;
250
+ vertical-align: super;
251
  }
252
 
253
  #wio-overview-chart-legend {
254
+ overflow: hidden;
255
  }
256
 
257
  .imagify-doughnut-legend {
258
+ margin-top: 38px;
259
+ list-style: none;
260
  }
261
 
262
  .wio-doughnut-legend li {
263
+ display: inline-block;
264
+ padding-left: 30px;
265
+ position: relative;
266
+ margin-bottom: 15px;
267
+ border-radius: 5px;
268
+ padding: 3px 8px 2px 31px;
269
+ font-size: 13px;
270
+ cursor: default;
271
+ -webkit-transition: background-color 200ms ease-in-out;
272
+ -moz-transition: background-color 200ms ease-in-out;
273
+ -o-transition: background-color 200ms ease-in-out;
274
+ transition: background-color 200ms ease-in-out;
275
  }
276
 
277
  .wio-doughnut-legend li span {
278
+ display: block;
279
+ position: absolute;
280
+ left: 0;
281
+ top: 0;
282
+ width: 25px;
283
+ height: 25px;
284
+ border-radius: 50%;
285
  }
286
+
287
  @media (max-width: 1380px) and (min-width: 1246px), (max-width: 380px) {
288
+ .wio-overview-chart-container {
289
+ float: none;
290
+ margin-right: 0;
291
+ }
292
+
293
+ .wio-doughnut-legend {
294
+ margin-top: 18px;
295
+ }
296
+
297
+ .wio-global-optim-phrase {
298
+ padding-top: 0;
299
+ width: auto;
300
+ }
301
  }
302
+
303
  .wio-global-optim-phrase {
304
+ width: 180px;
305
+ padding-top: 20px;
306
+ font-size: 14px;
307
+ text-align: center;
308
  }
309
 
310
  .wio-clear {
311
+ clear: both;
312
  }
313
 
314
  .wio-total-percent {
315
+ color: #46b1ce;
316
  }
317
 
318
  .wio-columns .col-chart.col-chart {
354
 
355
  .wio-optimize-button.running {
356
  color: #a57b3c;
357
+ background: #fdd599 url("../img/Spinner-1s-33px.gif") 10px center no-repeat;
358
  }
359
 
360
  .wio-widget-bottom {
361
  display: table;
362
+ padding-top: 20px !important;
363
  width: 100%;
364
  text-align: right;
365
  }
366
+
367
  .wio-widget-bottom li {
368
+ display: table-cell;
369
  }
370
+
371
  .wio-widget-bottom li:first-child {
372
  text-align: left;
373
  }
384
  .wio-imagify-widget .col-chart.col-chart {
385
  width: 100%;
386
  }
387
+
388
  .wio-imagify-widget .col-controls {
389
  width: 45%;
390
+ padding-left: 5px;
391
+ padding-top: 110px
392
  }
393
 
394
  .wio-imagify-widget .wio-doughnut-legend {
395
+ /*padding-top:30px;*/
396
+ text-align: left;
397
  }
398
 
399
  #wio-start-msg-top, #wio-start-msg-right, #wio-start-msg-complete {
400
+ display: none;
401
  }
402
 
403
  .wio-text-left {
404
+ text-align: left;
405
  }
406
 
407
  span.wio-num {
408
+ display: inline !important;
409
+ position: inherit !important;
410
  }
admin/assets/css/sweetalert-custom.css ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Sub Layer */
2
+ body[class*="_rio"] .swal2-container.swal2-shown {
3
+ background: rgba(16, 17, 21, 0.9);
4
+ z-index: 100000;
5
+ }
6
+ .wrio-modal {
7
+ padding: 0 !important;
8
+ }
9
+ .wrio-modal .swal2-close {
10
+ color: rgba(0, 0, 0, 0.8);
11
+ }
12
+ .wrio-modal .swal2-modal {
13
+ border-radius: 2px;
14
+ }
15
+ .wrio-modal .swal2-icon {
16
+ margin-bottom: 25px;
17
+ }
18
+ .wrio-modal .swal2-title {
19
+ margin: 0;
20
+ padding: 28px 32px;
21
+ font-size: 24px;
22
+ text-align: left;
23
+ color: #fff !important;
24
+ background: #3e3e3e !important;
25
+ }
26
+ .wrio-modal .swal2-content {
27
+ font-size: 14px;
28
+ padding: 28px 32px;
29
+ background: #efefef;
30
+ }
31
+ .wrio-modal .swal2-buttonswrapper {
32
+ margin-top: 0;
33
+ padding: 22px;
34
+ background: #F4F7F9;
35
+ }
36
+ .wrio-modal .swal2-buttonswrapper a.button svg {
37
+ margin-right: 12px;
38
+ vertical-align: -2px;
39
+ }
40
+ .wrio-modal .swal2-buttonswrapper button.loading {
41
+ border-radius: 100% !important;
42
+ height: 40px !important;
43
+ padding: 0 !important;
44
+ box-shadow: none !important;
45
+ }
46
+ .wrio-modal .swal2-buttonswrapper button.swal2-styled {
47
+ height: auto;
48
+ padding: 12px 32px;
49
+ margin: 10px;
50
+ font-size: 14px;
51
+ letter-spacing: 1px;
52
+ text-transform: uppercase;
53
+ border-radius: 3px;
54
+ font-weight: bold;
55
+ outline: none;
56
+ }
57
+ .wrio-modal .swal2-buttonswrapper button.swal2-styled.swal2-confirm {
58
+ background-color: #fdd599 !important;
59
+ text-shadow: none !important;
60
+ box-shadow: 0 3px 0 #ceac7a !important;
61
+ color: #a57b3c !important;
62
+ }
63
+ .wrio-modal .swal2-buttonswrapper button.swal2-styled.swal2-cancel {
64
+ background-color: #d2d2d2 !important;
65
+ color: #656464 !important;
66
+ text-shadow: none !important;
67
+ box-shadow: 0 3px 0 #a9a9a9;
68
+ /*background-color: #c9deb2 !important;
69
+ color: #606956 !important;
70
+ text-shadow: none !important;
71
+ box-shadow: 0 3px 0 #a7b994;*/
72
+ }
73
+ .wrio-modal .swal2-buttonswrapper button.swal2-styled:focus,
74
+ .wrio-modal .swal2-buttonswrapper button.swal2-styled:hover {
75
+ outline: none;
76
+ text-shadow: none;
77
+ color: #FFF;
78
+ }
79
+ .wrio-modal-warning {
80
+ background: #FF5722 !important;
81
+ }
82
+ .wrio-modal-warning .swal2-title {
83
+ text-align: center;
84
+ color: #222 !important;
85
+ background: #efefef !important;
86
+ }
87
+ .wrio-modal-warning .swal2-content {
88
+ font-size: 16px;
89
+ padding: 10px 20px 32px;
90
+ background: #efefef;
91
+ }
92
+ .wrio-modal-warning .swal2-buttonswrapper {
93
+ background: #efefef;
94
+ }
95
+ .wrio-modal-error {
96
+ background: #dec2c0 !important;
97
+ }
98
+ .wrio-modal-error .swal2-title {
99
+ text-align: center;
100
+ color: #222 !important;
101
+ background: #efefef !important;
102
+ }
103
+ .wrio-modal-error .swal2-content {
104
+ font-size: 16px;
105
+ padding: 10px 20px 32px;
106
+ background: #efefef;
107
+ }
108
+ .wrio-modal-error .swal2-buttonswrapper {
109
+ background: #efefef;
110
+ }
111
+ .wrio-modal-optimization-way {
112
+ background: #1F2332 !important;
113
+ }
114
+ .wrio-modal-optimization-way .wrio-swal-subtitle {
115
+ padding: 0 0 28px;
116
+ margin-top: 0px;
117
+ font-weight: 500;
118
+ font-size: 18px;
119
+ text-align: left;
120
+ color: #8c8888;
121
+ background: #efefef;
122
+ }
123
+ .wrio-modal-optimization-way .wrio-list-infos {
124
+ margin: 0;
125
+ padding: 0;
126
+ }
127
+ .wrio-modal-optimization-way .wrio-list-infos li {
128
+ display: flex;
129
+ align-items: center;
130
+ padding: 15px 5px;
131
+ text-align: left;
132
+ font-size: 14px;
133
+ line-height: 1.5;
134
+ color: #8c8888;
135
+ }
136
+ .wrio-modal-optimization-way .wrio-list-infos li:first-child {
137
+ padding-top: 5px;
138
+ }
139
+ .wrio-modal-optimization-way .wrio-list-infos li:last-child {
140
+ padding-bottom: 5px;
141
+ }
142
+ .wrio-modal-optimization-way .wrio-list-infos li + li {
143
+ border-top: 1px solid #E9EFF2;
144
+ }
145
+ .wrio-modal-optimization-way .wrio-list-infos a:before {
146
+ content: '';
147
+ display: block;
148
+ }
149
+ .wrio-modal-optimization-way .wrio-info-icon {
150
+ flex-grow: 0;
151
+ flex-basis: 50px;
152
+ }
153
+ .wrio-modal-optimization-way .wrio-info-icon + span {
154
+ padding-left: 20px;
155
+ }
156
+ /*# sourceMappingURL=sweetalert-custom.css.map */
admin/assets/css/sweetalert-custom.css.map ADDED
@@ -0,0 +1 @@
 
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,sBAAA;EACA,8BAAA;;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,oCAAA;EACA,4BAAA;EACA,sCAAA;EACA,yBAAA;;AAGF,WAnCJ,sBAiBE,OAAM,aAkBH;EACC,oCAAA;EACA,yBAAA;EACA,4BAAA;EACA,2BAAA;;;;;;AAOF,WA9CJ,sBAiBE,OAAM,aA6BH;AAAQ,WA9Cb,sBAiBE,OAAM,aA6BM;EACR,aAAA;EACA,iBAAA;EACA,WAAA;;AAMR;EACE,8BAAA;;AADF,mBAGE;EACE,kBAAA;EACA,sBAAA;EACA,8BAAA;;AANJ,mBASE;EACE,eAAA;EACA,uBAAA;EACA,mBAAA;;AAZJ,mBAeE;EACE,mBAAA;;AAIJ;EACE,8BAAA;;AADF,iBAGE;EACE,kBAAA;EACA,sBAAA;EACA,8BAAA;;AANJ,iBASE;EACE,eAAA;EACA,uBAAA;EACA,mBAAA;;AAZJ,iBAeE;EACE,mBAAA;;AAIJ;EACE,8BAAA;;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"}
admin/assets/css/sweetalert-custom.less ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Sub Layer */
2
+ body[class*="_rio"] .swal2-container.swal2-shown {
3
+ background: rgba(16, 17, 21, 0.9);
4
+ z-index: 100000;
5
+ }
6
+
7
+ .wrio-modal {
8
+ padding: 0 !important;
9
+
10
+ .swal2-close {
11
+ color: rgba(0, 0, 0, .8);
12
+ }
13
+
14
+ .swal2-modal {
15
+ border-radius: 2px;
16
+ }
17
+
18
+ .swal2-icon {
19
+ margin-bottom: 25px;
20
+ }
21
+
22
+ .swal2-title {
23
+ margin: 0;
24
+ padding: 28px 32px;
25
+ font-size: 24px;
26
+ text-align: left;
27
+ color: #fff !important;
28
+ background: #3e3e3e !important;
29
+ }
30
+
31
+ .swal2-content {
32
+ font-size: 14px;
33
+ padding: 28px 32px;
34
+ background: #efefef;
35
+ }
36
+
37
+ .swal2-buttonswrapper {
38
+ margin-top: 0;
39
+ padding: 22px;
40
+ background: #F4F7F9;
41
+
42
+ a.button svg {
43
+ margin-right: 12px;
44
+ vertical-align: -2px;
45
+ }
46
+
47
+ button.loading {
48
+ border-radius: 100% !important;
49
+ height: 40px !important;
50
+ padding: 0 !important;
51
+ box-shadow: none !important;
52
+ }
53
+
54
+ button.swal2-styled {
55
+ height: auto;
56
+ padding: 12px 32px;
57
+ margin: 10px;
58
+ font-size: 14px;
59
+ letter-spacing: 1px;
60
+ text-transform: uppercase;
61
+ border-radius: 3px;
62
+ font-weight: bold;
63
+ outline: none;
64
+
65
+ &.swal2-confirm {
66
+ background-color: #fdd599 !important;
67
+ text-shadow: none !important;
68
+ box-shadow: 0 3px 0 #ceac7a !important;
69
+ color: #a57b3c !important;
70
+ }
71
+
72
+ &.swal2-cancel {
73
+ background-color: #d2d2d2 !important;
74
+ color: #656464 !important;
75
+ text-shadow: none !important;
76
+ box-shadow: 0 3px 0 #a9a9a9;
77
+ /*background-color: #c9deb2 !important;
78
+ color: #606956 !important;
79
+ text-shadow: none !important;
80
+ box-shadow: 0 3px 0 #a7b994;*/
81
+ }
82
+
83
+ &:focus, &:hover {
84
+ outline: none;
85
+ text-shadow: none;
86
+ color: #FFF;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ .wrio-modal-warning {
93
+ background: #FF5722 !important;
94
+
95
+ .swal2-title {
96
+ text-align: center;
97
+ color: #222 !important;
98
+ background: #efefef !important;
99
+ }
100
+
101
+ .swal2-content {
102
+ font-size: 16px;
103
+ padding: 10px 20px 32px;
104
+ background: #efefef;
105
+ }
106
+
107
+ .swal2-buttonswrapper {
108
+ background: #efefef;
109
+ }
110
+ }
111
+
112
+ .wrio-modal-error {
113
+ background: #dec2c0 !important;
114
+
115
+ .swal2-title {
116
+ text-align: center;
117
+ color: #222 !important;
118
+ background: #efefef !important;
119
+ }
120
+
121
+ .swal2-content {
122
+ font-size: 16px;
123
+ padding: 10px 20px 32px;
124
+ background: #efefef;
125
+ }
126
+
127
+ .swal2-buttonswrapper {
128
+ background: #efefef;
129
+ }
130
+ }
131
+
132
+ .wrio-modal-optimization-way {
133
+ background: #1F2332 !important;
134
+
135
+ .wrio-swal-subtitle {
136
+ padding: 0 0 28px;
137
+ margin-top: 0px;
138
+ font-weight: 500;
139
+ font-size: 18px;
140
+ text-align: left;
141
+ color: #8c8888;
142
+ background: #efefef;
143
+ }
144
+
145
+ .wrio-list-infos {
146
+ margin: 0;
147
+ padding: 0;
148
+
149
+ li {
150
+ display: flex;
151
+ align-items: center;
152
+ padding: 15px 5px;
153
+ text-align: left;
154
+ font-size: 14px;
155
+ line-height: 1.5;
156
+ color: #8c8888;
157
+ }
158
+
159
+ li:first-child {
160
+ padding-top: 5px;
161
+ }
162
+
163
+ li:last-child {
164
+ padding-bottom: 5px;
165
+ }
166
+
167
+ li + li {
168
+ border-top: 1px solid #E9EFF2;
169
+ }
170
+
171
+ a:before {
172
+ content: '';
173
+ display: block;
174
+ }
175
+ }
176
+
177
+ .wrio-info-icon {
178
+ flex-grow: 0;
179
+ flex-basis: 50px;
180
+ }
181
+
182
+ .wrio-info-icon + span {
183
+ padding-left: 20px;
184
+ }
185
+ }
admin/assets/css/sweetalert2.css ADDED
@@ -0,0 +1,716 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body.swal2-shown {
2
+ overflow-y: hidden; }
3
+
4
+ body.swal2-iosfix {
5
+ position: fixed;
6
+ left: 0;
7
+ right: 0; }
8
+
9
+ .swal2-container {
10
+ display: -webkit-box;
11
+ display: -ms-flexbox;
12
+ display: flex;
13
+ -webkit-box-align: center;
14
+ -ms-flex-align: center;
15
+ align-items: center;
16
+ position: fixed;
17
+ top: 0;
18
+ left: 0;
19
+ bottom: 0;
20
+ right: 0;
21
+ padding: 10px;
22
+ background-color: transparent;
23
+ z-index: 1060; }
24
+ .swal2-container.swal2-fade {
25
+ -webkit-transition: background-color .1s;
26
+ transition: background-color .1s; }
27
+ .swal2-container.swal2-shown {
28
+ background-color: rgba(0, 0, 0, 0.4); }
29
+
30
+ .swal2-modal {
31
+ background-color: #fff;
32
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
33
+ border-radius: 5px;
34
+ -webkit-box-sizing: border-box;
35
+ box-sizing: border-box;
36
+ text-align: center;
37
+ margin: auto;
38
+ overflow-x: hidden;
39
+ overflow-y: auto;
40
+ display: none;
41
+ position: relative;
42
+ max-width: 100%; }
43
+ .swal2-modal:focus {
44
+ outline: none; }
45
+ .swal2-modal.swal2-loading {
46
+ overflow-y: hidden; }
47
+ .swal2-modal .swal2-title {
48
+ color: #595959;
49
+ font-size: 30px;
50
+ text-align: center;
51
+ font-weight: 600;
52
+ text-transform: none;
53
+ position: relative;
54
+ margin: 0 0 .4em;
55
+ padding: 0;
56
+ display: block;
57
+ word-wrap: break-word; }
58
+ .swal2-modal .swal2-buttonswrapper {
59
+ margin-top: 15px; }
60
+ .swal2-modal .swal2-buttonswrapper:not(.swal2-loading) .swal2-styled[disabled] {
61
+ opacity: .4;
62
+ cursor: no-drop; }
63
+ .swal2-modal .swal2-buttonswrapper.swal2-loading .swal2-styled.swal2-confirm {
64
+ -webkit-box-sizing: border-box;
65
+ box-sizing: border-box;
66
+ border: 4px solid transparent;
67
+ border-color: transparent;
68
+ width: 40px;
69
+ height: 40px;
70
+ padding: 0;
71
+ margin: 7.5px;
72
+ vertical-align: top;
73
+ background-color: transparent !important;
74
+ color: transparent;
75
+ cursor: default;
76
+ border-radius: 100%;
77
+ -webkit-animation: rotate-loading 1.5s linear 0s infinite normal;
78
+ animation: rotate-loading 1.5s linear 0s infinite normal;
79
+ -webkit-user-select: none;
80
+ -moz-user-select: none;
81
+ -ms-user-select: none;
82
+ user-select: none; }
83
+ .swal2-modal .swal2-buttonswrapper.swal2-loading .swal2-styled.swal2-cancel {
84
+ margin-left: 30px;
85
+ margin-right: 30px; }
86
+ .swal2-modal .swal2-buttonswrapper.swal2-loading :not(.swal2-styled).swal2-confirm::after {
87
+ display: inline-block;
88
+ content: '';
89
+ margin-left: 5px 0 15px;
90
+ vertical-align: -1px;
91
+ height: 15px;
92
+ width: 15px;
93
+ border: 3px solid #999999;
94
+ -webkit-box-shadow: 1px 1px 1px #fff;
95
+ box-shadow: 1px 1px 1px #fff;
96
+ border-right-color: transparent;
97
+ border-radius: 50%;
98
+ -webkit-animation: rotate-loading 1.5s linear 0s infinite normal;
99
+ animation: rotate-loading 1.5s linear 0s infinite normal; }
100
+ .swal2-modal .swal2-styled {
101
+ border: 0;
102
+ border-radius: 3px;
103
+ -webkit-box-shadow: none;
104
+ box-shadow: none;
105
+ color: #fff;
106
+ cursor: pointer;
107
+ font-size: 17px;
108
+ font-weight: 500;
109
+ margin: 15px 5px 0;
110
+ padding: 10px 32px; }
111
+ .swal2-modal .swal2-image {
112
+ margin: 20px auto;
113
+ max-width: 100%; }
114
+ .swal2-modal .swal2-close {
115
+ background: transparent;
116
+ border: 0;
117
+ margin: 0;
118
+ padding: 0;
119
+ width: 38px;
120
+ height: 40px;
121
+ font-size: 36px;
122
+ line-height: 40px;
123
+ font-family: serif;
124
+ position: absolute;
125
+ top: 5px;
126
+ right: 8px;
127
+ cursor: pointer;
128
+ color: #cccccc;
129
+ -webkit-transition: color .1s ease;
130
+ transition: color .1s ease; }
131
+ .swal2-modal .swal2-close:hover {
132
+ color: #d55; }
133
+ .swal2-modal > .swal2-input,
134
+ .swal2-modal > .swal2-file,
135
+ .swal2-modal > .swal2-textarea,
136
+ .swal2-modal > .swal2-select,
137
+ .swal2-modal > .swal2-radio,
138
+ .swal2-modal > .swal2-checkbox {
139
+ display: none; }
140
+ .swal2-modal .swal2-content {
141
+ font-size: 18px;
142
+ text-align: center;
143
+ font-weight: 300;
144
+ position: relative;
145
+ float: none;
146
+ margin: 0;
147
+ padding: 0;
148
+ line-height: normal;
149
+ color: #545454;
150
+ word-wrap: break-word; }
151
+ .swal2-modal .swal2-input,
152
+ .swal2-modal .swal2-file,
153
+ .swal2-modal .swal2-textarea,
154
+ .swal2-modal .swal2-select,
155
+ .swal2-modal .swal2-radio,
156
+ .swal2-modal .swal2-checkbox {
157
+ margin: 20px auto; }
158
+ .swal2-modal .swal2-input,
159
+ .swal2-modal .swal2-file,
160
+ .swal2-modal .swal2-textarea {
161
+ width: 100%;
162
+ -webkit-box-sizing: border-box;
163
+ box-sizing: border-box;
164
+ font-size: 18px;
165
+ border-radius: 3px;
166
+ border: 1px solid #d9d9d9;
167
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06);
168
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.06);
169
+ -webkit-transition: border-color box-shadow .3s;
170
+ transition: border-color box-shadow .3s; }
171
+ .swal2-modal .swal2-input.swal2-inputerror,
172
+ .swal2-modal .swal2-file.swal2-inputerror,
173
+ .swal2-modal .swal2-textarea.swal2-inputerror {
174
+ border-color: #f27474 !important;
175
+ -webkit-box-shadow: 0 0 2px #f27474 !important;
176
+ box-shadow: 0 0 2px #f27474 !important; }
177
+ .swal2-modal .swal2-input:focus,
178
+ .swal2-modal .swal2-file:focus,
179
+ .swal2-modal .swal2-textarea:focus {
180
+ outline: none;
181
+ border: 1px solid #b4dbed;
182
+ -webkit-box-shadow: 0 0 3px #c4e6f5;
183
+ box-shadow: 0 0 3px #c4e6f5; }
184
+ .swal2-modal .swal2-input:focus::-webkit-input-placeholder,
185
+ .swal2-modal .swal2-file:focus::-webkit-input-placeholder,
186
+ .swal2-modal .swal2-textarea:focus::-webkit-input-placeholder {
187
+ -webkit-transition: opacity .3s .03s ease;
188
+ transition: opacity .3s .03s ease;
189
+ opacity: .8; }
190
+ .swal2-modal .swal2-input:focus:-ms-input-placeholder,
191
+ .swal2-modal .swal2-file:focus:-ms-input-placeholder,
192
+ .swal2-modal .swal2-textarea:focus:-ms-input-placeholder {
193
+ -webkit-transition: opacity .3s .03s ease;
194
+ transition: opacity .3s .03s ease;
195
+ opacity: .8; }
196
+ .swal2-modal .swal2-input:focus::placeholder,
197
+ .swal2-modal .swal2-file:focus::placeholder,
198
+ .swal2-modal .swal2-textarea:focus::placeholder {
199
+ -webkit-transition: opacity .3s .03s ease;
200
+ transition: opacity .3s .03s ease;
201
+ opacity: .8; }
202
+ .swal2-modal .swal2-input::-webkit-input-placeholder,
203
+ .swal2-modal .swal2-file::-webkit-input-placeholder,
204
+ .swal2-modal .swal2-textarea::-webkit-input-placeholder {
205
+ color: #e6e6e6; }
206
+ .swal2-modal .swal2-input:-ms-input-placeholder,
207
+ .swal2-modal .swal2-file:-ms-input-placeholder,
208
+ .swal2-modal .swal2-textarea:-ms-input-placeholder {
209
+ color: #e6e6e6; }
210
+ .swal2-modal .swal2-input::placeholder,
211
+ .swal2-modal .swal2-file::placeholder,
212
+ .swal2-modal .swal2-textarea::placeholder {
213
+ color: #e6e6e6; }
214
+ .swal2-modal .swal2-range input {
215
+ float: left;
216
+ width: 80%; }
217
+ .swal2-modal .swal2-range output {
218
+ float: right;
219
+ width: 20%;
220
+ font-size: 20px;
221
+ font-weight: 600;
222
+ text-align: center; }
223
+ .swal2-modal .swal2-range input,
224
+ .swal2-modal .swal2-range output {
225
+ height: 43px;
226
+ line-height: 43px;
227
+ vertical-align: middle;
228
+ margin: 20px auto;
229
+ padding: 0; }
230
+ .swal2-modal .swal2-input {
231
+ height: 43px;
232
+ padding: 0 12px; }
233
+ .swal2-modal .swal2-input[type='number'] {
234
+ max-width: 150px; }
235
+ .swal2-modal .swal2-file {
236
+ font-size: 20px; }
237
+ .swal2-modal .swal2-textarea {
238
+ height: 108px;
239
+ padding: 12px; }
240
+ .swal2-modal .swal2-select {
241
+ color: #545454;
242
+ font-size: inherit;
243
+ padding: 5px 10px;
244
+ min-width: 40%;
245
+ max-width: 100%; }
246
+ .swal2-modal .swal2-radio {
247
+ border: 0; }
248
+ .swal2-modal .swal2-radio label:not(:first-child) {
249
+ margin-left: 20px; }
250
+ .swal2-modal .swal2-radio input,
251
+ .swal2-modal .swal2-radio span {
252
+ vertical-align: middle; }
253
+ .swal2-modal .swal2-radio input {
254
+ margin: 0 3px 0 0; }
255
+ .swal2-modal .swal2-checkbox {
256
+ color: #545454; }
257
+ .swal2-modal .swal2-checkbox input,
258
+ .swal2-modal .swal2-checkbox span {
259
+ vertical-align: middle; }
260
+ .swal2-modal .swal2-validationerror {
261
+ background-color: #f0f0f0;
262
+ margin: 0 -20px;
263
+ overflow: hidden;
264
+ padding: 10px;
265
+ color: gray;
266
+ font-size: 16px;
267
+ font-weight: 300;
268
+ display: none; }
269
+ .swal2-modal .swal2-validationerror::before {
270
+ content: '!';
271
+ display: inline-block;
272
+ width: 24px;
273
+ height: 24px;
274
+ border-radius: 50%;
275
+ background-color: #ea7d7d;
276
+ color: #fff;
277
+ line-height: 24px;
278
+ text-align: center;
279
+ margin-right: 10px; }
280
+
281
+ @supports (-ms-accelerator: true) {
282
+ .swal2-range input {
283
+ width: 100% !important; }
284
+ .swal2-range output {
285
+ display: none; } }
286
+
287
+ @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
288
+ .swal2-range input {
289
+ width: 100% !important; }
290
+ .swal2-range output {
291
+ display: none; } }
292
+
293
+ .swal2-icon {
294
+ width: 80px;
295
+ height: 80px;
296
+ border: 4px solid transparent;
297
+ border-radius: 50%;
298
+ margin: 20px auto 30px;
299
+ padding: 0;
300
+ position: relative;
301
+ -webkit-box-sizing: content-box;
302
+ box-sizing: content-box;
303
+ cursor: default;
304
+ -webkit-user-select: none;
305
+ -moz-user-select: none;
306
+ -ms-user-select: none;
307
+ user-select: none; }
308
+ .swal2-icon.swal2-error {
309
+ border-color: #f27474; }
310
+ .swal2-icon.swal2-error .swal2-x-mark {
311
+ position: relative;
312
+ display: block; }
313
+ .swal2-icon.swal2-error [class^='swal2-x-mark-line'] {
314
+ position: absolute;
315
+ height: 5px;
316
+ width: 47px;
317
+ background-color: #f27474;
318
+ display: block;
319
+ top: 37px;
320
+ border-radius: 2px; }
321
+ .swal2-icon.swal2-error [class^='swal2-x-mark-line'][class$='left'] {
322
+ -webkit-transform: rotate(45deg);
323
+ transform: rotate(45deg);
324
+ left: 17px; }
325
+ .swal2-icon.swal2-error [class^='swal2-x-mark-line'][class$='right'] {
326
+ -webkit-transform: rotate(-45deg);
327
+ transform: rotate(-45deg);
328
+ right: 16px; }
329
+ .swal2-icon.swal2-warning {
330
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
331
+ color: #f8bb86;
332
+ border-color: #facea8;
333
+ font-size: 60px;
334
+ line-height: 80px;
335
+ text-align: center; }
336
+ .swal2-icon.swal2-info {
337
+ font-family: 'Open Sans', sans-serif;
338
+ color: #3fc3ee;
339
+ border-color: #9de0f6;
340
+ font-size: 60px;
341
+ line-height: 80px;
342
+ text-align: center; }
343
+ .swal2-icon.swal2-question {
344
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
345
+ color: #87adbd;
346
+ border-color: #c9dae1;
347
+ font-size: 60px;
348
+ line-height: 80px;
349
+ text-align: center; }
350
+ .swal2-icon.swal2-success {
351
+ border-color: #a5dc86; }
352
+ .swal2-icon.swal2-success [class^='swal2-success-circular-line'] {
353
+ border-radius: 50%;
354
+ position: absolute;
355
+ width: 60px;
356
+ height: 120px;
357
+ -webkit-transform: rotate(45deg);
358
+ transform: rotate(45deg); }
359
+ .swal2-icon.swal2-success [class^='swal2-success-circular-line'][class$='left'] {
360
+ border-radius: 120px 0 0 120px;
361
+ top: -7px;
362
+ left: -33px;
363
+ -webkit-transform: rotate(-45deg);
364
+ transform: rotate(-45deg);
365
+ -webkit-transform-origin: 60px 60px;
366
+ transform-origin: 60px 60px; }
367
+ .swal2-icon.swal2-success [class^='swal2-success-circular-line'][class$='right'] {
368
+ border-radius: 0 120px 120px 0;
369
+ top: -11px;
370
+ left: 30px;
371
+ -webkit-transform: rotate(-45deg);
372
+ transform: rotate(-45deg);
373
+ -webkit-transform-origin: 0 60px;
374
+ transform-origin: 0 60px; }
375
+ .swal2-icon.swal2-success .swal2-success-ring {
376
+ width: 80px;
377
+ height: 80px;
378
+ border: 4px solid rgba(165, 220, 134, 0.2);
379
+ border-radius: 50%;
380
+ -webkit-box-sizing: content-box;
381
+ box-sizing: content-box;
382
+ position: absolute;
383
+ left: -4px;
384
+ top: -4px;
385
+ z-index: 2; }
386
+ .swal2-icon.swal2-success .swal2-success-fix {
387
+ width: 7px;
388
+ height: 90px;
389
+ position: absolute;
390
+ left: 28px;
391
+ top: 8px;
392
+ z-index: 1;
393
+ -webkit-transform: rotate(-45deg);
394
+ transform: rotate(-45deg); }
395
+ .swal2-icon.swal2-success [class^='swal2-success-line'] {
396
+ height: 5px;
397
+ background-color: #a5dc86;
398
+ display: block;
399
+ border-radius: 2px;
400
+ position: absolute;
401
+ z-index: 2; }
402
+ .swal2-icon.swal2-success [class^='swal2-success-line'][class$='tip'] {
403
+ width: 25px;
404
+ left: 14px;
405
+ top: 46px;
406
+ -webkit-transform: rotate(45deg);
407
+ transform: rotate(45deg); }
408
+ .swal2-icon.swal2-success [class^='swal2-success-line'][class$='long'] {
409
+ width: 47px;
410
+ right: 8px;
411
+ top: 38px;
412
+ -webkit-transform: rotate(-45deg);
413
+ transform: rotate(-45deg); }
414
+
415
+ .swal2-progresssteps {
416
+ font-weight: 600;
417
+ margin: 0 0 20px;
418
+ padding: 0; }
419
+ .swal2-progresssteps li {
420
+ display: inline-block;
421
+ position: relative; }
422
+ .swal2-progresssteps .swal2-progresscircle {
423
+ background: #3085d6;
424
+ border-radius: 2em;
425
+ color: #fff;
426
+ height: 2em;
427
+ line-height: 2em;
428
+ text-align: center;
429
+ width: 2em;
430
+ z-index: 20; }
431
+ .swal2-progresssteps .swal2-progresscircle:first-child {
432
+ margin-left: 0; }
433
+ .swal2-progresssteps .swal2-progresscircle:last-child {
434
+ margin-right: 0; }
435
+ .swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep {
436
+ background: #3085d6; }
437
+ .swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep ~ .swal2-progresscircle {
438
+ background: #add8e6; }
439
+ .swal2-progresssteps .swal2-progresscircle.swal2-activeprogressstep ~ .swal2-progressline {
440
+ background: #add8e6; }
441
+ .swal2-progresssteps .swal2-progressline {
442
+ background: #3085d6;
443
+ height: .4em;
444
+ margin: 0 -1px;
445
+ z-index: 10; }
446
+
447
+ [class^='swal2'] {
448
+ -webkit-tap-highlight-color: transparent; }
449
+
450
+ @-webkit-keyframes showSweetAlert {
451
+ 0% {
452
+ -webkit-transform: scale(0.7);
453
+ transform: scale(0.7); }
454
+ 45% {
455
+ -webkit-transform: scale(1.05);
456
+ transform: scale(1.05); }
457
+ 80% {
458
+ -webkit-transform: scale(0.95);
459
+ transform: scale(0.95); }
460
+ 100% {
461
+ -webkit-transform: scale(1);
462
+ transform: scale(1); } }
463
+
464
+ @keyframes showSweetAlert {
465
+ 0% {
466
+ -webkit-transform: scale(0.7);
467
+ transform: scale(0.7); }
468
+ 45% {
469
+ -webkit-transform: scale(1.05);
470
+ transform: scale(1.05); }
471
+ 80% {
472
+ -webkit-transform: scale(0.95);
473
+ transform: scale(0.95); }
474
+ 100% {
475
+ -webkit-transform: scale(1);
476
+ transform: scale(1); } }
477
+
478
+ @-webkit-keyframes hideSweetAlert {
479
+ 0% {
480
+ -webkit-transform: scale(1);
481
+ transform: scale(1);
482
+ opacity: 1; }
483
+ 100% {
484
+ -webkit-transform: scale(0.5);
485
+ transform: scale(0.5);
486
+ opacity: 0; } }
487
+
488
+ @keyframes hideSweetAlert {
489
+ 0% {
490
+ -webkit-transform: scale(1);
491
+ transform: scale(1);
492
+ opacity: 1; }
493
+ 100% {
494
+ -webkit-transform: scale(0.5);
495
+ transform: scale(0.5);
496
+ opacity: 0; } }
497
+
498
+ .swal2-show {
499
+ -webkit-animation: showSweetAlert 0.3s;
500
+ animation: showSweetAlert 0.3s; }
501
+ .swal2-show.swal2-noanimation {
502
+ -webkit-animation: none;
503
+ animation: none; }
504
+
505
+ .swal2-hide {
506
+ -webkit-animation: hideSweetAlert 0.15s forwards;
507
+ animation: hideSweetAlert 0.15s forwards; }
508
+ .swal2-hide.swal2-noanimation {
509
+ -webkit-animation: none;
510
+ animation: none; }
511
+
512
+ @-webkit-keyframes animate-success-tip {
513
+ 0% {
514
+ width: 0;
515
+ left: 1px;
516
+ top: 19px; }
517
+ 54% {
518
+ width: 0;
519
+ left: 1px;
520
+ top: 19px; }
521
+ 70% {
522
+ width: 50px;
523
+ left: -8px;
524
+ top: 37px; }
525
+ 84% {
526
+ width: 17px;
527
+ left: 21px;
528
+ top: 48px; }
529
+ 100% {
530
+ width: 25px;
531
+ left: 14px;
532
+ top: 45px; } }
533
+
534
+ @keyframes animate-success-tip {
535
+ 0% {
536
+ width: 0;
537
+ left: 1px;
538
+ top: 19px; }
539
+ 54% {
540
+ width: 0;
541
+ left: 1px;
542
+ top: 19px; }
543
+ 70% {
544
+ width: 50px;
545
+ left: -8px;
546
+ top: 37px; }
547
+ 84% {
548
+ width: 17px;
549
+ left: 21px;
550
+ top: 48px; }
551
+ 100% {
552
+ width: 25px;
553
+ left: 14px;
554
+ top: 45px; } }
555
+
556
+ @-webkit-keyframes animate-success-long {
557
+ 0% {
558
+ width: 0;
559
+ right: 46px;
560
+ top: 54px; }
561
+ 65% {
562
+ width: 0;
563
+ right: 46px;
564
+ top: 54px; }
565
+ 84% {
566
+ width: 55px;
567
+ right: 0;
568
+ top: 35px; }
569
+ 100% {
570
+ width: 47px;
571
+ right: 8px;
572
+ top: 38px; } }
573
+
574
+ @keyframes animate-success-long {
575
+ 0% {
576
+ width: 0;
577
+ right: 46px;
578
+ top: 54px; }
579
+ 65% {
580
+ width: 0;
581
+ right: 46px;
582
+ top: 54px; }
583
+ 84% {
584
+ width: 55px;
585
+ right: 0;
586
+ top: 35px; }
587
+ 100% {
588
+ width: 47px;
589
+ right: 8px;
590
+ top: 38px; } }
591
+
592
+ @-webkit-keyframes rotatePlaceholder {
593
+ 0% {
594
+ -webkit-transform: rotate(-45deg);
595
+ transform: rotate(-45deg); }
596
+ 5% {
597
+ -webkit-transform: rotate(-45deg);
598
+ transform: rotate(-45deg); }
599
+ 12% {
600
+ -webkit-transform: rotate(-405deg);
601
+ transform: rotate(-405deg); }
602
+ 100% {
603
+ -webkit-transform: rotate(-405deg);
604
+ transform: rotate(-405deg); } }
605
+
606
+ @keyframes rotatePlaceholder {
607
+ 0% {
608
+ -webkit-transform: rotate(-45deg);
609
+ transform: rotate(-45deg); }
610
+ 5% {
611
+ -webkit-transform: rotate(-45deg);
612
+ transform: rotate(-45deg); }
613
+ 12% {
614
+ -webkit-transform: rotate(-405deg);
615
+ transform: rotate(-405deg); }
616
+ 100% {
617
+ -webkit-transform: rotate(-405deg);
618
+ transform: rotate(-405deg); } }
619
+
620
+ .swal2-animate-success-line-tip {
621
+ -webkit-animation: animate-success-tip 0.75s;
622
+ animation: animate-success-tip 0.75s; }
623
+
624
+ .swal2-animate-success-line-long {
625
+ -webkit-animation: animate-success-long 0.75s;
626
+ animation: animate-success-long 0.75s; }
627
+
628
+ .swal2-success.swal2-animate-success-icon .swal2-success-circular-line-right {
629
+ -webkit-animation: rotatePlaceholder 4.25s ease-in;
630
+ animation: rotatePlaceholder 4.25s ease-in; }
631
+
632
+ @-webkit-keyframes animate-error-icon {
633
+ 0% {
634
+ -webkit-transform: rotateX(100deg);
635
+ transform: rotateX(100deg);
636
+ opacity: 0; }
637
+ 100% {
638
+ -webkit-transform: rotateX(0deg);
639
+ transform: rotateX(0deg);
640
+ opacity: 1; } }
641
+
642
+ @keyframes animate-error-icon {
643
+ 0% {
644
+ -webkit-transform: rotateX(100deg);
645
+ transform: rotateX(100deg);
646
+ opacity: 0; }
647
+ 100% {
648
+ -webkit-transform: rotateX(0deg);
649
+ transform: rotateX(0deg);
650
+ opacity: 1; } }
651
+
652
+ .swal2-animate-error-icon {
653
+ -webkit-animation: animate-error-icon 0.5s;
654
+ animation: animate-error-icon 0.5s; }
655
+
656
+ @-webkit-keyframes animate-x-mark {
657
+ 0% {
658
+ -webkit-transform: scale(0.4);
659
+ transform: scale(0.4);
660
+ margin-top: 26px;
661
+ opacity: 0; }
662
+ 50% {
663
+ -webkit-transform: scale(0.4);
664
+ transform: scale(0.4);
665
+ margin-top: 26px;
666
+ opacity: 0; }
667
+ 80% {
668
+ -webkit-transform: scale(1.15);
669
+ transform: scale(1.15);
670
+ margin-top: -6px; }
671
+ 100% {
672
+ -webkit-transform: scale(1);
673
+ transform: scale(1);
674
+ margin-top: 0;
675
+ opacity: 1; } }
676
+
677
+ @keyframes animate-x-mark {
678
+ 0% {
679
+ -webkit-transform: scale(0.4);
680
+ transform: scale(0.4);
681
+ margin-top: 26px;
682
+ opacity: 0; }
683
+ 50% {
684
+ -webkit-transform: scale(0.4);
685
+ transform: scale(0.4);
686
+ margin-top: 26px;
687
+ opacity: 0; }
688
+ 80% {
689
+ -webkit-transform: scale(1.15);
690
+ transform: scale(1.15);
691
+ margin-top: -6px; }
692
+ 100% {
693
+ -webkit-transform: scale(1);
694
+ transform: scale(1);
695
+ margin-top: 0;
696
+ opacity: 1; } }
697
+
698
+ .swal2-animate-x-mark {
699
+ -webkit-animation: animate-x-mark 0.5s;
700
+ animation: animate-x-mark 0.5s; }
701
+
702
+ @-webkit-keyframes rotate-loading {
703
+ 0% {
704
+ -webkit-transform: rotate(0deg);
705
+ transform: rotate(0deg); }
706
+ 100% {
707
+ -webkit-transform: rotate(360deg);
708
+ transform: rotate(360deg); } }
709
+
710
+ @keyframes rotate-loading {
711
+ 0% {
712
+ -webkit-transform: rotate(0deg);
713
+ transform: rotate(0deg); }
714
+ 100% {
715
+ -webkit-transform: rotate(360deg);
716
+ transform: rotate(360deg); } }
admin/assets/img/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/assets/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/assets/js/bulk-optimization.js ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ var bulkOptimization = {
3
+
4
+ inprogress: false,
5
+
6
+ serverDown: false,
7
+
8
+ i18n: {},
9
+
10
+ settings: {},
11
+
12
+ init: function() {
13
+ if( wrio_l18n_bulk_page === undefined || wrio_settings_bulk_page === undefined ) {
14
+ console.log('[Error]: Required global variables are not declared.');
15
+ return;
16
+ }
17
+
18
+ this.i18n = wrio_l18n_bulk_page;
19
+ this.settings = wrio_settings_bulk_page;
20
+ this.startOptButton = $('#wrio-start-optimization');
21
+
22
+ this.registerEvents();
23
+ this.checkServerStatus();
24
+ },
25
+
26
+ registerEvents: function() {
27
+ var self = this;
28
+
29
+ $('#wrio-change-optimization-server').on('change', function() {
30
+ $(this).prop('disabled', true);
31
+ self.checkServerStatus();
32
+ });
33
+
34
+ this.startOptButton.on('click', function() {
35
+ self.startOptButton = $(this);
36
+
37
+ if( $(this).hasClass('wio-running') ) {
38
+ self.stop();
39
+ return;
40
+ }
41
+
42
+ if( self.serverDown ) {
43
+ $.wrio_modal.showErrorModal(self.i18n.server_down_warning);
44
+ return;
45
+ }
46
+
47
+ if( "1" === self.settings.need_migration ) {
48
+ $.wrio_modal.showErrorModal(self.i18n.need_migrations);
49
+ return;
50
+ }
51
+
52
+ if( "0" === self.settings.images_backup ) {
53
+ $.wrio_modal.showWarningModal(self.i18n.process_without_backup, function() {
54
+ self.showModal();
55
+ });
56
+ return;
57
+ }
58
+
59
+ self.showModal();
60
+
61
+ return false;
62
+ });
63
+ },
64
+
65
+ checkServerStatus: function() {
66
+ var self = this,
67
+ serverStatus = $('.wrio-server-status'),
68
+ data = {
69
+ 'action': 'wbcr-rio-check-servers-status',
70
+ '_wpnonce': self.settings.nonce
71
+ };
72
+
73
+ self.serverDown = false;
74
+
75
+ data['server_name'] = $('#wrio-change-optimization-server').val();
76
+
77
+ serverStatus.addClass('wrio-server-check-proccess');
78
+ serverStatus.text('');
79
+ serverStatus.removeClass('wrio-down').removeClass('wrio-stable');
80
+
81
+ self.startOptButton.prop('disabled', true);
82
+
83
+ $.post(ajaxurl, data, function(response) {
84
+ serverStatus.removeClass('wrio-server-check-proccess');
85
+
86
+ if( !response || !response.data || !response.success ) {
87
+ if( !response || !response.data ) {
88
+ console.log('[Error]: Response error');
89
+ console.log(response);
90
+ return;
91
+ }
92
+ serverStatus.addClass('wrio-down');
93
+ console.log(self.i18n.server_status_down);
94
+ serverStatus.text(self.i18n.server_status_down);
95
+ self.serverDown = true;
96
+ } else {
97
+ serverStatus.addClass('wrio-stable');
98
+ serverStatus.text(self.i18n.server_status_stable);
99
+ }
100
+
101
+ $('#wrio-change-optimization-server').prop('disabled', false);
102
+ self.startOptButton.prop('disabled', false);
103
+
104
+ }).fail(function(xhr, status, error) {
105
+ console.log(xhr);
106
+ console.log(status);
107
+ console.log(error);
108
+
109
+ self.throwError(error);
110
+ });
111
+ },
112
+
113
+ showModal: function() {
114
+ var self = this;
115
+ var infosModal = $('#wrio-tmpl-bulk-optimization');
116
+
117
+ if( !infosModal.length ) {
118
+ console.log('[Error]: Html template for modal not found.');
119
+ return;
120
+ }
121
+
122
+ // Swal Information before loading the optimize process.
123
+ swal({
124
+ title: this.i18n.modal_optimization_title,
125
+ html: infosModal.html(),
126
+ type: '',
127
+ customClass: 'wrio-modal wrio-modal-optimization-way',
128
+ showCancelButton: true,
129
+ showCloseButton: true,
130
+ padding: 0,
131
+ width: 654,
132
+ confirmButtonText: this.i18n.modal_optimization_monual_button,
133
+ cancelButtonText: this.i18n.modal_optimization_cron_button,
134
+ reverseButtons: true,
135
+ }).then(function(result) {
136
+
137
+ self.process();
138
+
139
+ window.onbeforeunload = function() {
140
+ return self.i18n.leave_page_warning;
141
+ }
142
+
143
+ }, function(dismiss) {
144
+ if( dismiss === 'cancel' ) { // you might also handle 'close' or 'timer' if you used those
145
+ self.process('cron');
146
+ } else {
147
+ throw dismiss;
148
+ }
149
+ });
150
+
151
+ },
152
+
153
+ /**
154
+ * Start optimization
155
+ * @param {string} type
156
+ */
157
+ process: function(type) {
158
+
159
+ this.inprogress = true;
160
+
161
+ var sendData = {
162
+ 'action': 'wrio-bulk-optimization-process',
163
+ 'scope': this.settings.scope,
164
+ 'multisite': 0,
165
+ '_wpnonce': this.settings.nonce,
166
+ };
167
+
168
+ this.setButtonStyleRun(type);
169
+
170
+ if( 'cron' === type ) {
171
+ this.startOptButton.addClass('wrio-cron-mode');
172
+
173
+ sendData['action'] = 'wrio-cron-start';
174
+
175
+ $.post(ajaxurl, sendData, function(response) {
176
+ if( !response || !response.success ) {
177
+ console.log('[Error]: Failed ajax request (Start cron).');
178
+ console.log(sendData);
179
+ console.log(response);
180
+
181
+ if( response.data && response.data.error_message ) {
182
+ self.throwError(response.data.error_message);
183
+ }
184
+ }
185
+ }).fail(function(xhr, status, error) {
186
+ console.log(xhr);
187
+ console.log(status);
188
+ console.log(error);
189
+
190
+ self.throwError(error);
191
+ });
192
+
193
+ return;
194
+ }
195
+
196
+ this.showMessage(this.i18n.optimization_inprogress.replace("%s", parseInt($('#wio-unoptimized-num').text())));
197
+
198
+ // show message: Optimization remined
199
+ /*if( "1" === this.settings.is_network_admin ) {
200
+ sendData['multisite'] = 1;
201
+ }*/
202
+
203
+ sendData['reset_current_errors'] = 1;
204
+
205
+ this.sendRequest(sendData);
206
+ },
207
+
208
+ stop: function() {
209
+ var self = this;
210
+
211
+ this.inprogress = false;
212
+
213
+ window.onbeforeunload = null;
214
+ self.setButtonStyleStop();
215
+ self.destroyMessages();
216
+
217
+ if( this.startOptButton.hasClass('wrio-cron-mode') ) {
218
+ this.startOptButton.removeClass('wrio-cron-mode');
219
+
220
+ $.post(ajaxurl, {
221
+ 'action': 'wrio-cron-stop',
222
+ '_wpnonce': self.settings.nonce,
223
+ 'type': self.settings.scope
224
+ }, function(response) {
225
+ if( !response || !response.success ) {
226
+ console.log('[Error]: Failed ajax request (Stop cron).');
227
+ console.log(response);
228
+
229
+ if( response.data && response.data.error_message ) {
230
+ self.throwError(response.data.error_message);
231
+ }
232
+ }
233
+ }).fail(function(xhr, status, error) {
234
+ console.log(xhr);
235
+ console.log(status);
236
+ console.log(error);
237
+
238
+ self.throwError(error);
239
+ });
240
+ }
241
+
242
+ },
243
+
244
+ complete: function() {
245
+ this.inprogress = false;
246
+ window.onbeforeunload = null;
247
+ this.setButtonStyleComplete();
248
+ },
249
+
250
+ setButtonStyleRun: function(mode) {
251
+
252
+ this.startOptButton.addClass('wio-running');
253
+
254
+ if( "cron" === mode ) {
255
+ this.startOptButton.text(this.i18n.button_stop_cron);
256
+ return;
257
+ }
258
+
259
+ this.startOptButton.text(this.i18n.button_stop);
260
+ },
261
+
262
+ setButtonStyleComplete: function() {
263
+ this.showMessage(this.i18n.optimization_complete);
264
+ this.startOptButton.text(this.i18n.button_completed);
265
+ this.startOptButton.removeClass('wio-running');
266
+ this.startOptButton.prop('disabled', true);
267
+ },
268
+
269
+ setButtonStyleStop: function() {
270
+ this.startOptButton.removeClass('wio-running');
271
+ this.startOptButton.text(this.i18n.buttom_start);
272
+ },
273
+
274
+ showMessage: function(text) {
275
+ var contanier = $('.wio-page-statistic'),
276
+ message;
277
+
278
+ if( contanier.find('.wrio-statistic-message').length ) {
279
+ message = contanier.find('.wrio-statistic-message');
280
+ } else {
281
+ message = $('<div>');
282
+ message.addClass('wrio-statistic-message');
283
+ contanier.append(message);
284
+ }
285
+
286
+ message.html(text);
287
+ },
288
+
289
+ throwError: function(error_message) {
290
+ this.stop();
291
+
292
+ var noticeId = $.wbcr_factory_clearfy_208.app.showNotice(error_message, 'danger');
293
+
294
+ setTimeout(function() {
295
+ $.wbcr_factory_clearfy_208.app.hideNotice(noticeId);
296
+ }, 10000);
297
+ },
298
+
299
+ destroyMessages: function() {
300
+ $('.wio-page-statistic').find('.wrio-statistic-message').remove();
301
+ },
302
+
303
+ sendRequest: function(data) {
304
+ var self = this;
305
+
306
+ if( !this.inprogress ) {
307
+ return;
308
+ }
309
+
310
+ $.post(ajaxurl, data, function(response) {
311
+ if( !self.inprogress ) {
312
+ return;
313
+ }
314
+
315
+ if( !response || !response.success ) {
316
+ console.log('[Error]: Failed ajax request (Try to optimize images).');
317
+ console.log(response);
318
+
319
+ if( response.data && response.data.error_message ) {
320
+ self.throwError(response.data.error_message);
321
+ }
322
+
323
+ return;
324
+ }
325
+
326
+ data.reset_current_errors = 0;
327
+
328
+ if( !response.data.end ) {
329
+ $('#wio-total-unoptimized').text(parseInt(response.data.remain));
330
+ self.showMessage(self.i18n.optimization_inprogress.replace("%s", parseInt(response.data.remain)));
331
+ self.sendRequest(data);
332
+ } else {
333
+ $('#wio-total-unoptimized').text(response.data.remain);
334
+ self.complete();
335
+
336
+ // если мультисайт режим, то не скрываем кнопку запуска оптимизации
337
+ /*if( $('#wbcr-rio-current-blog').length ) {
338
+ $('#wio-start-optimization').toggleClass('wio-running');
339
+ } else {
340
+ $('#wio-start-optimization').hide();
341
+ }*/
342
+ }
343
+
344
+ redraw_statistics(response.data.statistic);
345
+
346
+ self.updateLog(response.data.last_optimized);
347
+ }).fail(function(xhr, status, error) {
348
+ console.log(xhr);
349
+ console.log(status);
350
+ console.log(error);
351
+
352
+ self.throwError(error);
353
+ });
354
+ },
355
+
356
+ updateLog: function(new_item_data) {
357
+ var self = this;
358
+
359
+ var limit = 100,
360
+ tableEl = $('.wrio-optimization-progress .wrio-table');
361
+
362
+ if( !tableEl.length || !new_item_data ) {
363
+ return;
364
+ }
365
+
366
+ // если таблица была пустая
367
+ if( $('.wrio-table-container-empty').length ) {
368
+ $('.wrio-table-container-empty').addClass('wrio-table-container').removeClass('wrio-table-container-empty');
369
+ if( tableEl.find('tbody').length ) {
370
+ tableEl.find('tbody').empty();
371
+ }
372
+ }
373
+
374
+ $.each(new_item_data, function(index, value) {
375
+ var trEl = $('<tr>'),
376
+ tdEl = $('<td>'),
377
+ webpSize = value.webp_size ? value.webp_size : '-';
378
+
379
+ if( tableEl.find('.wrio-row-id-' + value.id).length ) {
380
+ tableEl.find('.wrio-row-id-' + value.id).remove();
381
+ }
382
+
383
+ trEl.addClass('flash').addClass('wrio-table-item').addClass('wrio-row-id-' + value.id);
384
+
385
+ if( 'error' === value.type ) {
386
+ trEl.addClass('wrio-error');
387
+ }
388
+
389
+ var preview = $('<img width="40" height="40" src="' + value.url + '" alt="">'),
390
+ previewUrl = $('<a href="' + value.url + '">' + value.file_name + '</a>');
391
+
392
+ tableEl.prepend(trEl);
393
+
394
+ trEl.append(tdEl.clone().append(preview));
395
+ trEl.append(tdEl.clone().append(previewUrl));
396
+
397
+ if( 'error' === value.type ) {
398
+ trEl.append(tdEl.clone().attr('colspan', '5').text("Error: " + value.error_msg));
399
+ } else {
400
+ trEl.append(tdEl.clone().text(value.original_size));
401
+ trEl.append(tdEl.clone().text(value.optimized_size));
402
+ trEl.append(tdEl.clone().text(webpSize));
403
+ trEl.append(tdEl.clone().text(value.original_saving));
404
+
405
+ if( "custom-folders" !== self.settings.scope ) {
406
+ trEl.append(tdEl.clone().text(value.thumbnails_count));
407
+ }
408
+
409
+ trEl.append(tdEl.clone().text(value.total_saving));
410
+ }
411
+ });
412
+
413
+ if( tableEl.find('tr').length > limit ) {
414
+ var diff = tableEl.find('tr').length - limit;
415
+
416
+ for( var i = 0; i < diff; i++ ) {
417
+ tableEl.find('tr:last').remove();
418
+ }
419
+ }
420
+ }
421
+
422
+ };
423
+
424
+ $(document).ready(function() {
425
+ bulkOptimization.init();
426
+ });
427
+
428
+ var ajaxUrl = ajaxurl;
429
+ var ai_data;
430
+
431
+ function redraw_statistics(statistic) {
432
+ $('#wio-main-chart').attr('data-unoptimized', statistic.unoptimized)
433
+ .attr('data-optimized', statistic.optimized)
434
+ .attr('data-errors', statistic.error);
435
+ $('#wio-total-optimized-attachments').text(statistic.optimized); // optimized
436
+ $('#wio-original-size').text(bytesToSize(statistic.original_size));
437
+ $('#wio-optimized-size').text(bytesToSize(statistic.optimized_size));
438
+ $('#wio-total-optimized-attachments-pct').text(statistic.save_size_percent + '%');
439
+ $('#wio-overview-chart-percent').html(statistic.optimized_percent + '<span>%</span>');
440
+ $('.wio-total-percent').text(statistic.optimized_percent + '%');
441
+ $('#wio-optimized-bar').css('width', statistic.percent_line + '%');
442
+
443
+ $('#wio-unoptimized-num').text(statistic.unoptimized);
444
+ $('#wio-optimized-num').text(statistic.optimized);
445
+ $('#wio-error-num').text(statistic.error);
446
+
447
+ if( $('.wrio-statistic-nav li.active').length ) {
448
+ $('.wrio-statistic-nav li.active').find('span.wio-statistic-tab-percent').text(statistic.optimized_percent + '%');
449
+ }
450
+
451
+ window.wio_chart.data.datasets[0].data[0] = statistic.unoptimized; // unoptimized
452
+ window.wio_chart.data.datasets[0].data[1] = statistic.optimized; // optimized
453
+ window.wio_chart.data.datasets[0].data[2] = statistic.error; // errors
454
+ window.wio_chart.update();
455
+ if( $('#wio-overview-chart-percent').text() == '100%' ) {
456
+ window.onbeforeunload = null;
457
+ }
458
+ }
459
+
460
+ function bytesToSize(bytes) {
461
+ var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
462
+ if( bytes == 0 ) {
463
+ return '0 Byte';
464
+ }
465
+ var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
466
+ if( i == 0 ) {
467
+ return bytes + ' ' + sizes[i];
468
+ }
469
+ return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
470
+ }
471
+
472
+ /*$('#wbcr-rio-current-blog').on('change', function() {
473
+ var self = $(this);
474
+ $('#wio-start-msg-complete').hide();
475
+ $(this).attr('disabled', true);
476
+ $('#wio-start-optimization').attr('disabled', true);
477
+ var ai_data = {
478
+ 'action': 'wbcr_rio_update_current_blog',
479
+ 'wpnonce': $(this).data('nonce'),
480
+ 'current_blog_id': $(this).find('option:selected').val(),
481
+ 'context': $(this).attr('data-context')
482
+ };
483
+ $.post(ajaxUrl, ai_data, function(response) {
484
+ self.removeAttr('disabled');
485
+ $('#wio-start-optimization').removeAttr('disabled');
486
+ redraw_statistics(response.data.statistic);
487
+ });
488
+ });*/
489
+
490
+ });
admin/assets/js/bulk-optimozation.js DELETED
@@ -1,121 +0,0 @@
1
- jQuery(function($){
2
- var ajaxUrl = ajaxurl;
3
- var ai_data;
4
- $('#wio-start-optimization').on('click', function(){
5
- if ( $(this).hasClass( 'wio-nobackup' ) ) {
6
- result = confirm( $(this).attr('data-confirm') );
7
- if ( ! result ) {
8
- return false;
9
- }
10
- }
11
- $(this).toggleClass( 'wio-running' );
12
- var cron_mode = 0;
13
- if ( $(this).hasClass( 'wio-cron-mode' ) ) {
14
- cron_mode = 1;
15
- }
16
- if ( ! cron_mode ) {
17
- $('#wio-start-msg-top').show();
18
- }
19
-
20
- if ( typeof(ai_data) === 'undefined' ) {
21
- var ai_data = {
22
- 'action': 'wio_process_images',
23
- '_wpnonce': $('#wio-iph-nonce').val(),
24
- 'cron_mode': 0,
25
- 'reset_current_errors': 1
26
- };
27
- }
28
- if ( ! cron_mode ) {
29
- send_post_data(ai_data);
30
- } else {
31
- ai_data.cron_mode = 1;
32
- toggle_cron(ai_data);
33
- }
34
-
35
- if ( $(this).hasClass( 'wio-running' ) ) {
36
- $(this).text( $(this).attr('data-stop') );
37
- if ( ! cron_mode ) {
38
- $('#wio-start-msg-top').show();
39
- }
40
- } else {
41
- $(this).text( $(this).attr('data-start') );
42
- if ( ! cron_mode ) {
43
- $('#wio-start-msg-top').hide();
44
- }
45
- }
46
- });
47
-
48
- function send_post_data(data){
49
- if ( $('#wio-start-optimization').hasClass( 'wio-running' ) ) {
50
- data.reset_current_errors = 0; // текущие ошибки сбрасываем только
51
- $.post(ajaxUrl, data, function(response) {
52
-
53
- if ( ! response.end ) {
54
- $('#wio-total-unoptimized').text( response.remain );
55
- send_post_data(data);
56
- } else {
57
- if ( response.msg ) {
58
- alert( response.msg );
59
- $('#wio-start-msg-top').hide();
60
- $('#wio-start-optimization').hide();
61
- return false;
62
- }
63
- $('#wio-total-unoptimized').text( response.remain );
64
- $('#wio-start-msg-complete').show();
65
- $('#wio-start-msg-top').hide();
66
- $('#wio-start-optimization').hide();
67
- }
68
- redraw_statistics( response.statistic );
69
- });
70
- }
71
- }
72
-
73
- function toggle_cron(data) {
74
- $.post(ajaxUrl, data, function(response) {
75
-
76
- });
77
- }
78
-
79
- function redraw_statistics( statistic ) {
80
- $('#wio-main-chart').attr('data-unoptimized', statistic.unoptimized )
81
- .attr('data-optimized', statistic.optimized )
82
- .attr('data-errors', statistic.error );
83
- $('#wio-total-optimized-attachments').text(statistic.optimized); // optimized
84
- $('#wio-original-size').text( bytesToSize( statistic.original_size ) );
85
- $('#wio-optimized-size').text( bytesToSize( statistic.optimized_size ) );
86
- $('#wio-total-optimized-attachments-pct').text(statistic.save_size_percent + '%');
87
- $('#wio-overview-chart-percent').html(statistic.optimized_percent + '<span>%</span>');
88
- $('.wio-total-percent').text(statistic.optimized_percent + '%');
89
- $('#wio-optimized-bar').css('width', statistic.percent_line + '%');
90
-
91
- $('#wio-unoptimized-num').text( statistic.unoptimized );
92
- $('#wio-optimized-num').text( statistic.optimized );
93
- $('#wio-error-num').text( statistic.error );
94
-
95
- window.wio_chart.data.datasets[0].data[0] = statistic.unoptimized; // unoptimized
96
- window.wio_chart.data.datasets[0].data[1] = statistic.optimized; // optimized
97
- window.wio_chart.data.datasets[0].data[2] = statistic.error; // errors
98
- window.wio_chart.update();
99
- }
100
-
101
- function bytesToSize(bytes) {
102
- var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
103
- if (bytes == 0) return '0 Byte';
104
- var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
105
- if (i == 0) return bytes + ' ' + sizes[i];
106
- return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
107
- }
108
-
109
- $('#wio-level-buttons button').on('click', function() {
110
- var ai_data = {
111
- 'action': 'wio_settings_update_level',
112
- '_wpnonce': $('#wio-iph-nonce').val(),
113
- 'level': $(this).attr('data-level')
114
- };
115
- $.post(ajaxUrl, ai_data, function(response) {
116
-
117
- });
118
- });
119
-
120
-
121
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/assets/js/check-servers-status.js DELETED
@@ -1,82 +0,0 @@
1
- (function($) {
2
- var checkServersStatus = {
3
- init: function() {
4
- this.checkStatus();
5
-
6
- $('.wbcr-rio-select-server-button').click(function() {
7
- var serverName = $(this).data('server'),
8
- nonce = $(this).data('nonce'),
9
- self = $(this);
10
-
11
- if( self.hasClass('wbcr-rio-selected') ) {
12
- return false;
13
- }
14
-
15
- if( !serverName ) {
16
- alert('Undefined error, no server selected');
17
- return;
18
- }
19
-
20
- var data = {
21
- 'action': 'wbcr_rio_select_server',
22
- 'server_name': serverName,
23
- '_wpnonce': nonce
24
- };
25
-
26
- self.addClass('wbcr-rio-loading');
27
-
28
- $.post(ajaxurl, data, function(response) {
29
- $('.wbcr-rio-servers-list-item').removeClass('wbcr-rio-servers-list-item-selected');
30
- $('.wbcr-rio-select-server-button').removeClass('wbcr-rio-selected');
31
-
32
- self.addClass('wbcr-rio-selected').removeClass('wbcr-rio-loading');
33
- self.closest('tr').addClass('wbcr-rio-servers-list-item-selected');
34
- });
35
- });
36
- },
37
-
38
- checkStatus: function() {
39
- var data = {
40
- 'action': 'wbcr_rio_check_servers_status'
41
- };
42
-
43
- var servers = ['server_1', 'server_2', /*'server_4',*/ 'server_3'];
44
-
45
- for( var i = 0; i < servers.length; i++ ) {
46
-
47
- data['server_name'] = servers[i];
48
-
49
- $('.wbcr-rio-' + servers[i]).find('.wbcr-rio-server-status').html('<div class="wbcr-rio-server-check-proccess"></div>');
50
-
51
- $.post(ajaxurl, data, function(response) {
52
- var serverName = response.data.server_name,
53
- serverElement = $('.wbcr-rio-' + serverName).find('.wbcr-rio-server-status');
54
-
55
- if( !response || !response.data || !response.success ) {
56
- if( !response || !response.data ) {
57
- console.log('[Error]: Response error');
58
- console.log(response);
59
- return;
60
- }
61
-
62
- serverElement.removeClass('wbcr-rio-server-warning').removeClass('wbcr-rio-server-success');
63
- serverElement.addClass('wbcr-rio-server-error').html('Down');
64
- return;
65
- }
66
-
67
- serverElement.removeClass('wbcr-rio-server-warning').removeClass('wbcr-rio-server-error');
68
- serverElement.addClass('wbcr-rio-server-success').html('Stable');
69
- });
70
- }
71
- }
72
- };
73
-
74
- $(document).ready(function() {
75
- checkServersStatus.init();
76
- });
77
- })(jQuery);
78
-
79
-
80
-
81
-
82
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/assets/js/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/assets/js/meta-migrations.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ $('#wbcr-wio-meta-migration-action').on('click', function() {
3
+
4
+ var data = {
5
+ 'action': 'wrio_meta_migrations',
6
+ '_wpnonce': $(this).data('nonce'),
7
+ };
8
+
9
+ $(this).addClass('disabled').text('Please wait...');
10
+
11
+ send_request($(this), data);
12
+ });
13
+
14
+ function send_request(button, data) {
15
+ $.post(window.ajaxurl, data, function(response) {
16
+
17
+ console.log(response);
18
+
19
+ if( !response || !response.data ) {
20
+ console.log('An unknown server error has occurred.');
21
+ console.log(response);
22
+ return false;
23
+ }
24
+
25
+ if( !response.data.need_more_time ) {
26
+ if( button.closest('.notice').length ) {
27
+ button.closest('.notice').remove();
28
+ }
29
+ if( button.closest('.alert').length ) {
30
+ button.closest('.alert').remove();
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ button.text(response.data.message);
37
+
38
+ send_request(button, data);
39
+ }).fail(function(xhr, status, error) {
40
+ console.log(xhr);
41
+ console.log(status);
42
+ console.log(error);
43
+
44
+ data.limit = 5;
45
+ data.error = 1;
46
+
47
+ setTimeout(function() {
48
+ send_request(button, data);
49
+ }, 2000);
50
+ });
51
+ }
52
+ });
admin/assets/js/modals.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * A set of tools for creating pop-ups. You can create a popup
3
+ * using a global method call.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 05.04.2019, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+
11
+ (function($) {
12
+ 'use strict';
13
+
14
+ if( !$.wrio_modal ) {
15
+ $.wrio_modal = {};
16
+ }
17
+
18
+ $.wrio_modal = $.wrio_popup || {
19
+
20
+ showErrorModal: function(text) {
21
+ if( !text ) {
22
+ console.log('[Error]: Text required.');
23
+ return;
24
+ }
25
+
26
+ swal({
27
+ title: 'Error',
28
+ text: text,
29
+ type: 'error',
30
+ customClass: 'wrio-modal wrio-modal-error',
31
+ width: 500,
32
+ confirmButtonText: 'OK',
33
+ });
34
+ },
35
+
36
+ showWarningModal: function(text, callback) {
37
+ if( !text ) {
38
+ console.log('[Error]: Text required.');
39
+ return;
40
+ }
41
+
42
+ swal({
43
+ title: 'Warning',
44
+ text: text,
45
+ type: 'warning',
46
+ customClass: 'wrio-modal wrio-modal-warning',
47
+ width: 500,
48
+ showCancelButton: true,
49
+ showCloseButton: true,
50
+ confirmButtonText: 'OK',
51
+ }).then(function(result) {
52
+ if( callback ) {
53
+ callback();
54
+ }
55
+ }).catch(swal.noop);
56
+ },
57
+ };
58
+
59
+ })(jQuery);
admin/assets/js/restore-backup.js CHANGED
@@ -2,6 +2,12 @@ jQuery(function($){
2
  var ajaxUrl = ajaxurl;
3
 
4
  $('#wio-restore-backup-btn').on('click', function() {
 
 
 
 
 
 
5
  result = confirm( $(this).attr('data-confirm') );
6
  if ( ! result ) {
7
  return false;
@@ -19,6 +25,12 @@ jQuery(function($){
19
 
20
  $('#wio-clear-backup-btn').on('click', function() {
21
  $('#wio-restore-backup-msg').hide();
 
 
 
 
 
 
22
  result = confirm( $(this).attr('data-confirm') );
23
  if ( ! result ) {
24
  return false;
@@ -32,6 +44,86 @@ jQuery(function($){
32
  });
33
  });
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  function send_post_data(data){
36
  $.post(ajaxUrl, data, function(response) {
37
  if ( ! response.end ) {
@@ -44,4 +136,27 @@ jQuery(function($){
44
  }
45
  });
46
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  });
2
  var ajaxUrl = ajaxurl;
3
 
4
  $('#wio-restore-backup-btn').on('click', function() {
5
+ if ( $('#wio-multisite-mode').length ) {
6
+ $('#wio-multisite-mode').toggle();
7
+ $('#wio-multisite-confirm').attr('data-action', 'restore');
8
+ $('#wio-multisite-restore-progress').empty();
9
+ return false;
10
+ }
11
  result = confirm( $(this).attr('data-confirm') );
12
  if ( ! result ) {
13
  return false;
25
 
26
  $('#wio-clear-backup-btn').on('click', function() {
27
  $('#wio-restore-backup-msg').hide();
28
+ if ( $('#wio-multisite-mode').length ) {
29
+ $('#wio-multisite-mode').toggle();
30
+ $('#wio-multisite-confirm').attr('data-action', 'clear');
31
+ $('#wio-multisite-restore-progress').empty();
32
+ return false;
33
+ }
34
  result = confirm( $(this).attr('data-confirm') );
35
  if ( ! result ) {
36
  return false;
44
  });
45
  });
46
 
47
+ $('#wio-multisite-confirm').on('click', function() {
48
+ var action = $(this).attr('data-action');
49
+ // если запущена очистка резервных копий
50
+ if ( action == 'clear' ) {
51
+ result = confirm( $('#wio-clear-backup-btn').attr('data-confirm') ); // берём сообщение из основной кнопки
52
+ if ( ! result ) {
53
+ return false;
54
+ }
55
+ var blogs = [];
56
+ $('.wbcr_io_multisite_blogs:checked').each(function() {
57
+ blogs.push( $(this).val() );
58
+ });
59
+ var data = {
60
+ 'action': 'wio_clear_backup',
61
+ '_wpnonce': $('#wio-iph-nonce').val(),
62
+ 'blogs': blogs
63
+ };
64
+
65
+ $.post(ajaxUrl, data, function(response) {
66
+ $('#wio-clear-backup-msg').show();
67
+ $('#wio-multisite-mode').toggle();
68
+ });
69
+ return false;
70
+ }
71
+
72
+ // если запущено восстановление из резервных копий
73
+ if ( action == 'restore' ) {
74
+ result = confirm( $('#wio-restore-backup-btn').attr('data-confirm') ); // берём сообщение из основной кнопки
75
+ if ( ! result ) {
76
+ return false;
77
+ }
78
+ $('#wio-multisite-mode').toggle();
79
+ $('#wio-multisite-restore-progress').empty();
80
+ $('.wbcr_io_multisite_blogs:checked').each(function() {
81
+ $('#wio-multisite-restore-progress').append('\
82
+ <label>'+$(this).attr('data-name')+'</label>\
83
+ <div class="progress">\
84
+ <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%">\
85
+ </div>\
86
+ </div>\
87
+ ');
88
+ });
89
+ $('#wio-multisite-restore-progress').show();
90
+ if ( ! $('.wio-restore-backup-progressbar').length ) {
91
+ $('#wio-restore-backup-msg').show();
92
+ return false;
93
+ }
94
+ var ai_data = {
95
+ 'total' : '?',
96
+ 'action': 'wio_restore_backup',
97
+ '_wpnonce': $('#wio-iph-nonce').val(),
98
+ 'blog_id': $('.wio-restore-backup-progressbar:eq(0)').attr('data-id')
99
+ };
100
+ send_multisite_post_data(ai_data);
101
+ return false;
102
+ }
103
+ });
104
+
105
+ $('#wbcr_io_multisite_blog_all').on('change', function() {
106
+ if ( $(this).attr('checked') == 'checked' ) {
107
+ $('.wbcr_io_multisite_blogs').attr('checked', true);
108
+ } else {
109
+ $('.wbcr_io_multisite_blogs').removeAttr('checked');
110
+ }
111
+ });
112
+
113
+ $('.wbcr_io_multisite_blogs').on('change', function() {
114
+ var all_checked = true;
115
+ $('.wbcr_io_multisite_blogs').each(function() {
116
+ if ( $(this).attr('checked') != 'checked' ) {
117
+ all_checked = false;
118
+ }
119
+ });
120
+ if ( all_checked ) {
121
+ $('#wbcr_io_multisite_blog_all').attr('checked', true);
122
+ } else {
123
+ $('#wbcr_io_multisite_blog_all').removeAttr('checked');
124
+ }
125
+ });
126
+
127
  function send_post_data(data){
128
  $.post(ajaxUrl, data, function(response) {
129
  if ( ! response.end ) {
136
  }
137
  });
138
  }
139
+
140
+ function send_multisite_post_data(data){
141
+ $.post(ajaxUrl, data, function(response) {
142
+ if ( ! response.end ) {
143
+ data.total = response.total;
144
+ send_multisite_post_data(data);
145
+ $('#wio-restore-backup-progress-' + data.blog_id).css( 'width', response.percent + '%' );
146
+ } else {
147
+ $('#wio-restore-backup-progress-' + data.blog_id).css( 'width', '100%' ).removeClass('wio-restore-backup-progressbar');
148
+ if ( $('.wio-restore-backup-progressbar').length ) {
149
+ var ai_data = {
150
+ 'total' : '?',
151
+ 'action': 'wio_restore_backup',
152
+ '_wpnonce': $('#wio-iph-nonce').val(),
153
+ 'blog_id': $('.wio-restore-backup-progressbar:eq(0)').attr('data-id')
154
+ };
155
+ send_multisite_post_data(ai_data);
156
+ } else {
157
+ $('#wio-restore-backup-msg').show();
158
+ }
159
+ }
160
+ });
161
+ }
162
  });
admin/assets/js/single-optimization.js CHANGED
@@ -3,14 +3,23 @@ jQuery(function($) {
3
 
4
  $(document).on('click', '.wio-reoptimize', function() {
5
  var ai_data = {
6
- 'action' : 'wio_reoptimize_image',
7
  'id' : $(this).attr('data-id'),
8
  'level' : $(this).attr('data-level')
9
  };
10
  var td = $(this).closest('td');
11
  var msg = $(this).attr( 'data-waiting-label' );
12
  td.html('<p>'+msg+'</p>');
 
 
 
 
 
13
  $.post(ajaxUrl, ai_data, function(response) {
 
 
 
 
14
  td.html(response);
15
  var btn = $('.wio-reoptimize').first();
16
 
@@ -25,12 +34,11 @@ jQuery(function($) {
25
  }
26
  }
27
  });
28
- return false;
29
- });
30
 
31
  $(document).on('click', '.button-wio-restore', function() {
32
  var ai_data = {
33
- 'action' : 'wio_restore_image',
34
  'id' : $(this).attr('data-id')
35
  };
36
  var td = $(this).closest('td');
3
 
4
  $(document).on('click', '.wio-reoptimize', function() {
5
  var ai_data = {
6
+ 'action' : $(this).attr('data-action'),
7
  'id' : $(this).attr('data-id'),
8
  'level' : $(this).attr('data-level')
9
  };
10
  var td = $(this).closest('td');
11
  var msg = $(this).attr( 'data-waiting-label' );
12
  td.html('<p>'+msg+'</p>');
13
+ wio_reoptimize( ai_data, td );
14
+ return false;
15
+ });
16
+
17
+ function wio_reoptimize( ai_data, td ) {
18
  $.post(ajaxUrl, ai_data, function(response) {
19
+ if ( response === 'processing' ) {
20
+ wio_reoptimize( ai_data, td );
21
+ return false;
22
+ }
23
  td.html(response);
24
  var btn = $('.wio-reoptimize').first();
25
 
34
  }
35
  }
36
  });
37
+ }
 
38
 
39
  $(document).on('click', '.button-wio-restore', function() {
40
  var ai_data = {
41
+ 'action' : $(this).attr('data-action'),
42
  'id' : $(this).attr('data-id')
43
  };
44
  var td = $(this).closest('td');
admin/assets/js/sweetalert2.js ADDED
@@ -0,0 +1,1641 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * sweetalert2 v6.6.6
3
+ * Released under the MIT License.
4
+ */
5
+ (function (global, factory) {
6
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
7
+ typeof define === 'function' && define.amd ? define(factory) :
8
+ (global.Sweetalert2 = factory());
9
+ }(this, (function () { 'use strict';
10
+
11
+ var defaultParams = {
12
+ title: '',
13
+ titleText: '',
14
+ text: '',
15
+ html: '',
16
+ type: null,
17
+ customClass: '',
18
+ target: 'body',
19
+ animation: true,
20
+ allowOutsideClick: true,
21
+ allowEscapeKey: true,
22
+ allowEnterKey: true,
23
+ showConfirmButton: true,
24
+ showCancelButton: false,
25
+ preConfirm: null,
26
+ confirmButtonText: 'OK',
27
+ confirmButtonColor: '#3085d6',
28
+ confirmButtonClass: null,
29
+ cancelButtonText: 'Cancel',
30
+ cancelButtonColor: '#aaa',
31
+ cancelButtonClass: null,
32
+ buttonsStyling: true,
33
+ reverseButtons: false,
34
+ focusCancel: false,
35
+ showCloseButton: false,
36
+ showLoaderOnConfirm: false,
37
+ imageUrl: null,
38
+ imageWidth: null,
39
+ imageHeight: null,
40
+ imageClass: null,
41
+ timer: null,
42
+ width: 500,
43
+ padding: 20,
44
+ background: '#fff',
45
+ input: null,
46
+ inputPlaceholder: '',
47
+ inputValue: '',
48
+ inputOptions: {},
49
+ inputAutoTrim: true,
50
+ inputClass: null,
51
+ inputAttributes: {},
52
+ inputValidator: null,
53
+ progressSteps: [],
54
+ currentProgressStep: null,
55
+ progressStepsDistance: '40px',
56
+ onOpen: null,
57
+ onClose: null,
58
+ useRejections: true
59
+ };
60
+
61
+ var swalPrefix = 'swal2-';
62
+
63
+ var prefix = function prefix(items) {
64
+ var result = {};
65
+ for (var i in items) {
66
+ result[items[i]] = swalPrefix + items[i];
67
+ }
68
+ return result;
69
+ };
70
+
71
+ var swalClasses = prefix(['container', 'shown', 'iosfix', 'modal', 'overlay', 'fade', 'show', 'hide', 'noanimation', 'close', 'title', 'content', 'buttonswrapper', 'confirm', 'cancel', 'icon', 'image', 'input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea', 'inputerror', 'validationerror', 'progresssteps', 'activeprogressstep', 'progresscircle', 'progressline', 'loading', 'styled']);
72
+
73
+ var iconTypes = prefix(['success', 'warning', 'info', 'question', 'error']);
74
+
75
+ /*
76
+ * Set hover, active and focus-states for buttons (source: http://www.sitepoint.com/javascript-generate-lighter-darker-color)
77
+ */
78
+ var colorLuminance = function colorLuminance(hex, lum) {
79
+ // Validate hex string
80
+ hex = String(hex).replace(/[^0-9a-f]/gi, '');
81
+ if (hex.length < 6) {
82
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
83
+ }
84
+ lum = lum || 0;
85
+
86
+ // Convert to decimal and change luminosity
87
+ var rgb = '#';
88
+ for (var i = 0; i < 3; i++) {
89
+ var c = parseInt(hex.substr(i * 2, 2), 16);
90
+ c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16);
91
+ rgb += ('00' + c).substr(c.length);
92
+ }
93
+
94
+ return rgb;
95
+ };
96
+
97
+ var uniqueArray = function uniqueArray(arr) {
98
+ var result = [];
99
+ for (var i in arr) {
100
+ if (result.indexOf(arr[i]) === -1) {
101
+ result.push(arr[i]);
102
+ }
103
+ }
104
+ return result;
105
+ };
106
+
107
+ /* global MouseEvent */
108
+
109
+ // Remember state in cases where opening and handling a modal will fiddle with it.
110
+ var states = {
111
+ previousWindowKeyDown: null,
112
+ previousActiveElement: null,
113
+ previousBodyPadding: null
114
+
115
+ /*
116
+ * Add modal + overlay to DOM
117
+ */
118
+ };var init = function init(params) {
119
+ if (typeof document === 'undefined') {
120
+ console.error('SweetAlert2 requires document to initialize');
121
+ return;
122
+ }
123
+
124
+ var container = document.createElement('div');
125
+ container.className = swalClasses.container;
126
+ container.innerHTML = sweetHTML;
127
+
128
+ var targetElement = document.querySelector(params.target);
129
+ if (!targetElement) {
130
+ console.warn('SweetAlert2: Can\'t find the target "' + params.target + '"');
131
+ targetElement = document.body;
132
+ }
133
+ targetElement.appendChild(container);
134
+
135
+ var modal = getModal();
136
+ var input = getChildByClass(modal, swalClasses.input);
137
+ var file = getChildByClass(modal, swalClasses.file);
138
+ var range = modal.querySelector('.' + swalClasses.range + ' input');
139
+ var rangeOutput = modal.querySelector('.' + swalClasses.range + ' output');
140
+ var select = getChildByClass(modal, swalClasses.select);
141
+ var checkbox = modal.querySelector('.' + swalClasses.checkbox + ' input');
142
+ var textarea = getChildByClass(modal, swalClasses.textarea);
143
+
144
+ input.oninput = function () {
145
+ sweetAlert.resetValidationError();
146
+ };
147
+
148
+ input.onkeydown = function (event) {
149
+ setTimeout(function () {
150
+ if (event.keyCode === 13 && params.allowEnterKey) {
151
+ event.stopPropagation();
152
+ sweetAlert.clickConfirm();
153
+ }
154
+ }, 0);
155
+ };
156
+
157
+ file.onchange = function () {
158
+ sweetAlert.resetValidationError();
159
+ };
160
+
161
+ range.oninput = function () {
162
+ sweetAlert.resetValidationError();
163
+ rangeOutput.value = range.value;
164
+ };
165
+
166
+ range.onchange = function () {
167
+ sweetAlert.resetValidationError();
168
+ range.previousSibling.value = range.value;
169
+ };
170
+
171
+ select.onchange = function () {
172
+ sweetAlert.resetValidationError();
173
+ };
174
+
175
+ checkbox.onchange = function () {
176
+ sweetAlert.resetValidationError();
177
+ };
178
+
179
+ textarea.oninput = function () {
180
+ sweetAlert.resetValidationError();
181
+ };
182
+
183
+ return modal;
184
+ };
185
+
186
+ /*
187
+ * Manipulate DOM
188
+ */
189
+
190
+ var sweetHTML = ('\n <div role="dialog" aria-labelledby="' + swalClasses.title + '" aria-describedby="' + swalClasses.content + '" class="' + swalClasses.modal + '" tabindex="-1">\n <ul class="' + swalClasses.progresssteps + '"></ul>\n <div class="' + swalClasses.icon + ' ' + iconTypes.error + '">\n <span class="swal2-x-mark"><span class="swal2-x-mark-line-left"></span><span class="swal2-x-mark-line-right"></span></span>\n </div>\n <div class="' + swalClasses.icon + ' ' + iconTypes.question + '">?</div>\n <div class="' + swalClasses.icon + ' ' + iconTypes.warning + '">!</div>\n <div class="' + swalClasses.icon + ' ' + iconTypes.info + '">i</div>\n <div class="' + swalClasses.icon + ' ' + iconTypes.success + '">\n <div class="swal2-success-circular-line-left"></div>\n <span class="swal2-success-line-tip"></span> <span class="swal2-success-line-long"></span>\n <div class="swal2-success-ring"></div> <div class="swal2-success-fix"></div>\n <div class="swal2-success-circular-line-right"></div>\n </div>\n <img class="' + swalClasses.image + '" />\n <h2 class="' + swalClasses.title + '" id="' + swalClasses.title + '"></h2>\n <div id="' + swalClasses.content + '" class="' + swalClasses.content + '"></div>\n <input class="' + swalClasses.input + '" />\n <input type="file" class="' + swalClasses.file + '" />\n <div class="' + swalClasses.range + '">\n <output></output>\n <input type="range" />\n </div>\n <select class="' + swalClasses.select + '"></select>\n <div class="' + swalClasses.radio + '"></div>\n <label for="' + swalClasses.checkbox + '" class="' + swalClasses.checkbox + '">\n <input type="checkbox" />\n </label>\n <textarea class="' + swalClasses.textarea + '"></textarea>\n <div class="' + swalClasses.validationerror + '"></div>\n <div class="' + swalClasses.buttonswrapper + '">\n <button type="button" class="' + swalClasses.confirm + '">OK</button>\n <button type="button" class="' + swalClasses.cancel + '">Cancel</button>\n </div>\n <button type="button" class="' + swalClasses.close + '" aria-label="Close this dialog">\xD7</button>\n </div>\n').replace(/(^|\n)\s*/g, '');
191
+
192
+ var getContainer = function getContainer() {
193
+ return document.body.querySelector('.' + swalClasses.container);
194
+ };
195
+
196
+ var getModal = function getModal() {
197
+ return getContainer() ? getContainer().querySelector('.' + swalClasses.modal) : null;
198
+ };
199
+
200
+ var getIcons = function getIcons() {
201
+ var modal = getModal();
202
+ return modal.querySelectorAll('.' + swalClasses.icon);
203
+ };
204
+
205
+ var elementByClass = function elementByClass(className) {
206
+ return getContainer() ? getContainer().querySelector('.' + className) : null;
207
+ };
208
+
209
+ var getTitle = function getTitle() {
210
+ return elementByClass(swalClasses.title);
211
+ };
212
+
213
+ var getContent = function getContent() {
214
+ return elementByClass(swalClasses.content);
215
+ };
216
+
217
+ var getImage = function getImage() {
218
+ return elementByClass(swalClasses.image);
219
+ };
220
+
221
+ var getButtonsWrapper = function getButtonsWrapper() {
222
+ return elementByClass(swalClasses.buttonswrapper);
223
+ };
224
+
225
+ var getProgressSteps = function getProgressSteps() {
226
+ return elementByClass(swalClasses.progresssteps);
227
+ };
228
+
229
+ var getValidationError = function getValidationError() {
230
+ return elementByClass(swalClasses.validationerror);
231
+ };
232
+
233
+ var getConfirmButton = function getConfirmButton() {
234
+ return elementByClass(swalClasses.confirm);
235
+ };
236
+
237
+ var getCancelButton = function getCancelButton() {
238
+ return elementByClass(swalClasses.cancel);
239
+ };
240
+
241
+ var getCloseButton = function getCloseButton() {
242
+ return elementByClass(swalClasses.close);
243
+ };
244
+
245
+ var getFocusableElements = function getFocusableElements(focusCancel) {
246
+ var buttons = [getConfirmButton(), getCancelButton()];
247
+ if (focusCancel) {
248
+ buttons.reverse();
249
+ }
250
+ var focusableElements = buttons.concat(Array.prototype.slice.call(getModal().querySelectorAll('button, input:not([type=hidden]), textarea, select, a, *[tabindex]:not([tabindex="-1"])')));
251
+ return uniqueArray(focusableElements);
252
+ };
253
+
254
+ var hasClass = function hasClass(elem, className) {
255
+ if (elem.classList) {
256
+ return elem.classList.contains(className);
257
+ }
258
+ return false;
259
+ };
260
+
261
+ var focusInput = function focusInput(input) {
262
+ input.focus();
263
+
264
+ // place cursor at end of text in text input
265
+ if (input.type !== 'file') {
266
+ // http://stackoverflow.com/a/2345915/1331425
267
+ var val = input.value;
268
+ input.value = '';
269
+ input.value = val;
270
+ }
271
+ };
272
+
273
+ var addClass = function addClass(elem, className) {
274
+ if (!elem || !className) {
275
+ return;
276
+ }
277
+ var classes = className.split(/\s+/).filter(Boolean);
278
+ classes.forEach(function (className) {
279
+ elem.classList.add(className);
280
+ });
281
+ };
282
+
283
+ var removeClass = function removeClass(elem, className) {
284
+ if (!elem || !className) {
285
+ return;
286
+ }
287
+ var classes = className.split(/\s+/).filter(Boolean);
288
+ classes.forEach(function (className) {
289
+ elem.classList.remove(className);
290
+ });
291
+ };
292
+
293
+ var getChildByClass = function getChildByClass(elem, className) {
294
+ for (var i = 0; i < elem.childNodes.length; i++) {
295
+ if (hasClass(elem.childNodes[i], className)) {
296
+ return elem.childNodes[i];
297
+ }
298
+ }
299
+ };
300
+
301
+ var show = function show(elem, display) {
302
+ if (!display) {
303
+ display = 'block';
304
+ }
305
+ elem.style.opacity = '';
306
+ elem.style.display = display;
307
+ };
308
+
309
+ var hide = function hide(elem) {
310
+ elem.style.opacity = '';
311
+ elem.style.display = 'none';
312
+ };
313
+
314
+ var empty = function empty(elem) {
315
+ while (elem.firstChild) {
316
+ elem.removeChild(elem.firstChild);
317
+ }
318
+ };
319
+
320
+ // borrowed from jqeury $(elem).is(':visible') implementation
321
+ var isVisible = function isVisible(elem) {
322
+ return elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length;
323
+ };
324
+
325
+ var removeStyleProperty = function removeStyleProperty(elem, property) {
326
+ if (elem.style.removeProperty) {
327
+ elem.style.removeProperty(property);
328
+ } else {
329
+ elem.style.removeAttribute(property);
330
+ }
331
+ };
332
+
333
+ var fireClick = function fireClick(node) {
334
+ if (!isVisible(node)) {
335
+ return false;
336
+ }
337
+
338
+ // Taken from http://www.nonobtrusive.com/2011/11/29/programatically-fire-crossbrowser-click-event-with-javascript/
339
+ // Then fixed for today's Chrome browser.
340
+ if (typeof MouseEvent === 'function') {
341
+ // Up-to-date approach
342
+ var mevt = new MouseEvent('click', {
343
+ view: window,
344
+ bubbles: false,
345
+ cancelable: true
346
+ });
347
+ node.dispatchEvent(mevt);
348
+ } else if (document.createEvent) {
349
+ // Fallback
350
+ var evt = document.createEvent('MouseEvents');
351
+ evt.initEvent('click', false, false);
352
+ node.dispatchEvent(evt);
353
+ } else if (document.createEventObject) {
354
+ node.fireEvent('onclick');
355
+ } else if (typeof node.onclick === 'function') {
356
+ node.onclick();
357
+ }
358
+ };
359
+
360
+ var animationEndEvent = function () {
361
+ var testEl = document.createElement('div');
362
+ var transEndEventNames = {
363
+ 'WebkitAnimation': 'webkitAnimationEnd',
364
+ 'OAnimation': 'oAnimationEnd oanimationend',
365
+ 'msAnimation': 'MSAnimationEnd',
366
+ 'animation': 'animationend'
367
+ };
368
+ for (var i in transEndEventNames) {
369
+ if (transEndEventNames.hasOwnProperty(i) && testEl.style[i] !== undefined) {
370
+ return transEndEventNames[i];
371
+ }
372
+ }
373
+
374
+ return false;
375
+ }();
376
+
377
+ // Reset previous window keydown handler and focued element
378
+ var resetPrevState = function resetPrevState() {
379
+ window.onkeydown = states.previousWindowKeyDown;
380
+ if (states.previousActiveElement && states.previousActiveElement.focus) {
381
+ var x = window.scrollX;
382
+ var y = window.scrollY;
383
+ states.previousActiveElement.focus();
384
+ if (x && y) {
385
+ // IE has no scrollX/scrollY support
386
+ window.scrollTo(x, y);
387
+ }
388
+ }
389
+ };
390
+
391
+ // Measure width of scrollbar
392
+ // https://github.com/twbs/bootstrap/blob/master/js/modal.js#L279-L286
393
+ var measureScrollbar = function measureScrollbar() {
394
+ var supportsTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints;
395
+ if (supportsTouch) {
396
+ return 0;
397
+ }
398
+ var scrollDiv = document.createElement('div');
399
+ scrollDiv.style.width = '50px';
400
+ scrollDiv.style.height = '50px';
401
+ scrollDiv.style.overflow = 'scroll';
402
+ document.body.appendChild(scrollDiv);
403
+ var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
404
+ document.body.removeChild(scrollDiv);
405
+ return scrollbarWidth;
406
+ };
407
+
408
+ // JavaScript Debounce Function
409
+ // Simplivied version of https://davidwalsh.name/javascript-debounce-function
410
+ var debounce = function debounce(func, wait) {
411
+ var timeout = void 0;
412
+ return function () {
413
+ var later = function later() {
414
+ timeout = null;
415
+ func();
416
+ };
417
+ clearTimeout(timeout);
418
+ timeout = setTimeout(later, wait);
419
+ };
420
+ };
421
+
422
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
423
+ return typeof obj;
424
+ } : function (obj) {
425
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
426
+ };
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+ var _extends = Object.assign || function (target) {
449
+ for (var i = 1; i < arguments.length; i++) {
450
+ var source = arguments[i];
451
+
452
+ for (var key in source) {
453
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
454
+ target[key] = source[key];
455
+ }
456
+ }
457
+ }
458
+
459
+ return target;
460
+ };
461
+
462
+ var modalParams = _extends({}, defaultParams);
463
+ var queue = [];
464
+ var swal2Observer = void 0;
465
+
466
+ /*
467
+ * Set type, text and actions on modal
468
+ */
469
+ var setParameters = function setParameters(params) {
470
+ var modal = getModal() || init(params);
471
+
472
+ for (var param in params) {
473
+ if (!defaultParams.hasOwnProperty(param) && param !== 'extraParams') {
474
+ console.warn('SweetAlert2: Unknown parameter "' + param + '"');
475
+ }
476
+ }
477
+
478
+ // Set modal width
479
+ modal.style.width = typeof params.width === 'number' ? params.width + 'px' : params.width;
480
+
481
+ modal.style.padding = params.padding + 'px';
482
+ modal.style.background = params.background;
483
+ var successIconParts = modal.querySelectorAll('[class^=swal2-success-circular-line], .swal2-success-fix');
484
+ for (var i = 0; i < successIconParts.length; i++) {
485
+ successIconParts[i].style.background = params.background;
486
+ }
487
+
488
+ var title = getTitle();
489
+ var content = getContent();
490
+ var buttonsWrapper = getButtonsWrapper();
491
+ var confirmButton = getConfirmButton();
492
+ var cancelButton = getCancelButton();
493
+ var closeButton = getCloseButton();
494
+
495
+ // Title
496
+ if (params.titleText) {
497
+ title.innerText = params.titleText;
498
+ } else {
499
+ title.innerHTML = params.title.split('\n').join('<br />');
500
+ }
501
+
502
+ // Content
503
+ if (params.text || params.html) {
504
+ if (_typeof(params.html) === 'object') {
505
+ content.innerHTML = '';
506
+ if (0 in params.html) {
507
+ for (var _i = 0; _i in params.html; _i++) {
508
+ content.appendChild(params.html[_i].cloneNode(true));
509
+ }
510
+ } else {
511
+ content.appendChild(params.html.cloneNode(true));
512
+ }
513
+ } else if (params.html) {
514
+ content.innerHTML = params.html;
515
+ } else if (params.text) {
516
+ content.textContent = params.text;
517
+ }
518
+ show(content);
519
+ } else {
520
+ hide(content);
521
+ }
522
+
523
+ // Close button
524
+ if (params.showCloseButton) {
525
+ show(closeButton);
526
+ } else {
527
+ hide(closeButton);
528
+ }
529
+
530
+ // Custom Class
531
+ modal.className = swalClasses.modal;
532
+ if (params.customClass) {
533
+ addClass(modal, params.customClass);
534
+ }
535
+
536
+ // Progress steps
537
+ var progressStepsContainer = getProgressSteps();
538
+ var currentProgressStep = parseInt(params.currentProgressStep === null ? sweetAlert.getQueueStep() : params.currentProgressStep, 10);
539
+ if (params.progressSteps.length) {
540
+ show(progressStepsContainer);
541
+ empty(progressStepsContainer);
542
+ if (currentProgressStep >= params.progressSteps.length) {
543
+ console.warn('SweetAlert2: Invalid currentProgressStep parameter, it should be less than progressSteps.length ' + '(currentProgressStep like JS arrays starts from 0)');
544
+ }
545
+ params.progressSteps.forEach(function (step, index) {
546
+ var circle = document.createElement('li');
547
+ addClass(circle, swalClasses.progresscircle);
548
+ circle.innerHTML = step;
549
+ if (index === currentProgressStep) {
550
+ addClass(circle, swalClasses.activeprogressstep);
551
+ }
552
+ progressStepsContainer.appendChild(circle);
553
+ if (index !== params.progressSteps.length - 1) {
554
+ var line = document.createElement('li');
555
+ addClass(line, swalClasses.progressline);
556
+ line.style.width = params.progressStepsDistance;
557
+ progressStepsContainer.appendChild(line);
558
+ }
559
+ });
560
+ } else {
561
+ hide(progressStepsContainer);
562
+ }
563
+
564
+ // Icon
565
+ var icons = getIcons();
566
+ for (var _i2 = 0; _i2 < icons.length; _i2++) {
567
+ hide(icons[_i2]);
568
+ }
569
+ if (params.type) {
570
+ var validType = false;
571
+ for (var iconType in iconTypes) {
572
+ if (params.type === iconType) {
573
+ validType = true;
574
+ break;
575
+ }
576
+ }
577
+ if (!validType) {
578
+ console.error('SweetAlert2: Unknown alert type: ' + params.type);
579
+ return false;
580
+ }
581
+ var icon = modal.querySelector('.' + swalClasses.icon + '.' + iconTypes[params.type]);
582
+ show(icon);
583
+
584
+ // Animate icon
585
+ if (params.animation) {
586
+ switch (params.type) {
587
+ case 'success':
588
+ addClass(icon, 'swal2-animate-success-icon');
589
+ addClass(icon.querySelector('.swal2-success-line-tip'), 'swal2-animate-success-line-tip');
590
+ addClass(icon.querySelector('.swal2-success-line-long'), 'swal2-animate-success-line-long');
591
+ break;
592
+ case 'error':
593
+ addClass(icon, 'swal2-animate-error-icon');
594
+ addClass(icon.querySelector('.swal2-x-mark'), 'swal2-animate-x-mark');
595
+ break;
596
+ default:
597
+ break;
598
+ }
599
+ }
600
+ }
601
+
602
+ // Custom image
603
+ var image = getImage();
604
+ if (params.imageUrl) {
605
+ image.setAttribute('src', params.imageUrl);
606
+ show(image);
607
+
608
+ if (params.imageWidth) {
609
+ image.setAttribute('width', params.imageWidth);
610
+ } else {
611
+ image.removeAttribute('width');
612
+ }
613
+
614
+ if (params.imageHeight) {
615
+ image.setAttribute('height', params.imageHeight);
616
+ } else {
617
+ image.removeAttribute('height');
618
+ }
619
+
620
+ image.className = swalClasses.image;
621
+ if (params.imageClass) {
622
+ addClass(image, params.imageClass);
623
+ }
624
+ } else {
625
+ hide(image);
626
+ }
627
+
628
+ // Cancel button
629
+ if (params.showCancelButton) {
630
+ cancelButton.style.display = 'inline-block';
631
+ } else {
632
+ hide(cancelButton);
633
+ }
634
+
635
+ // Confirm button
636
+ if (params.showConfirmButton) {
637
+ removeStyleProperty(confirmButton, 'display');
638
+ } else {
639
+ hide(confirmButton);
640
+ }
641
+
642
+ // Buttons wrapper
643
+ if (!params.showConfirmButton && !params.showCancelButton) {
644
+ hide(buttonsWrapper);
645
+ } else {
646
+ show(buttonsWrapper);
647
+ }
648
+
649
+ // Edit text on cancel and confirm buttons
650
+ confirmButton.innerHTML = params.confirmButtonText;
651
+ cancelButton.innerHTML = params.cancelButtonText;
652
+
653
+ // Set buttons to selected background colors
654
+ if (params.buttonsStyling) {
655
+ confirmButton.style.backgroundColor = params.confirmButtonColor;
656
+ cancelButton.style.backgroundColor = params.cancelButtonColor;
657
+ }
658
+
659
+ // Add buttons custom classes
660
+ confirmButton.className = swalClasses.confirm;
661
+ addClass(confirmButton, params.confirmButtonClass);
662
+ cancelButton.className = swalClasses.cancel;
663
+ addClass(cancelButton, params.cancelButtonClass);
664
+
665
+ // Buttons styling
666
+ if (params.buttonsStyling) {
667
+ addClass(confirmButton, swalClasses.styled);
668
+ addClass(cancelButton, swalClasses.styled);
669
+ } else {
670
+ removeClass(confirmButton, swalClasses.styled);
671
+ removeClass(cancelButton, swalClasses.styled);
672
+
673
+ confirmButton.style.backgroundColor = confirmButton.style.borderLeftColor = confirmButton.style.borderRightColor = '';
674
+ cancelButton.style.backgroundColor = cancelButton.style.borderLeftColor = cancelButton.style.borderRightColor = '';
675
+ }
676
+
677
+ // CSS animation
678
+ if (params.animation === true) {
679
+ removeClass(modal, swalClasses.noanimation);
680
+ } else {
681
+ addClass(modal, swalClasses.noanimation);
682
+ }
683
+ };
684
+
685
+ /*
686
+ * Animations
687
+ */
688
+ var openModal = function openModal(animation, onComplete) {
689
+ var container = getContainer();
690
+ var modal = getModal();
691
+
692
+ if (animation) {
693
+ addClass(modal, swalClasses.show);
694
+ addClass(container, swalClasses.fade);
695
+ removeClass(modal, swalClasses.hide);
696
+ } else {
697
+ removeClass(modal, swalClasses.fade);
698
+ }
699
+ show(modal);
700
+
701
+ // scrolling is 'hidden' until animation is done, after that 'auto'
702
+ container.style.overflowY = 'hidden';
703
+ if (animationEndEvent && !hasClass(modal, swalClasses.noanimation)) {
704
+ modal.addEventListener(animationEndEvent, function swalCloseEventFinished() {
705
+ modal.removeEventListener(animationEndEvent, swalCloseEventFinished);
706
+ container.style.overflowY = 'auto';
707
+ });
708
+ } else {
709
+ container.style.overflowY = 'auto';
710
+ }
711
+
712
+ addClass(document.documentElement, swalClasses.shown);
713
+ addClass(document.body, swalClasses.shown);
714
+ addClass(container, swalClasses.shown);
715
+ fixScrollbar();
716
+ iOSfix();
717
+ states.previousActiveElement = document.activeElement;
718
+ if (onComplete !== null && typeof onComplete === 'function') {
719
+ setTimeout(function () {
720
+ onComplete(modal);
721
+ });
722
+ }
723
+ };
724
+
725
+ var fixScrollbar = function fixScrollbar() {
726
+ // for queues, do not do this more than once
727
+ if (states.previousBodyPadding !== null) {
728
+ return;
729
+ }
730
+ // if the body has overflow
731
+ if (document.body.scrollHeight > window.innerHeight) {
732
+ // add padding so the content doesn't shift after removal of scrollbar
733
+ states.previousBodyPadding = document.body.style.paddingRight;
734
+ document.body.style.paddingRight = measureScrollbar() + 'px';
735
+ }
736
+ };
737
+
738
+ var undoScrollbar = function undoScrollbar() {
739
+ if (states.previousBodyPadding !== null) {
740
+ document.body.style.paddingRight = states.previousBodyPadding;
741
+ states.previousBodyPadding = null;
742
+ }
743
+ };
744
+
745
+ // Fix iOS scrolling http://stackoverflow.com/q/39626302/1331425
746
+ var iOSfix = function iOSfix() {
747
+ var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
748
+ if (iOS && !hasClass(document.body, swalClasses.iosfix)) {
749
+ var offset = document.body.scrollTop;
750
+ document.body.style.top = offset * -1 + 'px';
751
+ addClass(document.body, swalClasses.iosfix);
752
+ }
753
+ };
754
+
755
+ var undoIOSfix = function undoIOSfix() {
756
+ if (hasClass(document.body, swalClasses.iosfix)) {
757
+ var offset = parseInt(document.body.style.top, 10);
758
+ removeClass(document.body, swalClasses.iosfix);
759
+ document.body.style.top = '';
760
+ document.body.scrollTop = offset * -1;
761
+ }
762
+ };
763
+
764
+ // SweetAlert entry point
765
+ var sweetAlert = function sweetAlert() {
766
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
767
+ args[_key] = arguments[_key];
768
+ }
769
+
770
+ if (args[0] === undefined) {
771
+ console.error('SweetAlert2 expects at least 1 attribute!');
772
+ return false;
773
+ }
774
+
775
+ var params = _extends({}, modalParams);
776
+
777
+ switch (_typeof(args[0])) {
778
+ case 'string':
779
+ params.title = args[0];
780
+ params.html = args[1];
781
+ params.type = args[2];
782
+
783
+ break;
784
+
785
+ case 'object':
786
+ _extends(params, args[0]);
787
+ params.extraParams = args[0].extraParams;
788
+
789
+ if (params.input === 'email' && params.inputValidator === null) {
790
+ params.inputValidator = function (email) {
791
+ return new Promise(function (resolve, reject) {
792
+ var emailRegex = /^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
793
+ if (emailRegex.test(email)) {
794
+ resolve();
795
+ } else {
796
+ reject('Invalid email address');
797
+ }
798
+ });
799
+ };
800
+ }
801
+
802
+ if (params.input === 'url' && params.inputValidator === null) {
803
+ params.inputValidator = function (url) {
804
+ return new Promise(function (resolve, reject) {
805
+ // taken from https://stackoverflow.com/a/3809435/1331425
806
+ var urlRegex = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)$/;
807
+ if (urlRegex.test(url)) {
808
+ resolve();
809
+ } else {
810
+ reject('Invalid URL');
811
+ }
812
+ });
813
+ };
814
+ }
815
+ break;
816
+
817
+ default:
818
+ console.error('SweetAlert2: Unexpected type of argument! Expected "string" or "object", got ' + _typeof(args[0]));
819
+ return false;
820
+ }
821
+
822
+ setParameters(params);
823
+
824
+ var container = getContainer();
825
+ var modal = getModal();
826
+
827
+ return new Promise(function (resolve, reject) {
828
+ // Close on timer
829
+ if (params.timer) {
830
+ modal.timeout = setTimeout(function () {
831
+ sweetAlert.closeModal(params.onClose);
832
+ if (params.useRejections) {
833
+ reject('timer');
834
+ } else {
835
+ resolve({ dismiss: 'timer' });
836
+ }
837
+ }, params.timer);
838
+ }
839
+
840
+ // Get input element by specified type or, if type isn't specified, by params.input
841
+ var getInput = function getInput(inputType) {
842
+ inputType = inputType || params.input;
843
+ if (!inputType) {
844
+ return null;
845
+ }
846
+ switch (inputType) {
847
+ case 'select':
848
+ case 'textarea':
849
+ case 'file':
850
+ return getChildByClass(modal, swalClasses[inputType]);
851
+ case 'checkbox':
852
+ return modal.querySelector('.' + swalClasses.checkbox + ' input');
853
+ case 'radio':
854
+ return modal.querySelector('.' + swalClasses.radio + ' input:checked') || modal.querySelector('.' + swalClasses.radio + ' input:first-child');
855
+ case 'range':
856
+ return modal.querySelector('.' + swalClasses.range + ' input');
857
+ default:
858
+ return getChildByClass(modal, swalClasses.input);
859
+ }
860
+ };
861
+
862
+ // Get the value of the modal input
863
+ var getInputValue = function getInputValue() {
864
+ var input = getInput();
865
+ if (!input) {
866
+ return null;
867
+ }
868
+ switch (params.input) {
869
+ case 'checkbox':
870
+ return input.checked ? 1 : 0;
871
+ case 'radio':
872
+ return input.checked ? input.value : null;
873
+ case 'file':
874
+ return input.files.length ? input.files[0] : null;
875
+ default:
876
+ return params.inputAutoTrim ? input.value.trim() : input.value;
877
+ }
878
+ };
879
+
880
+ // input autofocus
881
+ if (params.input) {
882
+ setTimeout(function () {
883
+ var input = getInput();
884
+ if (input) {
885
+ focusInput(input);
886
+ }
887
+ }, 0);
888
+ }
889
+
890
+ var confirm = function confirm(value) {
891
+ if (params.showLoaderOnConfirm) {
892
+ sweetAlert.showLoading();
893
+ }
894
+
895
+ if (params.preConfirm) {
896
+ params.preConfirm(value, params.extraParams).then(function (preConfirmValue) {
897
+ sweetAlert.closeModal(params.onClose);
898
+ resolve(preConfirmValue || value);
899
+ }, function (error) {
900
+ sweetAlert.hideLoading();
901
+ if (error) {
902
+ sweetAlert.showValidationError(error);
903
+ }
904
+ });
905
+ } else {
906
+ sweetAlert.closeModal(params.onClose);
907
+ if (params.useRejections) {
908
+ resolve(value);
909
+ } else {
910
+ resolve({ value: value });
911
+ }
912
+ }
913
+ };
914
+
915
+ // Mouse interactions
916
+ var onButtonEvent = function onButtonEvent(event) {
917
+ var e = event || window.event;
918
+ var target = e.target || e.srcElement;
919
+ var confirmButton = getConfirmButton();
920
+ var cancelButton = getCancelButton();
921
+ var targetedConfirm = confirmButton && (confirmButton === target || confirmButton.contains(target));
922
+ var targetedCancel = cancelButton && (cancelButton === target || cancelButton.contains(target));
923
+
924
+ switch (e.type) {
925
+ case 'mouseover':
926
+ case 'mouseup':
927
+ if (params.buttonsStyling) {
928
+ if (targetedConfirm) {
929
+ confirmButton.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.1);
930
+ } else if (targetedCancel) {
931
+ cancelButton.style.backgroundColor = colorLuminance(params.cancelButtonColor, -0.1);
932
+ }
933
+ }
934
+ break;
935
+ case 'mouseout':
936
+ if (params.buttonsStyling) {
937
+ if (targetedConfirm) {
938
+ confirmButton.style.backgroundColor = params.confirmButtonColor;
939
+ } else if (targetedCancel) {
940
+ cancelButton.style.backgroundColor = params.cancelButtonColor;
941
+ }
942
+ }
943
+ break;
944
+ case 'mousedown':
945
+ if (params.buttonsStyling) {
946
+ if (targetedConfirm) {
947
+ confirmButton.style.backgroundColor = colorLuminance(params.confirmButtonColor, -0.2);
948
+ } else if (targetedCancel) {
949
+ cancelButton.style.backgroundColor = colorLuminance(params.cancelButtonColor, -0.2);
950
+ }
951
+ }
952
+ break;
953
+ case 'click':
954
+ // Clicked 'confirm'
955
+ if (targetedConfirm && sweetAlert.isVisible()) {
956
+ sweetAlert.disableButtons();
957
+ if (params.input) {
958
+ var inputValue = getInputValue();
959
+
960
+ if (params.inputValidator) {
961
+ sweetAlert.disableInput();
962
+ params.inputValidator(inputValue, params.extraParams).then(function () {
963
+ sweetAlert.enableButtons();
964
+ sweetAlert.enableInput();
965
+ confirm(inputValue);
966
+ }, function (error) {
967
+ sweetAlert.enableButtons();
968
+ sweetAlert.enableInput();
969
+ if (error) {
970
+ sweetAlert.showValidationError(error);
971
+ }
972
+ });
973
+ } else {
974
+ confirm(inputValue);
975
+ }
976
+ } else {
977
+ confirm(true);
978
+ }
979
+
980
+ // Clicked 'cancel'
981
+ } else if (targetedCancel && sweetAlert.isVisible()) {
982
+ sweetAlert.disableButtons();
983
+ sweetAlert.closeModal(params.onClose);
984
+ if (params.useRejections) {
985
+ reject('cancel');
986
+ } else {
987
+ resolve({ dismiss: 'cancel' });
988
+ }
989
+ }
990
+ break;
991
+ default:
992
+ }
993
+ };
994
+
995
+ var buttons = modal.querySelectorAll('button');
996
+ for (var i = 0; i < buttons.length; i++) {
997
+ buttons[i].onclick = onButtonEvent;
998
+ buttons[i].onmouseover = onButtonEvent;
999
+ buttons[i].onmouseout = onButtonEvent;
1000
+ buttons[i].onmousedown = onButtonEvent;
1001
+ }
1002
+
1003
+ // Closing modal by close button
1004
+ getCloseButton().onclick = function () {
1005
+ sweetAlert.closeModal(params.onClose);
1006
+ if (params.useRejections) {
1007
+ reject('close');
1008
+ } else {
1009
+ resolve({ dismiss: 'close' });
1010
+ }
1011
+ };
1012
+
1013
+ // Closing modal by overlay click
1014
+ container.onclick = function (e) {
1015
+ if (e.target !== container) {
1016
+ return;
1017
+ }
1018
+ if (params.allowOutsideClick) {
1019
+ sweetAlert.closeModal(params.onClose);
1020
+ if (params.useRejections) {
1021
+ reject('overlay');
1022
+ } else {
1023
+ resolve({ dismiss: 'overlay' });
1024
+ }
1025
+ }
1026
+ };
1027
+
1028
+ var buttonsWrapper = getButtonsWrapper();
1029
+ var confirmButton = getConfirmButton();
1030
+ var cancelButton = getCancelButton();
1031
+
1032
+ // Reverse buttons (Confirm on the right side)
1033
+ if (params.reverseButtons) {
1034
+ confirmButton.parentNode.insertBefore(cancelButton, confirmButton);
1035
+ } else {
1036
+ confirmButton.parentNode.insertBefore(confirmButton, cancelButton);
1037
+ }
1038
+
1039
+ // Focus handling
1040
+ var setFocus = function setFocus(index, increment) {
1041
+ var focusableElements = getFocusableElements(params.focusCancel);
1042
+ // search for visible elements and select the next possible match
1043
+ for (var _i3 = 0; _i3 < focusableElements.length; _i3++) {
1044
+ index = index + increment;
1045
+
1046
+ // rollover to first item
1047
+ if (index === focusableElements.length) {
1048
+ index = 0;
1049
+
1050
+ // go to last item
1051
+ } else if (index === -1) {
1052
+ index = focusableElements.length - 1;
1053
+ }
1054
+
1055
+ // determine if element is visible
1056
+ var el = focusableElements[index];
1057
+ if (isVisible(el)) {
1058
+ return el.focus();
1059
+ }
1060
+ }
1061
+ };
1062
+
1063
+ var handleKeyDown = function handleKeyDown(event) {
1064
+ var e = event || window.event;
1065
+ var keyCode = e.keyCode || e.which;
1066
+
1067
+ if ([9, 13, 32, 27, 37, 38, 39, 40].indexOf(keyCode) === -1) {
1068
+ // Don't do work on keys we don't care about.
1069
+ return;
1070
+ }
1071
+
1072
+ var targetElement = e.target || e.srcElement;
1073
+
1074
+ var focusableElements = getFocusableElements(params.focusCancel);
1075
+ var btnIndex = -1; // Find the button - note, this is a nodelist, not an array.
1076
+ for (var _i4 = 0; _i4 < focusableElements.length; _i4++) {
1077
+ if (targetElement === focusableElements[_i4]) {
1078
+ btnIndex = _i4;
1079
+ break;
1080
+ }
1081
+ }
1082
+
1083
+ // TAB
1084
+ if (keyCode === 9) {
1085
+ if (!e.shiftKey) {
1086
+ // Cycle to the next button
1087
+ setFocus(btnIndex, 1);
1088
+ } else {
1089
+ // Cycle to the prev button
1090
+ setFocus(btnIndex, -1);
1091
+ }
1092
+ e.stopPropagation();
1093
+ e.preventDefault();
1094
+
1095
+ // ARROWS - switch focus between buttons
1096
+ } else if (keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40) {
1097
+ // focus Cancel button if Confirm button is currently focused
1098
+ if (document.activeElement === confirmButton && isVisible(cancelButton)) {
1099
+ cancelButton.focus();
1100
+ // and vice versa
1101
+ } else if (document.activeElement === cancelButton && isVisible(confirmButton)) {
1102
+ confirmButton.focus();
1103
+ }
1104
+
1105
+ // ENTER/SPACE
1106
+ } else if (keyCode === 13 || keyCode === 32) {
1107
+ if (btnIndex === -1 && params.allowEnterKey) {
1108
+ // ENTER/SPACE clicked outside of a button.
1109
+ if (params.focusCancel) {
1110
+ fireClick(cancelButton, e);
1111
+ } else {
1112
+ fireClick(confirmButton, e);
1113
+ }
1114
+ e.stopPropagation();
1115
+ e.preventDefault();
1116
+ }
1117
+
1118
+ // ESC
1119
+ } else if (keyCode === 27 && params.allowEscapeKey === true) {
1120
+ sweetAlert.closeModal(params.onClose);
1121
+ if (params.useRejections) {
1122
+ reject('esc');
1123
+ } else {
1124
+ resolve({ dismiss: 'esc' });
1125
+ }
1126
+ }
1127
+ };
1128
+
1129
+ if (!window.onkeydown || window.onkeydown.toString() !== handleKeyDown.toString()) {
1130
+ states.previousWindowKeyDown = window.onkeydown;
1131
+ window.onkeydown = handleKeyDown;
1132
+ }
1133
+
1134
+ // Loading state
1135
+ if (params.buttonsStyling) {
1136
+ confirmButton.style.borderLeftColor = params.confirmButtonColor;
1137
+ confirmButton.style.borderRightColor = params.confirmButtonColor;
1138
+ }
1139
+
1140
+ /**
1141
+ * Show spinner instead of Confirm button and disable Cancel button
1142
+ */
1143
+ sweetAlert.hideLoading = sweetAlert.disableLoading = function () {
1144
+ if (!params.showConfirmButton) {
1145
+ hide(confirmButton);
1146
+ if (!params.showCancelButton) {
1147
+ hide(getButtonsWrapper());
1148
+ }
1149
+ }
1150
+ removeClass(buttonsWrapper, swalClasses.loading);
1151
+ removeClass(modal, swalClasses.loading);
1152
+ confirmButton.disabled = false;
1153
+ cancelButton.disabled = false;
1154
+ };
1155
+
1156
+ sweetAlert.getTitle = function () {
1157
+ return getTitle();
1158
+ };
1159
+ sweetAlert.getContent = function () {
1160
+ return getContent();
1161
+ };
1162
+ sweetAlert.getInput = function () {
1163
+ return getInput();
1164
+ };
1165
+ sweetAlert.getImage = function () {
1166
+ return getImage();
1167
+ };
1168
+ sweetAlert.getButtonsWrapper = function () {
1169
+ return getButtonsWrapper();
1170
+ };
1171
+ sweetAlert.getConfirmButton = function () {
1172
+ return getConfirmButton();
1173
+ };
1174
+ sweetAlert.getCancelButton = function () {
1175
+ return getCancelButton();
1176
+ };
1177
+
1178
+ sweetAlert.enableButtons = function () {
1179
+ confirmButton.disabled = false;
1180
+ cancelButton.disabled = false;
1181
+ };
1182
+
1183
+ sweetAlert.disableButtons = function () {
1184
+ confirmButton.disabled = true;
1185
+ cancelButton.disabled = true;
1186
+ };
1187
+
1188
+ sweetAlert.enableConfirmButton = function () {
1189
+ confirmButton.disabled = false;
1190
+ };
1191
+
1192
+ sweetAlert.disableConfirmButton = function () {
1193
+ confirmButton.disabled = true;
1194
+ };
1195
+
1196
+ sweetAlert.enableInput = function () {
1197
+ var input = getInput();
1198
+ if (!input) {
1199
+ return false;
1200
+ }
1201
+ if (input.type === 'radio') {
1202
+ var radiosContainer = input.parentNode.parentNode;
1203
+ var radios = radiosContainer.querySelectorAll('input');
1204
+ for (var _i5 = 0; _i5 < radios.length; _i5++) {
1205
+ radios[_i5].disabled = false;
1206
+ }
1207
+ } else {
1208
+ input.disabled = false;
1209
+ }
1210
+ };
1211
+
1212
+ sweetAlert.disableInput = function () {
1213
+ var input = getInput();
1214
+ if (!input) {
1215
+ return false;
1216
+ }
1217
+ if (input && input.type === 'radio') {
1218
+ var radiosContainer = input.parentNode.parentNode;
1219
+ var radios = radiosContainer.querySelectorAll('input');
1220
+ for (var _i6 = 0; _i6 < radios.length; _i6++) {
1221
+ radios[_i6].disabled = true;
1222
+ }
1223
+ } else {
1224
+ input.disabled = true;
1225
+ }
1226
+ };
1227
+
1228
+ // Set modal min-height to disable scrolling inside the modal
1229
+ sweetAlert.recalculateHeight = debounce(function () {
1230
+ var modal = getModal();
1231
+ if (!modal) {
1232
+ return;
1233
+ }
1234
+ var prevState = modal.style.display;
1235
+ modal.style.minHeight = '';
1236
+ show(modal);
1237
+ modal.style.minHeight = modal.scrollHeight + 1 + 'px';
1238
+ modal.style.display = prevState;
1239
+ }, 50);
1240
+
1241
+ // Show block with validation error
1242
+ sweetAlert.showValidationError = function (error) {
1243
+ var validationError = getValidationError();
1244
+ validationError.innerHTML = error;
1245
+ show(validationError);
1246
+
1247
+ var input = getInput();
1248
+ if (input) {
1249
+ focusInput(input);
1250
+ addClass(input, swalClasses.inputerror);
1251
+ }
1252
+ };
1253
+
1254
+ // Hide block with validation error
1255
+ sweetAlert.resetValidationError = function () {
1256
+ var validationError = getValidationError();
1257
+ hide(validationError);
1258
+ sweetAlert.recalculateHeight();
1259
+
1260
+ var input = getInput();
1261
+ if (input) {
1262
+ removeClass(input, swalClasses.inputerror);
1263
+ }
1264
+ };
1265
+
1266
+ sweetAlert.getProgressSteps = function () {
1267
+ return params.progressSteps;
1268
+ };
1269
+
1270
+ sweetAlert.setProgressSteps = function (progressSteps) {
1271
+ params.progressSteps = progressSteps;
1272
+ setParameters(params);
1273
+ };
1274
+
1275
+ sweetAlert.showProgressSteps = function () {
1276
+ show(getProgressSteps());
1277
+ };
1278
+
1279
+ sweetAlert.hideProgressSteps = function () {
1280
+ hide(getProgressSteps());
1281
+ };
1282
+
1283
+ sweetAlert.enableButtons();
1284
+ sweetAlert.hideLoading();
1285
+ sweetAlert.resetValidationError();
1286
+
1287
+ // inputs
1288
+ var inputTypes = ['input', 'file', 'range', 'select', 'radio', 'checkbox', 'textarea'];
1289
+ var input = void 0;
1290
+ for (var _i7 = 0; _i7 < inputTypes.length; _i7++) {
1291
+ var inputClass = swalClasses[inputTypes[_i7]];
1292
+ var inputContainer = getChildByClass(modal, inputClass);
1293
+ input = getInput(inputTypes[_i7]);
1294
+
1295
+ // set attributes
1296
+ if (input) {
1297
+ for (var j in input.attributes) {
1298
+ if (input.attributes.hasOwnProperty(j)) {
1299
+ var attrName = input.attributes[j].name;
1300
+ if (attrName !== 'type' && attrName !== 'value') {
1301
+ input.removeAttribute(attrName);
1302
+ }
1303
+ }
1304
+ }
1305
+ for (var attr in params.inputAttributes) {
1306
+ input.setAttribute(attr, params.inputAttributes[attr]);
1307
+ }
1308
+ }
1309
+
1310
+ // set class
1311
+ inputContainer.className = inputClass;
1312
+ if (params.inputClass) {
1313
+ addClass(inputContainer, params.inputClass);
1314
+ }
1315
+
1316
+ hide(inputContainer);
1317
+ }
1318
+
1319
+ var populateInputOptions = void 0;
1320
+ switch (params.input) {
1321
+ case 'text':
1322
+ case 'email':
1323
+ case 'password':
1324
+ case 'number':
1325
+ case 'tel':
1326
+ case 'url':
1327
+ input = getChildByClass(modal, swalClasses.input);
1328
+ input.value = params.inputValue;
1329
+ input.placeholder = params.inputPlaceholder;
1330
+ input.type = params.input;
1331
+ show(input);
1332
+ break;
1333
+ case 'file':
1334
+ input = getChildByClass(modal, swalClasses.file);
1335
+ input.placeholder = params.inputPlaceholder;
1336
+ input.type = params.input;
1337
+ show(input);
1338
+ break;
1339
+ case 'range':
1340
+ var range = getChildByClass(modal, swalClasses.range);
1341
+ var rangeInput = range.querySelector('input');
1342
+ var rangeOutput = range.querySelector('output');
1343
+ rangeInput.value = params.inputValue;
1344
+ rangeInput.type = params.input;
1345
+ rangeOutput.value = params.inputValue;
1346
+ show(range);
1347
+ break;
1348
+ case 'select':
1349
+ var select = getChildByClass(modal, swalClasses.select);
1350
+ select.innerHTML = '';
1351
+ if (params.inputPlaceholder) {
1352
+ var placeholder = document.createElement('option');
1353
+ placeholder.innerHTML = params.inputPlaceholder;
1354
+ placeholder.value = '';
1355
+ placeholder.disabled = true;
1356
+ placeholder.selected = true;
1357
+ select.appendChild(placeholder);
1358
+ }
1359
+ populateInputOptions = function populateInputOptions(inputOptions) {
1360
+ for (var optionValue in inputOptions) {
1361
+ var option = document.createElement('option');
1362
+ option.value = optionValue;
1363
+ option.innerHTML = inputOptions[optionValue];
1364
+ if (params.inputValue === optionValue) {
1365
+ option.selected = true;
1366
+ }
1367
+ select.appendChild(option);
1368
+ }
1369
+ show(select);
1370
+ select.focus();
1371
+ };
1372
+ break;
1373
+ case 'radio':
1374
+ var radio = getChildByClass(modal, swalClasses.radio);
1375
+ radio.innerHTML = '';
1376
+ populateInputOptions = function populateInputOptions(inputOptions) {
1377
+ for (var radioValue in inputOptions) {
1378
+ var radioInput = document.createElement('input');
1379
+ var radioLabel = document.createElement('label');
1380
+ var radioLabelSpan = document.createElement('span');
1381
+ radioInput.type = 'radio';
1382
+ radioInput.name = swalClasses.radio;
1383
+ radioInput.value = radioValue;
1384
+ if (params.inputValue === radioValue) {
1385
+ radioInput.checked = true;
1386
+ }
1387
+ radioLabelSpan.innerHTML = inputOptions[radioValue];
1388
+ radioLabel.appendChild(radioInput);
1389
+ radioLabel.appendChild(radioLabelSpan);
1390
+ radioLabel.for = radioInput.id;
1391
+ radio.appendChild(radioLabel);
1392
+ }
1393
+ show(radio);
1394
+ var radios = radio.querySelectorAll('input');
1395
+ if (radios.length) {
1396
+ radios[0].focus();
1397
+ }
1398
+ };
1399
+ break;
1400
+ case 'checkbox':
1401
+ var checkbox = getChildByClass(modal, swalClasses.checkbox);
1402
+ var checkboxInput = getInput('checkbox');
1403
+ checkboxInput.type = 'checkbox';
1404
+ checkboxInput.value = 1;
1405
+ checkboxInput.id = swalClasses.checkbox;
1406
+ checkboxInput.checked = Boolean(params.inputValue);
1407
+ var label = checkbox.getElementsByTagName('span');
1408
+ if (label.length) {
1409
+ checkbox.removeChild(label[0]);
1410
+ }
1411
+ label = document.createElement('span');
1412
+ label.innerHTML = params.inputPlaceholder;
1413
+ checkbox.appendChild(label);
1414
+ show(checkbox);
1415
+ break;
1416
+ case 'textarea':
1417
+ var textarea = getChildByClass(modal, swalClasses.textarea);
1418
+ textarea.value = params.inputValue;
1419
+ textarea.placeholder = params.inputPlaceholder;
1420
+ show(textarea);
1421
+ break;
1422
+ case null:
1423
+ break;
1424
+ default:
1425
+ console.error('SweetAlert2: Unexpected type of input! Expected "text", "email", "password", "number", "tel", "select", "radio", "checkbox", "textarea", "file" or "url", got "' + params.input + '"');
1426
+ break;
1427
+ }
1428
+
1429
+ if (params.input === 'select' || params.input === 'radio') {
1430
+ if (params.inputOptions instanceof Promise) {
1431
+ sweetAlert.showLoading();
1432
+ params.inputOptions.then(function (inputOptions) {
1433
+ sweetAlert.hideLoading();
1434
+ populateInputOptions(inputOptions);
1435
+ });
1436
+ } else if (_typeof(params.inputOptions) === 'object') {
1437
+ populateInputOptions(params.inputOptions);
1438
+ } else {
1439
+ console.error('SweetAlert2: Unexpected type of inputOptions! Expected object or Promise, got ' + _typeof(params.inputOptions));
1440
+ }
1441
+ }
1442
+
1443
+ openModal(params.animation, params.onOpen);
1444
+
1445
+ // Focus the first element (input or button)
1446
+ if (params.allowEnterKey) {
1447
+ setFocus(-1, 1);
1448
+ } else {
1449
+ if (document.activeElement) {
1450
+ document.activeElement.blur();
1451
+ }
1452
+ }
1453
+
1454
+ // fix scroll
1455
+ getContainer().scrollTop = 0;
1456
+
1457
+ // Observe changes inside the modal and adjust height
1458
+ if (typeof MutationObserver !== 'undefined' && !swal2Observer) {
1459
+ swal2Observer = new MutationObserver(sweetAlert.recalculateHeight);
1460
+ swal2Observer.observe(modal, { childList: true, characterData: true, subtree: true });
1461
+ }
1462
+ });
1463
+ };
1464
+
1465
+ /*
1466
+ * Global function to determine if swal2 modal is shown
1467
+ */
1468
+ sweetAlert.isVisible = function () {
1469
+ return !!getModal();
1470
+ };
1471
+
1472
+ /*
1473
+ * Global function for chaining sweetAlert modals
1474
+ */
1475
+ sweetAlert.queue = function (steps) {
1476
+ queue = steps;
1477
+ var resetQueue = function resetQueue() {
1478
+ queue = [];
1479
+ document.body.removeAttribute('data-swal2-queue-step');
1480
+ };
1481
+ var queueResult = [];
1482
+ return new Promise(function (resolve, reject) {
1483
+ (function step(i, callback) {
1484
+ if (i < queue.length) {
1485
+ document.body.setAttribute('data-swal2-queue-step', i);
1486
+
1487
+ sweetAlert(queue[i]).then(function (result) {
1488
+ queueResult.push(result);
1489
+ step(i + 1, callback);
1490
+ }, function (dismiss) {
1491
+ resetQueue();
1492
+ reject(dismiss);
1493
+ });
1494
+ } else {
1495
+ resetQueue();
1496
+ resolve(queueResult);
1497
+ }
1498
+ })(0);
1499
+ });
1500
+ };
1501
+
1502
+ /*
1503
+ * Global function for getting the index of current modal in queue
1504
+ */
1505
+ sweetAlert.getQueueStep = function () {
1506
+ return document.body.getAttribute('data-swal2-queue-step');
1507
+ };
1508
+
1509
+ /*
1510
+ * Global function for inserting a modal to the queue
1511
+ */
1512
+ sweetAlert.insertQueueStep = function (step, index) {
1513
+ if (index && index < queue.length) {
1514
+ return queue.splice(index, 0, step);
1515
+ }
1516
+ return queue.push(step);
1517
+ };
1518
+
1519
+ /*
1520
+ * Global function for deleting a modal from the queue
1521
+ */
1522
+ sweetAlert.deleteQueueStep = function (index) {
1523
+ if (typeof queue[index] !== 'undefined') {
1524
+ queue.splice(index, 1);
1525
+ }
1526
+ };
1527
+
1528
+ /*
1529
+ * Global function to close sweetAlert
1530
+ */
1531
+ sweetAlert.close = sweetAlert.closeModal = function (onComplete) {
1532
+ var container = getContainer();
1533
+ var modal = getModal();
1534
+ if (!modal) {
1535
+ return;
1536
+ }
1537
+ removeClass(modal, swalClasses.show);
1538
+ addClass(modal, swalClasses.hide);
1539
+ clearTimeout(modal.timeout);
1540
+
1541
+ resetPrevState();
1542
+
1543
+ var removeModalAndResetState = function removeModalAndResetState() {
1544
+ if (container.parentNode) {
1545
+ container.parentNode.removeChild(container);
1546
+ }
1547
+ removeClass(document.documentElement, swalClasses.shown);
1548
+ removeClass(document.body, swalClasses.shown);
1549
+ undoScrollbar();
1550
+ undoIOSfix();
1551
+ };
1552
+
1553
+ // If animation is supported, animate
1554
+ if (animationEndEvent && !hasClass(modal, swalClasses.noanimation)) {
1555
+ modal.addEventListener(animationEndEvent, function swalCloseEventFinished() {
1556
+ modal.removeEventListener(animationEndEvent, swalCloseEventFinished);
1557
+ if (hasClass(modal, swalClasses.hide)) {
1558
+ removeModalAndResetState();
1559
+ }
1560
+ });
1561
+ } else {
1562
+ // Otherwise, remove immediately
1563
+ removeModalAndResetState();
1564
+ }
1565
+ if (onComplete !== null && typeof onComplete === 'function') {
1566
+ setTimeout(function () {
1567
+ onComplete(modal);
1568
+ });
1569
+ }
1570
+ };
1571
+
1572
+ /*
1573
+ * Global function to click 'Confirm' button
1574
+ */
1575
+ sweetAlert.clickConfirm = function () {
1576
+ return getConfirmButton().click();
1577
+ };
1578
+
1579
+ /*
1580
+ * Global function to click 'Cancel' button
1581
+ */
1582
+ sweetAlert.clickCancel = function () {
1583
+ return getCancelButton().click();
1584
+ };
1585
+
1586
+ /**
1587
+ * Show spinner instead of Confirm button and disable Cancel button
1588
+ */
1589
+ sweetAlert.showLoading = sweetAlert.enableLoading = function () {
1590
+ var modal = getModal();
1591
+ if (!modal) {
1592
+ sweetAlert('');
1593
+ }
1594
+ var buttonsWrapper = getButtonsWrapper();
1595
+ var confirmButton = getConfirmButton();
1596
+ var cancelButton = getCancelButton();
1597
+
1598
+ show(buttonsWrapper);
1599
+ show(confirmButton, 'inline-block');
1600
+ addClass(buttonsWrapper, swalClasses.loading);
1601
+ addClass(modal, swalClasses.loading);
1602
+ confirmButton.disabled = true;
1603
+ cancelButton.disabled = true;
1604
+ };
1605
+
1606
+ /**
1607
+ * Set default params for each popup
1608
+ * @param {Object} userParams
1609
+ */
1610
+ sweetAlert.setDefaults = function (userParams) {
1611
+ if (!userParams || (typeof userParams === 'undefined' ? 'undefined' : _typeof(userParams)) !== 'object') {
1612
+ return console.error('SweetAlert2: the argument for setDefaults() is required and has to be a object');
1613
+ }
1614
+
1615
+ for (var param in userParams) {
1616
+ if (!defaultParams.hasOwnProperty(param) && param !== 'extraParams') {
1617
+ console.warn('SweetAlert2: Unknown parameter "' + param + '"');
1618
+ delete userParams[param];
1619
+ }
1620
+ }
1621
+
1622
+ _extends(modalParams, userParams);
1623
+ };
1624
+
1625
+ /**
1626
+ * Reset default params for each popup
1627
+ */
1628
+ sweetAlert.resetDefaults = function () {
1629
+ modalParams = _extends({}, defaultParams);
1630
+ };
1631
+
1632
+ sweetAlert.noop = function () {};
1633
+
1634
+ sweetAlert.version = '6.6.6';
1635
+
1636
+ sweetAlert.default = sweetAlert;
1637
+
1638
+ return sweetAlert;
1639
+
1640
+ })));
1641
+ if (window.Sweetalert2) window.sweetAlert = window.swal = window.Sweetalert2;
admin/boot.php CHANGED
@@ -1,110 +1,283 @@
1
  <?php
2
- /**
3
- * Admin boot
4
- * @author Webcraftic <wordpress.webraftic@gmail.com>
5
- * @copyright Webcraftic 25.05.2017
6
- * @version 1.0
7
- */
8
-
9
- // Exit if accessed directly
10
- if( !defined('ABSPATH') ) {
11
- exit;
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- /**
15
- * Widget with the offer to buy Clearfy Business
16
- *
17
- * @param array $widgets
18
- * @param string $position
19
- * @param Wbcr_Factory409_Plugin $plugin
20
- */
21
- add_filter('wbcr/factory/pages/impressive/widgets', function ($widgets, $position, $plugin) {
22
- if( $plugin->getPluginName() == WIO_Plugin::app()->getPluginName() ) {
23
- if( $position == 'right' ) {
24
- unset($widgets['donate_widget']);
25
- unset($widgets['info_widget']);
26
- //unset($widgets['businnes_suggetion']);
 
27
  }
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- return $widgets;
31
- }, 20, 3);
32
-
33
- /***
34
- * Flush configuration after saving the settings
35
- *
36
- * @param WHM_Plugin $plugin
37
- * @param Wbcr_FactoryPages410_ImpressiveThemplate $obj
38
- * @return bool
39
- */
40
- /*add_action('wbcr_factory_409_imppage_after_form_save', function ($plugin, $obj) {
41
- $is_rio = WIO_Plugin::app()->getPluginName() == $plugin->getPluginName();
42
-
43
- if( $is_rio ) {
44
- WIO_Cron::check();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
46
- }, 10, 2);*/
47
-
48
- /**
49
- * Виджет отзывов
50
- *
51
- * @param string $page_url
52
- * @param string $plugin_name
53
- * @return string
54
- */
55
- function wio_rating_widget_url($page_url, $plugin_name)
56
- {
57
- if( !defined('LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON') && ($plugin_name == WIO_Plugin::app()->getPluginName())
58
- ) {
59
- return 'https://wordpress.org/support/plugin/robin-image-optimizer/reviews/#new-post';
60
  }
 
 
 
 
61
 
62
- return $page_url;
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
- add_filter('wbcr_factory_pages_410_imppage_rating_widget_url', 'wio_rating_widget_url', 10, 2);
66
-
67
- /**
68
- * Widget with the offer to buy Clearfy Business
69
- *
70
- * @param array $widgets
71
- * @param string $position
72
- * @param Wbcr_Factory409_Plugin $plugin
73
- */
74
- function wio_donate_widget($widgets, $position, $plugin)
75
- {
76
- if( $plugin->getPluginName() == WIO_Plugin::app()->getPluginName() ) {
77
- $buy_premium_url = 'https://clearfy.pro/pricing/?utm_source=wordpress.org&utm_campaign=wbcr_robin_image_optimizer&utm_content=widget';
78
-
79
- ob_start();
80
- ?>
81
- <div id="wbcr-clr-go-to-premium-widget" class="wbcr-factory-sidebar-widget">
82
- <p>
83
- <strong><?php _e('Donation', 'clearfy'); ?></strong>
84
- </p>
85
-
86
- <div class="wbcr-clr-go-to-premium-widget-body">
87
- <p><?php _e('<b>Clearfy Business</b> is a paid package of components for the popular free WordPress plugin named Clearfy. You get access to all paid components at one price.', 'clearfy') ?></p>
88
-
89
- <p><?php _e('Paid license guarantees that you can download and update existing and future paid components of the plugin.', 'clearfy') ?></p>
90
- <a href="<?= $buy_premium_url ?>" class="wbcr-clr-purchase-premium" target="_blank" rel="noopener">
91
- <span class="btn btn-gold btn-inner-wrap">
92
- <i class="fa fa-star"></i> <?php printf(__('Upgrade to Clearfy Business for $%s', 'clearfy'), 19) ?>
93
- <i class="fa fa-star"></i>
94
- </span>
95
- </a>
96
- </div>
97
- </div>
98
- <?php
99
-
100
- $widgets['donate_widget'] = ob_get_contents();
101
- ob_end_clean();
102
- }
 
 
103
 
104
- return $widgets;
 
 
 
105
  }
106
 
107
- add_filter('wbcr/factory/pages/impressive/widgets', 'wio_donate_widget', 10, 3);
 
108
 
109
 
110
 
1
  <?php
2
+ /**
3
+ * Admin boot
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright Webcraftic 25.05.2017
7
+ * @version 1.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Проверяем таблицу в базе данных
17
+ *
18
+ * Если таблица не существует или её структура устарела, то обновляем.
19
+ * Проверка проводится при каждой инициализации плагина т.к. структура может измениться
20
+ * после очередного обновления плагина.
21
+ *
22
+ * @return bool
23
+ */
24
+ add_action( 'admin_init', function () {
25
+ RIO_Process_Queue::try_create_plugin_tables();
26
+ } );
27
 
28
+ /**
29
+ * Удаляет карточку компонента в плагине Clearfy.
30
+ *
31
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
32
+ * @since 1.3.0
33
+ */
34
+ add_filter( 'wbcr/clearfy/components/items_list', function ( $components ) {
35
+ if ( wrio_is_clearfy_license_activate() ) {
36
+ return $components;
37
+ }
38
+ if ( ! empty( $components ) ) {
39
+ foreach ( $components as $key => $component ) {
40
+ if ( "robin_image_optimizer" == $component['name'] ) {
41
+ unset( $components[ $key ] );
42
  }
43
  }
44
+ }
45
+
46
+ return $components;
47
+ } );
48
+
49
+ /**
50
+ * Добавляет карточку компонента на страницу компонентов
51
+ * в плагине Clearfy.
52
+ *
53
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
54
+ * @since 1.3.0
55
+ */
56
+ add_action( 'wbcr/clearfy/components/custom_plugins_card', function () {
57
+ if ( ! wrio_is_clearfy_license_activate() ) {
58
+ $view = WRIO_Views::get_instance( WRIO_PLUGIN_DIR );
59
+ $view->print_template( 'clearfy-component-card' );
60
+ }
61
+ } );
62
+
63
+ /**
64
+ * We asset migration scripts to all admin panel pages
65
+ *
66
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
67
+ * @since 1.3.0
68
+ */
69
+ add_action( 'admin_enqueue_scripts', function () {
70
+ if ( ! current_user_can( 'update_plugins' ) || ! wbcr_rio_has_meta_to_migrate() ) {
71
+ return;
72
+ }
73
+
74
+ wp_enqueue_script( 'wrio-meta-migrations', WRIO_PLUGIN_URL . '/admin/assets/js/meta-migrations.js', [
75
+ 'jquery',
76
+ 'wbcr-factory-clearfy-208-global'
77
+ ], WRIO_Plugin::app()->getPluginVersion() );
78
+ } );
79
+
80
+ /**
81
+ * Plugin was heavy migrated into new architecture. Specifically, post meta was moved to separate table and
82
+ * therefore it is required to migrate all of them to new table.
83
+ *
84
+ * This action prints a notice, which contains clickable link with JS onclick event, which invokes AJAX request
85
+ * to migrate these post metas to new table.
86
+ *
87
+ * Once all post meta migrated, notice would not be shown anymore.
88
+ *
89
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
90
+ * @since 1.3.0
91
+ *
92
+ * @param $notices
93
+ *
94
+ * @return array
95
+ * @see wbcr_rio_migrate_postmeta_to_process_queue() for further information about AJAX processing function.
96
+ * @see wbcr_rio_has_meta_to_migrate() used to check whether to show notice or not.
97
+ *
98
+ * @see RIO_Process_Queue for further information about new table.
99
+ */
100
+ add_action( "wbcr_factory_notices_000_list", function ( $notices ) {
101
+
102
+ if ( ! current_user_can( 'update_plugins' ) || ! wbcr_rio_has_meta_to_migrate() ) {
103
+ return $notices;
104
+ }
105
+
106
+ $notices[] = [
107
+ 'id' => WRIO_Plugin::app()->getPrefix() . 'meta_to_migration',
108
+ 'type' => 'warning',
109
+ 'dismissible' => false,
110
+ 'dismiss_expires' => 0,
111
+ 'text' => "<p><b>" . WRIO_Plugin::app()->getPluginTitle() . ":</b> " . wrio_get_meta_migration_notice_text() . '</p>'
112
+ ];
113
+
114
+ return $notices;
115
+ } );
116
+
117
+ /**
118
+ * Plugin was heavy migrated into new architecture. Specifically, post meta was moved to separate table and
119
+ * therefore it is required to migrate all of them to new table.
120
+ *
121
+ * This action prints a notice, which contains clickable link with JS onclick event, which invokes AJAX request
122
+ * to migrate these post metas to new table.
123
+ *
124
+ * Once all post meta migrated, notice would not be shown anymore.
125
+ *
126
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
127
+ * @since 1.3.0
128
+ *
129
+ * @param Wbcr_Factory412_Plugin $plugin
130
+ * @param Wbcr_FactoryPages412_ImpressiveThemplate $obj
131
+ *
132
+ * @see wbcr_rio_migrate_postmeta_to_process_queue() for further information about AJAX processing function.
133
+ * @see wbcr_rio_has_meta_to_migrate() used to check whether to show notice or not.
134
+ *
135
+ * @see RIO_Process_Queue for further information about new table.
136
+ */
137
+ add_action( 'wbcr/factory/pages/impressive/print_all_notices', function ( $plugin, $obj ) {
138
+ if ( ( $plugin->getPluginName() != WRIO_Plugin::app()->getPluginName() ) || ! wbcr_rio_has_meta_to_migrate() ) {
139
+ return;
140
+ }
141
+
142
+ $obj->printWarningNotice( wrio_get_meta_migration_notice_text() );
143
+ }, 10, 2 );
144
+
145
+ /***
146
+ * Flush configuration after saving the settings
147
+ *
148
+ * @param WHM_Plugin $plugin
149
+ * @param Wbcr_FactoryPages412_ImpressiveThemplate $obj
150
+ *
151
+ * @return bool
152
+ */
153
+ /*add_action('wbcr_factory_412_imppage_after_form_save', function ($plugin, $obj) {
154
+ $is_rio = WRIO_Plugin::app()->getPluginName() == $plugin->getPluginName();
155
+
156
+ if( $is_rio ) {
157
+ WRIO_Cron::check();
158
+ }
159
+ }, 10, 2);*/
160
 
161
+ /**
162
+ * Виджет отзывов
163
+ *
164
+ * @param string $page_url
165
+ * @param string $plugin_name
166
+ *
167
+ * @return string
168
+ */
169
+ function wio_rating_widget_url( $page_url, $plugin_name ) {
170
+ if ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) {
171
+ return 'https://wordpress.org/support/plugin/robin-image-optimizer/reviews/#new-post';
172
+ }
173
+
174
+ return $page_url;
175
+ }
176
+
177
+ add_filter( 'wbcr_factory_pages_412_imppage_rating_widget_url', 'wio_rating_widget_url', 10, 2 );
178
+
179
+ /**
180
+ * Widget with the offer to buy Clearfy Business
181
+ *
182
+ * @param array $widgets
183
+ * @param string $position
184
+ * @param Wbcr_Factory412_Plugin $plugin
185
+ */
186
+ add_filter( 'wbcr/factory/pages/impressive/widgets', function ( $widgets, $position, $plugin ) {
187
+ if ( $plugin->getPluginName() == WRIO_Plugin::app()->getPluginName() ) {
188
+ require_once WRIO_PLUGIN_DIR . '/admin/includes/sidebar-widgets.php';
189
+
190
+ if ( wrio_is_license_activate() ) {
191
+ unset( $widgets['donate_widget'] );
192
+
193
+ if ( $position == 'right' ) {
194
+ unset( $widgets['businnes_suggetion'] );
195
+ unset( $widgets['rating_widget'] );
196
+ unset( $widgets['info_widget'] );
197
+ }
198
+
199
+ if ( $position == 'bottom' ) {
200
+ $widgets['support'] = wrio_get_sidebar_support_widget();
201
+ }
202
+
203
+ return $widgets;
204
+ } else {
205
+ if ( $position == 'right' ) {
206
+ unset( $widgets['info_widget'] );
207
+ unset( $widgets['rating_widget'] );
208
+ $widgets['support'] = wrio_get_sidebar_support_widget();
209
+ }
210
  }
211
+
212
+ if ( $position == 'bottom' ) {
213
+ $widgets['donate_widget'] = wrio_get_sidebar_premium_widget();
 
 
 
 
 
 
 
 
 
 
 
214
  }
215
+ }
216
+
217
+ return $widgets;
218
+ }, 20, 3 );
219
 
220
+ /**
221
+ * Заменяет заголовок в рекламном виджете
222
+ *
223
+ * @param array $features
224
+ * @param string $plugin_name
225
+ * @param string $page_id
226
+ */
227
+ add_filter( 'wbcr/clearfy/pages/suggetion_title', function ( $features, $plugin_name, $page_id ) {
228
+ if ( ! empty( $plugin_name ) && ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) ) {
229
+ return __( "ROBIN IMAGE OPTIMIZER PRO", 'robin-image-optimizer' );
230
  }
231
 
232
+ return $features;
233
+ }, 20, 3 );
234
+
235
+ /**
236
+ * Заменяем премиум возможности в рекламном виджете
237
+ *
238
+ * @param array $features
239
+ * @param string $plugin_name
240
+ * @param string $page_id
241
+ */
242
+ add_filter( 'wbcr/clearfy/pages/suggetion_features', function ( $features, $plugin_name, $page_id ) {
243
+ if ( ! empty( $plugin_name ) && ( $plugin_name == WRIO_Plugin::app()->getPluginName() ) ) {
244
+ $upgrade_feature = [];
245
+ $upgrade_feature[] = __( 'Automatic convertation in Webp', 'robin-image-optimizer' );
246
+ $upgrade_feature[] = __( 'You can optimize custom folders', 'robin-image-optimizer' );
247
+ $upgrade_feature[] = __( 'Support Nextgen gallery', 'robin-image-optimizer' );
248
+ $upgrade_feature[] = __( 'Multisite support', 'robin-image-optimizer' );
249
+ $upgrade_feature[] = __( 'Fast optimization servers', 'robin-image-optimizer' );
250
+ $upgrade_feature[] = __( 'No ads', 'robin-image-optimizer' );
251
+ $upgrade_feature[] = __( 'Best support', 'robin-image-optimizer' );
252
+
253
+ return $upgrade_feature;
254
+ }
255
+
256
+ return $features;
257
+ }, 20, 3 );
258
+
259
+ /**
260
+ * Заменяем премиум возможности в рекламном виджете
261
+ *
262
+ * @param array $messages
263
+ * @param string $type
264
+ * @param string $plugin_name
265
+ */
266
+ add_filter( 'wbcr/factory/premium/notice_text', function ( $text, $type, $plugin_name ) {
267
+ if ( WRIO_Plugin::app()->getPluginName() != $plugin_name ) {
268
+ return $text;
269
+ }
270
+
271
+ $license_page_url = WRIO_Plugin::app()->getPluginPageUrl( 'rio_license' );
272
 
273
+ if ( 'need_activate_license' == $type ) {
274
+ return sprintf( __( '<a href="%s">License activation</a> required. A license is required to get premium plugin updates, as well as to get additional services.', 'robin-image-optimizer' ), $license_page_url );
275
+ } else if ( 'need_renew_license' == $type ) {
276
+ return sprintf( __( 'Your <a href="%s">license</a> has expired. You can no longer get premium plugin updates, premium support and your access to Webcraftic services has been suspended.', 'robin-image-optimizer' ), $license_page_url );
277
  }
278
 
279
+ return $text;
280
+ }, 10, 3 );
281
 
282
 
283
 
admin/includes/classes/class-rio-nextgen-landing.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ // Exit if accessed directly
5
+ if ( ! defined( 'ABSPATH' ) ) {
6
+ exit;
7
+ }
8
+
9
+ /**
10
+ * Класс используется для вывода страницы лендинга
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_NextgenLanding {
16
+
17
+ /**
18
+ * Инициализация лендинга
19
+ */
20
+ public function __construct() {
21
+ add_action( 'admin_menu', array( $this, 'removeSubMenu' ), 99999 );
22
+ add_action( 'admin_menu', array( $this, 'addSubMenu' ), 20 );
23
+ }
24
+
25
+ /**
26
+ * Удаляет лендинг nextgen
27
+ */
28
+ public function removeSubMenu() {
29
+ remove_submenu_page( 'nextgen-gallery', 'ngg_imagify' );
30
+ }
31
+
32
+ /**
33
+ * Добавляем свою страницу в меню Тextgen
34
+ */
35
+ public function addSubMenu() {
36
+ add_submenu_page(
37
+ 'nextgen-gallery',
38
+ __( 'Image optimizer', 'robin-image-optimizer' ),
39
+ __( 'Image optimizer', 'robin-image-optimizer' ),
40
+ 'manage_options',
41
+ 'ngg_robin', // если взять старый слаг ngg_imagify, то на странице выведет оба лендинга
42
+ array( $this, 'nngLandingPage' )
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Контент лендинга
48
+ */
49
+ public function nngLandingPage() {
50
+ // если активна премиум версия - делаем редирект на страницу статистики nextgen
51
+ if ( defined( 'WRIOP_PLUGIN_ACTIVE' ) && WRIOP_PLUGIN_ACTIVE ) {
52
+ wp_redirect( admin_url( 'admin.php?page=io_nextgen_gallery_statistic-wbcr_image_optimizer' ) );
53
+ die();
54
+ }
55
+ ?>
56
+ рекламма установки премиум аддона
57
+ <?php
58
+ }
59
+ }
60
+
61
+ new WIO_NextgenLanding;
admin/includes/classes/class-rio-optimize-template.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Класс реализует шаблон блока статистики
9
+ *
10
+ * @author Eugene Jokerov <jokerov@gmail.com>
11
+ * @copyright (c) 2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WIO_OptimizePageTemplate {
15
+
16
+ /**
17
+ * Тип страницы
18
+ */
19
+ protected $page_type = 'media-library';
20
+
21
+
22
+ public function __construct( $type = 'media-library' ) {
23
+ $this->page_type = $type;
24
+ }
25
+
26
+ /**
27
+ * Выводит контент страницы с учётом мультисайта
28
+ *
29
+ * @param Wbcr_FactoryClearfy208_PageBase $page
30
+ *
31
+ * @throws Exception
32
+ */
33
+
34
+ /*public function showPageContent( Wbcr_FactoryClearfy208_PageBase $page ) {
35
+ do_action( 'wbcr/rio/multisite_current_blog' );
36
+ $this->pageContent( $page );
37
+ do_action( 'wbcr/rio/multisite_restore_blog' );
38
+ }*/
39
+
40
+ /**
41
+ * Выбор сайта для мультисайт режима
42
+ *
43
+ */
44
+ /*public function selectSite() {
45
+ if ( ! WRIO_Plugin::app()->isNetworkAdmin() ) {
46
+ return;
47
+ }
48
+ $blogs = WIO_Multisite::getBlogs( $this->page_type );
49
+ $current_blog = WRIO_Plugin::app()->getPopulateOption( 'current_blog', 1 );
50
+ ?>
51
+ <select style="width:200px;display:inline-block; height: 45px; margin-left:40px;" id="wbcr-rio-current-blog"
52
+ class="factory-dropdown factory-from-control-dropdown form-control"
53
+ data-context="<?php echo esc_attr( $this->page_type ); ?>"
54
+ data-nonce="<?php echo wp_create_nonce( 'update_blog_id' ); ?>">
55
+ <?php foreach ( $blogs as $blog ) : ?>
56
+ <?php
57
+ $blog_name = $blog->domain . $blog->path;
58
+ if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
59
+ $blog_name = $blog->domain;
60
+ }
61
+ ?>
62
+ <option <?php selected( $current_blog, $blog->blog_id ); ?>
63
+ value="<?php echo esc_attr( $blog->blog_id ); ?>"><?php echo esc_attr( $blog_name ); ?></option>
64
+ <?php endforeach; ?>
65
+ </select>
66
+ <?php
67
+ }*/
68
+
69
+ /**
70
+ * Возвращает html код блока ручной оптимизации
71
+ *
72
+ * @param array $params {
73
+ * Параметры
74
+ *
75
+ * @type int $attachment_id Attachment post ID
76
+ * @type bool $is_optimized Оптимизировано ли изображение
77
+ * @type string $attach_dimensions Размеры изображения. Например 200x150
78
+ * @type int $attachment_file_size Размер оригинального основного файла в байтах
79
+ * @type bool $is_skipped Пропущено ли изображение. Изображения с таким флагом больше не участвуют в
80
+ * оптимизации
81
+ * @type int $optimized_size Оптимизированный размер основного файла + превьюшек в байтах
82
+ * @type int $original_size Оригинальный размер основного файла + превьюшек в байтах
83
+ * @type int $original_main_size Оригинальный размер основного файла в байтах
84
+ * @type int $thumbnails_optimized Кол-во оптимизированных превьюшек
85
+ * @type string $optimization_level Уровень оптимизации
86
+ * @type string $error_msg Текст ошибки
87
+ * @type bool $backuped Сделана ли резервная копия
88
+ * @type float $diff_percent Разница между оригиналом и оптимизацией в процентах
89
+ * @type float $diff_percent_all Общая оптимизация в процентах
90
+ * }
91
+ *
92
+ * @return string
93
+ */
94
+ public function getMediaColumnTemplate( $params ) {
95
+ ob_start();
96
+
97
+ $ajaxActionOptimize = apply_filters( 'wbcr/rio/optimize_template/reoptimize_ajax_action', 'wio_reoptimize_image', $this->page_type );
98
+ $ajaxActionRestore = apply_filters( 'wbcr/rio/optimize_template/restore_ajax_action', 'wio_restore_image', $this->page_type );
99
+
100
+ $attachment_id = $params['attachment_id'];
101
+ $is_optimized = $params['is_optimized'];
102
+ $attach_dimensions = $params['attach_dimensions'];
103
+ $attachment_file_size = $params['attachment_file_size'];
104
+ $is_skipped = $params['is_skipped'];
105
+
106
+ if ( $is_skipped ) {
107
+ return ob_get_clean();
108
+ }
109
+
110
+ if ( $is_optimized ) {
111
+ $original_main_size = $params['original_main_size'];
112
+ $thumbnails_optimized = $params['thumbnails_optimized'];
113
+ $optimization_level = $params['optimization_level'];
114
+ $error_msg = $params['error_msg'];
115
+ $backuped = $params['backuped'];
116
+ $diff_percent = $params['diff_percent'];
117
+ $diff_percent_all = $params['diff_percent_all'];
118
+
119
+ ?>
120
+ <ul class="wio-datas-list" data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
121
+ data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>">
122
+ <li class="wio-data-item"><span
123
+ class="data"><?php _e( 'New Filesize:', 'robin-image-optimizer' ); ?></span>
124
+ <strong class="big"><?php echo esc_attr( size_format( $attachment_file_size ) ); ?></strong></li>
125
+ <li class="wio-data-item">
126
+ <span class="data"><?php _e( 'Original Saving:', 'robin-image-optimizer' ); ?></span>
127
+ <strong>
128
+ <span class="wio-chart-value"><?php echo esc_attr( $diff_percent ); ?></span>%
129
+ </strong>
130
+ </li>
131
+ <li class="wio-data-item">
132
+ <span class="data"><?php _e( 'Original Filesize:', 'robin-image-optimizer' ); ?></span>
133
+ <strong class="original"><?php echo esc_attr( size_format( $original_main_size ) ); ?></strong>
134
+ </li>
135
+ <li class="wio-data-item"><span class="data"><?php _e( 'Level:', 'robin-image-optimizer' ); ?></span>
136
+ <strong>
137
+ <?php
138
+ if ( ! $error_msg ) {
139
+ // если уровень кастомный от 1 до 100
140
+ if ( is_numeric( $optimization_level ) ) {
141
+ echo __( 'Custom', 'robin-image-optimizer' ) . ' ' . intval( $optimization_level ) . '%';
142
+ } else {
143
+ // если уровень один из настроек
144
+ if ( $optimization_level == 'normal' ) {
145
+ echo __( 'lossless', 'robin-image-optimizer' );
146
+ } else if ( $optimization_level == 'aggresive' ) {
147
+ echo __( 'lossy', 'robin-image-optimizer' );
148
+ } else {
149
+ echo __( 'High', 'robin-image-optimizer' );
150
+ }
151
+ }
152
+ }
153
+ ?>
154
+ </strong>
155
+ </li>
156
+ <li class="wio-data-item">
157
+ <span class="data"><?php _e( 'Thumbnails Optimized:', 'robin-image-optimizer' ); ?></span>
158
+ <strong class="original"><?php echo intval( $thumbnails_optimized ); ?></strong>
159
+ </li>
160
+ <li class="wio-data-item">
161
+ <span class="data"><?php _e( 'Overall Saving:', 'robin-image-optimizer' ); ?></span>
162
+ <strong class="original"><?php echo esc_attr( $diff_percent_all ); ?>%</strong>
163
+ </li>
164
+ <?php if ( $error_msg ) : ?>
165
+ <li class="wio-data-item">
166
+ <span class="data"><?php _e( 'Error Message:', 'robin-image-optimizer' ); ?></span>
167
+ <strong><?php echo esc_attr( $error_msg ); ?></strong></li>
168
+ <?php endif; ?>
169
+ </ul>
170
+ <div class="wio-datas-actions-links" style="display:inline;">
171
+ <?php if ( $optimization_level != 'normal' ) : ?>
172
+ <a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
173
+ data-id="<?php echo esc_attr( $attachment_id ); ?>" data-level="normal" href="#"
174
+ class="wio-reoptimize button-wio-manual-override-upload"
175
+ data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
176
+ <span class="dashicons dashicons-admin-generic"></span><span
177
+ class="wio-hide-if-small"><?php _e( 'Re-Optimize to', 'robin-image-optimizer' ); ?> </span><?php _e( 'Normal', 'robin-image-optimizer' ); ?>
178
+ <span class="wio-hide-if-small"></span>
179
+ </a>
180
+ <?php endif; ?>
181
+ <?php if ( $optimization_level != 'aggresive' ) : ?>
182
+ <a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
183
+ data-id="<?php echo esc_attr( $attachment_id ); ?>" data-level="aggresive" href="#"
184
+ class="wio-reoptimize button-wio-manual-override-upload"
185
+ data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
186
+ <span class="dashicons dashicons-admin-generic"></span><span
187
+ class="wio-hide-if-small"><?php _e( 'Re-Optimize to', 'robin-image-optimizer' ); ?> </span><?php _e( 'Medium', 'robin-image-optimizer' ); ?>
188
+ <span class="wio-hide-if-small"></span>
189
+ </a>
190
+ <?php endif; ?>
191
+ <?php if ( $optimization_level != 'ultra' ) : ?>
192
+ <a data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
193
+ data-id="<?php echo esc_attr( $attachment_id ); ?>" data-level="ultra" href="#"
194
+ class="wio-reoptimize button-wio-manual-override-upload"
195
+ data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>">
196
+ <span class="dashicons dashicons-admin-generic"></span><span
197
+ class="wio-hide-if-small"><?php _e( 'Re-Optimize to', 'robin-image-optimizer' ); ?> </span><?php _e( 'High', 'robin-image-optimizer' ); ?>
198
+ <span class="wio-hide-if-small"></span>
199
+ </a>
200
+ <?php endif; ?>
201
+ <?php if ( $backuped ) : ?>
202
+ <a href="#" data-action="<?php echo esc_attr( $ajaxActionRestore ); ?>"
203
+ data-id="<?php echo esc_attr( $attachment_id ); ?>"
204
+ class="button-wio-restore attachment-has-backup"
205
+ data-waiting-label="<?php _e( 'Recovery in progress', 'robin-image-optimizer' ); ?>"><span
206
+ class="dashicons dashicons-image-rotate"></span><?php _e( 'Restore original', 'robin-image-optimizer' ); ?>
207
+ </a>
208
+ <?php endif; ?>
209
+ </div>
210
+ <!-- .wio-datas-actions-links -->
211
+ <?php
212
+ } else {
213
+ ?>
214
+ <button type="button" data-action="<?php echo esc_attr( $ajaxActionOptimize ); ?>"
215
+ data-id="<?php echo esc_attr( $attachment_id ); ?>" data-level=""
216
+ class="wio-reoptimize button button-primary button-large"
217
+ data-waiting-label="<?php _e( 'Optimization in progress', 'robin-image-optimizer' ); ?>"
218
+ data-size="<?php echo esc_attr( size_format( $attachment_file_size ) ); ?>"
219
+ data-dimensions="<?php echo esc_attr( $attach_dimensions ); ?>"><?php _e( 'Optimize', 'robin-image-optimizer' ); ?></button>
220
+ <?php
221
+ }
222
+
223
+ return ob_get_clean();
224
+ }
225
+
226
+ }
admin/includes/classes/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/includes/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/includes/sidebar-widgets.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Sidebar widgets
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 01.12.2018, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+ /**
11
+ * Return premium widget markup
12
+ *
13
+ * @return string
14
+ */
15
+ function wrio_get_sidebar_premium_widget() {
16
+
17
+ $purchase_url = WRIO_Plugin::app()->get_support()->get_pricing_url( true, 'license_page' );
18
+ $upgrade_price = WRIO_Plugin::app()->premium->get_price();
19
+
20
+ ob_start();
21
+ ?>
22
+ <div id="wbcr-clr-go-to-premium-widget" class="wbcr-factory-sidebar-widget">
23
+ <p>
24
+ <strong><?php _e( 'Activate Robin image optimizer pro', 'robin-image-optimizer' ); ?></strong>
25
+ </p>
26
+ <div class="wbcr-clr-go-to-premium-widget-body">
27
+ <p><?php _e( '<b>Clearfy Business</b> is a paid package of components for the popular free WordPress plugin named Clearfy. You get access to all paid components at one price.', 'wrio-image-optimizer' ) ?></p>
28
+ <p><?php _e( 'Paid license guarantees that you can download and update existing and future paid components of the plugin.', 'robin-image-optimizer' ) ?></p>
29
+ <a href="<?= $purchase_url ?>" class="wbcr-clr-purchase-premium" target="_blank" rel="noopener">
30
+ <span class="btn btn-gold btn-inner-wrap">
31
+ <?php printf( __( 'Upgrade to Clearfy Business for $%s', 'robin-image-optimizer' ), $upgrade_price ) ?>
32
+ </span>
33
+ </a>
34
+ </div>
35
+ </div>
36
+ <?php
37
+
38
+ $output = ob_get_contents();
39
+
40
+ ob_end_clean();
41
+
42
+ return $output;
43
+ }
44
+
45
+ /**
46
+ * Return support widget markup
47
+ *
48
+ * @return string
49
+ */
50
+ function wrio_get_sidebar_support_widget() {
51
+
52
+ //$free_support_url = WRIO_Plugin::app()->get_support()->get_contacts_url( true, 'support_widget' );
53
+ //$hot_support_url = WRIO_Plugin::app()->get_support()->get_tracking_page_url( 'hot-support', 'support_widget' );
54
+
55
+ $support_url = "https://webcraftic.com/support/";
56
+
57
+ ob_start();
58
+ ?>
59
+ <div id="wbcr-clr-support-widget" class="wbcr-factory-sidebar-widget">
60
+ <p><strong><?php _e( 'Having Issues?', 'robin-image-optimizer' ); ?></strong></p>
61
+ <div class="wbcr-clr-support-widget-body">
62
+ <p>
63
+ <?php _e( 'We provide free support for this plugin. If you are pushed with a problem, just create a new ticket. We will definitely help you!', 'robin-image-optimizer' ); ?>
64
+ </p>
65
+ <ul>
66
+ <li><span class="dashicons dashicons-sos"></span>
67
+ <a href="<?= $support_url ?>" target="_blank" rel="noopener"><?php _e( 'Get starting free support', 'robin-image-optimizer' ); ?></a>
68
+ </li>
69
+ <li style="margin-top: 15px;background: #fff4f1;padding: 10px;color: #a58074;">
70
+ <span class="dashicons dashicons-warning"></span>
71
+ <?php printf( __( 'If you find a php error or a vulnerability in plugin, you can <a href="%s" target="_blank" rel="noopener">create ticket</a> in hot support that we responded instantly.', 'wrio-image-optimizer' ), $support_url ); ?>
72
+ </li>
73
+ </ul>
74
+ </div>
75
+ </div>
76
+ <?php
77
+
78
+ $output = ob_get_contents();
79
+
80
+ ob_end_clean();
81
+
82
+ return $output;
83
+ }
admin/index.php CHANGED
@@ -1 +1,2 @@
1
  <?php
 
1
  <?php
2
+ // silence is golden
admin/pages/class-rio-license.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Class WRIOP_License
9
+ *
10
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
11
+ */
12
+ class WRIO_License_Page extends Wbcr_FactoryClearfy208_LicensePage {
13
+
14
+ /**
15
+ * {@inheritdoc}
16
+ */
17
+ public $id = 'rio_license';
18
+
19
+ /**
20
+ * {@inheritdoc}
21
+ */
22
+ public $page_parent_page = null;
23
+
24
+ /**
25
+ * {@inheritdoc}
26
+ */
27
+ public $available_for_multisite = true;
28
+
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public $clearfy_collaboration = false;
33
+
34
+ /**
35
+ * {@inheritdoc}
36
+ */
37
+ public $show_right_sidebar_in_options = true;
38
+
39
+ /**
40
+ * {@inheritdoc}
41
+ */
42
+ public $page_menu_position = 0;
43
+
44
+ /**
45
+ * {@inheritdoc}
46
+ * @param Wbcr_Factory412_Plugin $plugin
47
+ */
48
+ public function __construct( Wbcr_Factory412_Plugin $plugin ) {
49
+ $this->menu_title = __( 'License', 'robin-image-optimizer' );
50
+ $this->page_menu_short_description = __( 'Product activation', 'robin-image-optimizer' );
51
+
52
+ $this->plan_name = __( 'Robin image optimizer Premium', 'robin-image-optimizer' );
53
+
54
+ if ( is_multisite() && defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
55
+ $clearfy_is_active_for_network = is_plugin_active_for_network( Wbcr_FactoryClearfy_Compatibility::getClearfyBasePath() );
56
+
57
+ if ( WRIO_Plugin::app()->isNetworkActive() && $clearfy_is_active_for_network ) {
58
+ $this->clearfy_collaboration = true;
59
+ }
60
+ } else if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
61
+ $this->clearfy_collaboration = true;
62
+ }
63
+
64
+ parent::__construct( $plugin );
65
+ }
66
+
67
+ /**
68
+ * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
69
+ * Меню текущего плагина будет добавлено в общее меню Clearfy
70
+ *
71
+ * @return string
72
+ */
73
+ public function getMenuScope() {
74
+ if ( $this->clearfy_collaboration ) {
75
+ //$this->page_parent_page = 'rio_general';
76
+ $this->page_parent_page = 'none';
77
+
78
+ return 'wbcr_clearfy';
79
+ }
80
+
81
+ return $this->plugin->getPluginName();
82
+ }
83
+
84
+ public function get_plan_description() {
85
+ //$paragraf1 = sprintf( __( '<b>%s</b> is a premium image optimization plugin for WordPress. ', 'robin-image-optimizer' ), $this->plan_name ) . '</p>';
86
+ //$paragraf2 = '<p style="font-size: 16px;">' . __( 'Paid license guarantees that you can optimize images under better conditions and WebP support.', 'robin-image-optimizer' );
87
+ //return '<p style="font-size: 16px;">' . $paragraf1 . '</p><p style="font-size: 16px;">' . $paragraf2 . '</p>';
88
+
89
+ //return '<p style="font-size: 16px;">' . $paragraf1 . '</p>';
90
+ }
91
+ }
admin/pages/class-rio-log.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Класс отвечает за работу страницы логов.
9
+ *
10
+ * @author Eugene Jokerov <jokerov@gmail.com>
11
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_LogPage extends WRIO_Page {
16
+
17
+ /**
18
+ * {@inheritdoc}
19
+ */
20
+ public $id = 'rio_logs'; // Уникальный идентификатор страницы
21
+
22
+ /**
23
+ * {@inheritdoc}
24
+ */
25
+ public $page_menu_dashicon = 'dashicons-admin-tools';
26
+
27
+ /**
28
+ * {@inheritdoc}
29
+ */
30
+ public $type = 'page';
31
+
32
+ /**
33
+ * @param WRIO_Plugin $plugin
34
+ */
35
+ public function __construct( WRIO_Plugin $plugin ) {
36
+
37
+ $this->menu_title = __( 'Error Log', 'robin-image-optimizer' );
38
+ $this->page_menu_short_description = __( 'Plugin debug report', 'robin-image-optimizer' );
39
+
40
+ parent::__construct( $plugin );
41
+ }
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ *
46
+ * @since 1.0.0
47
+ * @return void
48
+ */
49
+ public function assets( $scripts, $styles ) {
50
+ parent::assets( $scripts, $styles );
51
+
52
+ // Add Clearfy styles for HMWP pages
53
+ if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
54
+ $this->styles->add( WCL_PLUGIN_URL . '/admin/assets/css/general.css' );
55
+ }
56
+ }
57
+
58
+ /**
59
+ * {@inheritdoc}
60
+ */
61
+ public function getMenuTitle() {
62
+ return defined( 'LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON' ) ? __( 'Image optimizer', 'robin-image-optimizer' ) : __( 'Error Log', 'robin-image-optimizer' );
63
+ }
64
+
65
+ /**
66
+ * {@inheritdoc}
67
+ */
68
+ public function showPageContent() {
69
+ ?>
70
+ <div class="wbcr-factory-page-group-header" style="margin-top:0;">
71
+ <strong><?php _e( 'Error Log', 'robin-image-optimizer' ) ?></strong>
72
+ <p>
73
+ <?php _e( 'In this section, you can track image optimization errors. Sending this log to us, will help in solving possible optimization issues.', 'robin-image-optimizer' ) ?>
74
+ </p>
75
+ </div>
76
+ <div class="wbcr-factory-page-group-body" style="padding:20px">
77
+ <div class="btn-group">
78
+ <a href="<?php echo wp_nonce_url( $this->getPageUrl() . 'action=export' ) ?>"
79
+ class="btn btn-default"><?php _e( 'Export Debug Information', 'robin-image-optimizer' ) ?></a>
80
+ <a href="#"
81
+ onclick="wrioLogCleanup(this);return false;"
82
+ data-working="<?php echo esc_attr__( 'Working...', 'robin-image-optimizer' ) ?>"
83
+ class="btn btn-default"><?php echo sprintf( __( 'Clean-up Logs (<span id="wbcr-log-size">%s</span>)', 'robin-image-optimizer' ), $this->get_log_size_formatted() ) ?></a>
84
+ </div>
85
+ <script>
86
+ function wrioLogCleanup(element) {
87
+
88
+ var btn = jQuery(element),
89
+ currentBtnText = btn.html();
90
+
91
+ console.log(btn.data('working'), btn);
92
+
93
+ btn.text(btn.data('working'));
94
+
95
+ jQuery.ajax({
96
+ url: ajaxurl,
97
+ method: 'post',
98
+ data: {
99
+ action: 'wrio_logs_cleanup',
100
+ nonce: '<?php echo wp_create_nonce( 'wrio_clean_logs' ) ?>'
101
+ },
102
+ success: function(data) {
103
+ btn.html(currentBtnText);
104
+
105
+ jQuery('#wbcr-log-viewer').html('');
106
+ jQuery('#wbcr-log-size').text('0B');
107
+ jQuery.wbcr_factory_clearfy_208.app.showNotice(data.message, data.type);
108
+ },
109
+ error: function(jqXHR, textStatus, errorThrown) {
110
+ jQuery.wbcr_factory_clearfy_208.app.showNotice('Error: ' + errorThrown + ', status: ' + textStatus, 'danger');
111
+ btn.html(currentBtnText);
112
+ }
113
+ });
114
+ }
115
+ </script>
116
+ <style>
117
+ .wbcr-log-viewer {
118
+ width: 100%;
119
+ height: 650px;
120
+ font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
121
+ font-size: 12px;
122
+ word-break: break-all;
123
+ word-wrap: break-word;
124
+ overflow: auto;
125
+ -ms-overflow-style: scrollbar;
126
+ background-color: #e9e9e9;
127
+ padding: 8px;
128
+ border: 1px solid #cfcfcf;
129
+ }
130
+ </style>
131
+ <div class="wbcr-log-viewer" id="wbcr-log-viewer">
132
+ <?php echo WIO_Log_Reader::prettify() ?>
133
+ </div>
134
+ </div>
135
+ <?php
136
+ }
137
+
138
+ /**
139
+ * Processing log export action in form of ZIP archive.
140
+ */
141
+ public function exportAction() {
142
+ $export = new WIO_Log_Export();
143
+
144
+ if ( $export->prepare() ) {
145
+ $export->download( true );
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get log size formatted.
151
+ *
152
+ * @return false|string
153
+ */
154
+ private function get_log_size_formatted() {
155
+
156
+ try {
157
+ return size_format( WRIO_Logger::get_total_size() );
158
+ } catch( \Exception $exception ) {
159
+ WRIO_Logger::error( sprintf( 'Failed to get total log size as exception was thrown: %s', $exception->getMessage() ) );
160
+ }
161
+
162
+ return '';
163
+ }
164
+ }
admin/pages/class-rio-page.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The page Settings.
5
+ *
6
+ * @since 1.0.0
7
+ */
8
+
9
+ // Exit if accessed directly
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Класс отвечает за работу страницы настроек
16
+ *
17
+ * @author Eugene Jokerov <jokerov@gmail.com>
18
+ * @copyright (c) 2018, Webcraftic
19
+ * @version 1.0
20
+ */
21
+ class WRIO_Page extends Wbcr_FactoryClearfy208_PageBase {
22
+
23
+ /**
24
+ * {@inheritdoc}
25
+ */
26
+ public $page_parent_page = null;
27
+
28
+ /**
29
+ * {@inheritdoc}
30
+ */
31
+ public $available_for_multisite = false;
32
+
33
+ /**
34
+ * {@inheritdoc}
35
+ */
36
+ public $clearfy_collaboration = false;
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ public $show_right_sidebar_in_options = true;
42
+
43
+
44
+ /**
45
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
46
+ * @since 1.3.0
47
+ * @var WRIO_Views
48
+ */
49
+ protected $view;
50
+
51
+ /**
52
+ * @param WRIO_Plugin $plugin
53
+ */
54
+ public function __construct( WRIO_Plugin $plugin ) {
55
+ $this->view = WRIO_Views::get_instance( WRIO_PLUGIN_DIR );
56
+
57
+ if ( is_multisite() && defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
58
+ $clearfy_is_active_for_network = is_plugin_active_for_network( Wbcr_FactoryClearfy_Compatibility::getClearfyBasePath() );
59
+
60
+ if ( WRIO_Plugin::app()->isNetworkActive() && $clearfy_is_active_for_network ) {
61
+ $this->clearfy_collaboration = true;
62
+ }
63
+ } else if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
64
+ $this->clearfy_collaboration = true;
65
+ }
66
+
67
+ parent::__construct( $plugin );
68
+ }
69
+
70
+ /**
71
+ * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
72
+ * Меню текущего плагина будет добавлено в общее меню Clearfy
73
+ *
74
+ * @return string
75
+ */
76
+ public function getMenuScope() {
77
+ if ( $this->clearfy_collaboration ) {
78
+ $this->page_parent_page = 'rio_general';
79
+
80
+ return 'wbcr_clearfy';
81
+ }
82
+
83
+ return $this->plugin->getPluginName();
84
+ }
85
+ }
admin/pages/class-rio-settings.php ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Класс отвечает за работу страницы настроек
9
+ *
10
+ * @author Eugene Jokerov <jokerov@gmail.com>
11
+ * @copyright (c) 2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WRIO_SettingsPage extends WRIO_Page {
15
+
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public $id = 'rio_settings';
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $page_menu_dashicon = 'dashicons-admin-generic';
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public $show_right_sidebar_in_options = true;
30
+
31
+ /**
32
+ * @param WRIO_Plugin $plugin
33
+ */
34
+ public function __construct( WRIO_Plugin $plugin ) {
35
+
36
+ $this->menu_title = __( 'Settings', 'robin-image-optimizer' );
37
+ $this->page_menu_short_description = __( 'Plugin configuration', 'robin-image-optimizer' );
38
+
39
+ parent::__construct( $plugin );
40
+ }
41
+
42
+ /**
43
+ * Подключаем скрипты и стили для страницы
44
+ *
45
+ * @since 1.0.0
46
+ * @return void
47
+ * @see Wbcr_FactoryPages412_AdminPage
48
+ *
49
+ */
50
+ public function assets( $scripts, $styles ) {
51
+ parent::assets( $scripts, $styles );
52
+
53
+ $this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css' );
54
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/restore-backup.js' );
55
+
56
+ // Add Clearfy styles for HMWP pages
57
+ if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
58
+ $this->styles->add( WCL_PLUGIN_URL . '/admin/assets/css/general.css' );
59
+ }
60
+ }
61
+
62
+
63
+ /**
64
+ * Выводим предупреждения
65
+ *
66
+ */
67
+ protected function warningNotice() {
68
+ $upload_dir = wp_upload_dir();
69
+
70
+ if ( ! wp_is_writable( $upload_dir['basedir'] ) ) {
71
+ $this->printErrorNotice( __( 'Folder wp-content/uploads/ is unavailable for writing', 'robin-image-optimizer' ) );
72
+ }
73
+
74
+ $wio_backup = $upload_dir['basedir'] . '/wio_backup/';
75
+ if ( file_exists( $wio_backup ) && ! wp_is_writable( $wio_backup ) ) {
76
+ $this->printErrorNotice( __( 'Folder wp-content/uploads/wio-backup/ is unavailable for writing', 'robin-image-optimizer' ) );
77
+ }
78
+
79
+ if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON == true ) {
80
+ $this->printErrorNotice( __( 'Cron is disabled in wp-config.php', 'robin-image-optimizer' ) );
81
+ }
82
+ }
83
+
84
+
85
+ /**
86
+ * Метод должен передать массив опций для создания формы с полями.
87
+ * Созданием страницы и формы занимается фреймворк
88
+ *
89
+ * @since 1.0.0
90
+ * @return mixed[]
91
+ */
92
+ public function getPageOptions() {
93
+ $options = [];
94
+
95
+ $options[] = [
96
+ 'type' => 'html',
97
+ 'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __( 'Main Settings', 'robin-image-optimizer' ) . '</strong><p>' . __( 'This section you can set main images optimization settings.', 'robin-image-optimizer' ) . '</p></div>'
98
+ ];
99
+
100
+ $options[] = [
101
+ 'type' => 'dropdown',
102
+ 'name' => 'image_optimization_server',
103
+ 'title' => __( 'Optimization server', 'robin-image-optimizer' ),
104
+ 'data' => [
105
+ [
106
+ 'server_1',
107
+ __( 'Server 1 (✰✰✰✰✰) - image size limit up to 5 MB', 'robin-image-optimizer' ),
108
+
109
+ ],
110
+ [
111
+ 'server_2',
112
+ __( 'Server 2 (✰✰) - poor compression, image size limit up to 1 MB', 'robin-image-optimizer' )
113
+
114
+ ],
115
+ [
116
+ 'server_3',
117
+ __( "Server 3 (✰✰) - poor compression, you can't use it on a localhost", 'robin-image-optimizer' )
118
+ ],
119
+ [
120
+ 'server_4',
121
+ __( 'Server 4 (✰✰✰✰) no limits (beta)', 'robin-image-optimizer' )
122
+ ],
123
+ ],
124
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
125
+ 'hint' => __( 'We use several free servers for image optimization and can’t fully guarantee their stable performance. The server can be not available in some countries due to the political reasons. There is a solution: if one of the servers is not available or can’t optimize the image, you can try to switch to the alternative server. Each server has individual limitations for image weight and optimization level. By default, you have the best server with minimum limitations.', 'robin-image-optimizer' ),
126
+ 'default' => 'server_1',
127
+ ];
128
+
129
+ // Радио переключатель
130
+ $options[] = [
131
+ 'type' => 'dropdown',
132
+ 'name' => 'image_optimization_level',
133
+ 'way' => 'buttons',
134
+ 'title' => __( 'Compression mode', 'robin-image-optimizer' ),
135
+ 'data' => [
136
+ [
137
+ 'normal',
138
+ __( 'Lossless', 'robin-image-optimizer' ),
139
+ __( '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' )
140
+ ],
141
+ [
142
+ 'aggresive',
143
+ __( 'Lossy', 'robin-image-optimizer' ),
144
+ __( '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' )
145
+ ],
146
+ [
147
+ 'ultra',
148
+ __( 'High', 'robin-image-optimizer' ),
149
+ __( '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' )
150
+ ],
151
+ [
152
+ 'custom',
153
+ __( 'Custom', 'robin-image-optimizer' ),
154
+ __( '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' )
155
+ ]
156
+ ],
157
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
158
+ 'hint' => __( 'Select the compression mode appropriate for your case.', 'robin-image-optimizer' ),
159
+ 'default' => 'normal',
160
+ 'events' => [
161
+ 'normal' => [
162
+ 'hide' => '.factory-control-image_optimization_level_custom'
163
+ ],
164
+ 'aggresive' => [
165
+ 'hide' => '.factory-control-image_optimization_level_custom'
166
+ ],
167
+ 'ultra' => [
168
+ 'hide' => '.factory-control-image_optimization_level_custom'
169
+ ],
170
+ 'custom' => [
171
+ 'show' => '.factory-control-image_optimization_level_custom',
172
+ ],
173
+ ]
174
+ ];
175
+
176
+ // Текстовое поле
177
+ $options[] = [
178
+ 'type' => 'textbox',
179
+ 'name' => 'image_optimization_level_custom',
180
+ 'title' => __( 'Enter custom quality', 'robin-image-optimizer' ),
181
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
182
+ 'hint' => __( 'custom quality 1-100', 'robin-image-optimizer' ),
183
+ 'default' => '70'
184
+ ];
185
+
186
+ // Переключатель
187
+ $options[] = [
188
+ 'type' => 'checkbox',
189
+ 'way' => 'buttons',
190
+ 'name' => 'auto_optimize_when_upload',
191
+ 'title' => __( 'Auto optimization on upload', 'robin-image-optimizer' ),
192
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
193
+ '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' ),
194
+ 'default' => false
195
+ ];
196
+
197
+ // Переключатель
198
+ $options[] = [
199
+ 'type' => 'checkbox',
200
+ 'way' => 'buttons',
201
+ 'name' => 'backup_origin_images',
202
+ 'title' => __( 'Backup images', 'robin-image-optimizer' ),
203
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'green' ],
204
+ 'hint' => __( 'Before optimizing, all your images will be saved in a separate folder for future recovery.', 'robin-image-optimizer' ),
205
+ 'default' => true
206
+ ];
207
+
208
+ // Переключатель
209
+ $options[] = [
210
+ 'type' => 'checkbox',
211
+ 'way' => 'buttons',
212
+ 'name' => 'error_log',
213
+ 'title' => __( 'Error Log', 'robin-image-optimizer' ),
214
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
215
+ 'hint' => __( 'Enable error logging. The log will be displayed on a separate tab.', 'robin-image-optimizer' ),
216
+ 'default' => false,
217
+ 'eventsOn' => [
218
+ 'show' => '#wrio-error-log-options'
219
+ ],
220
+ 'eventsOff' => [
221
+ 'hide' => '#wrio-error-log-options'
222
+ ]
223
+ ];
224
+
225
+ $options[] = [
226
+ 'type' => 'html',
227
+ 'html' => [ $this, 'error_log_options' ]
228
+ ];
229
+
230
+ $options[] = [
231
+ 'type' => 'checkbox',
232
+ 'way' => 'buttons',
233
+ 'name' => 'convert_webp_format',
234
+ 'title' => __( 'Convert Images to WebP', 'robin-image-optimizer' ),
235
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
236
+ 'hint' => __( 'Convert JPEG & PNG images into WebP format and replace them for browsers which support it. Unsupported browsers would be skipped.', 'robin-image-optimizer' ),
237
+ 'default' => false,
238
+ 'cssClass' => ! wrio_is_license_activate() ? [ 'factory-checkbox-disabled wrio-checkbox-premium-label' ] : [],
239
+ 'eventsOn' => [
240
+ 'show' => '#wrio-webp-options'
241
+ ],
242
+ 'eventsOff' => [
243
+ 'hide' => '#wrio-webp-options'
244
+ ]
245
+ ];
246
+
247
+ $options[] = [
248
+ 'type' => 'html',
249
+ 'html' => [ $this, 'conver_webp_options' ]
250
+ ];
251
+
252
+ // восстановление
253
+ $options[] = [
254
+ 'type' => 'html',
255
+ 'html' => [ $this, 'rollbackButton' ],
256
+ ];
257
+
258
+ // Переключатель
259
+ $options[] = [
260
+ 'type' => 'checkbox',
261
+ 'way' => 'buttons',
262
+ 'name' => 'save_exif_data',
263
+ 'title' => __( 'Leave EXIF data', 'robin-image-optimizer' ),
264
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
265
+ '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' ),
266
+ 'default' => true
267
+ ];
268
+
269
+ $options[] = [
270
+ 'type' => 'html',
271
+ '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>'
272
+ ];
273
+
274
+ // Переключатель
275
+ $options[] = [
276
+ 'type' => 'checkbox',
277
+ 'way' => 'buttons',
278
+ 'name' => 'resize_larger',
279
+ 'title' => __( 'Resizing large images', 'robin-image-optimizer' ),
280
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
281
+ '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' ),
282
+ 'default' => false,
283
+ // когда чекбокс включен показываем поле с классом .factory-control-resize_larger_w
284
+ 'eventsOn' => [
285
+ 'show' => '.factory-control-resize_larger_w,.factory-control-resize_larger_h'
286
+ ],
287
+ // когда чекбокс выключен, скрываем поле с классом .factory-control-resize_larger_w
288
+ 'eventsOff' => [
289
+ 'hide' => '.factory-control-resize_larger_w,.factory-control-resize_larger_h'
290
+ ]
291
+ ];
292
+
293
+ // Текстовое поле
294
+ $options[] = [
295
+ 'type' => 'textbox',
296
+ 'name' => 'resize_larger_w',
297
+ 'title' => __( 'Enter the maximum width (px)', 'robin-image-optimizer' ),
298
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
299
+ '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' ),
300
+ 'default' => '1600'
301
+ ];
302
+
303
+ // Текстовое поле
304
+ $options[] = [
305
+ 'type' => 'textbox',
306
+ 'name' => 'resize_larger_h',
307
+ 'title' => __( 'Enter the maximum height (px)', 'robin-image-optimizer' ),
308
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
309
+ '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' ),
310
+ 'default' => '1600'
311
+ ];
312
+
313
+ // получаем зарегистрированные размеры изображений
314
+ $wp_image_sizes = wrio_get_image_sizes();
315
+ $wio_image_sizes = [];
316
+ foreach ( $wp_image_sizes as $key => $value ) {
317
+ $wio_image_sizes[] = [
318
+ $key,
319
+ $key . ' - ' . $value['width'] . 'x' . $value['height'],
320
+ ];
321
+ }
322
+
323
+ $options[] = [
324
+ 'type' => 'list',
325
+ 'way' => 'checklist',
326
+ 'name' => 'allowed_sizes_thumbnail',
327
+ 'title' => __( 'Optimize thumbnails', 'robin-image-optimizer' ),
328
+ 'data' => $wio_image_sizes,
329
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
330
+ 'hint' => __( 'Choose which sizes of thumbnails should be optimized and uncheck those that do not need optimization.', 'robin-image-optimizer' ),
331
+ 'default' => 'thumbnail,medium'
332
+ ];
333
+
334
+ $options = apply_filters( 'wbcr/rio/settings_page/options', $options );
335
+
336
+ $formOptions = [];
337
+
338
+ $formOptions[] = [
339
+ 'type' => 'form-group',
340
+ 'items' => $options,
341
+ //'cssClass' => 'postbox'
342
+ ];
343
+
344
+ return $formOptions;
345
+ }
346
+
347
+ /**
348
+ * Save advanced options in database
349
+ *
350
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
351
+ * @since 1.3.6
352
+ */
353
+ public function beforeFormSave() {
354
+
355
+ /**
356
+ * Used to save webp options. It can also be used to intercept
357
+ * other unregistered fields.
358
+ *
359
+ * @since 1.3.6
360
+ */
361
+ do_action( "wrio/settings_page/berfore_form_save" );
362
+
363
+ $error_log = (int) WRIO_Plugin::app()->request->post( WRIO_Plugin::app()->getPrefix() . 'error_log', 0 );
364
+
365
+ if ( ! $error_log ) {
366
+ return;
367
+ }
368
+
369
+ $keep_error_log_on_frontend = (int) WRIO_Plugin::app()->request->post( 'wrio_keep_error_log_on_frontend', 0 );
370
+
371
+ WRIO_Plugin::app()->updatePopulateOption( 'keep_error_log_on_frontend', $keep_error_log_on_frontend );
372
+ }
373
+
374
+ /**
375
+ * This method adds advanced options for the "Convert Images to WebP" checkbox.
376
+ *
377
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
378
+ * @since 1.3.6
379
+ */
380
+ public function conver_webp_options() {
381
+
382
+ /**
383
+ * This hook prints options for delivering webp images.
384
+ *
385
+ * @since 1.3.6
386
+ */
387
+ do_action( "wrio/settings_page/conver_webp_options" );
388
+ }
389
+
390
+ /**
391
+ * This method adds advanced options for the "Error log" checkbox.
392
+ *
393
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
394
+ * @since 1.3.6
395
+ */
396
+ public function error_log_options() {
397
+ $this->view->print_template( 'part-settings-page-error-log-options', [
398
+ 'keep_error_log_on_frontend' => (int) WRIO_Plugin::app()->getPopulateOption( 'keep_error_log_on_frontend', 0 )
399
+ ] );
400
+ }
401
+
402
+ /**
403
+ * Кнопка восстановления изображений
404
+ */
405
+ public function rollbackButton() {
406
+ ?>
407
+ <div class="form-group form-group-checkbox factory-control-rollback-button">
408
+ <label for="wio-clear-backup-btn" class="col-sm-6 control-label">
409
+ <?php _e( 'Manage backups', 'robin-image-optimizer' ); ?>
410
+ <span class="factory-hint-icon factory-hint-icon-red" data-toggle="factory-tooltip"
411
+ data-placement="right" title=""
412
+ data-original-title="<?php _e( 'You can restore the original images from a backup or clear them.', 'robin-image-optimizer' ); ?>">
413
+ <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAQAAABKmM6bAAAAUUlEQVQIHU3BsQ1AQABA0X/komIrnQHYwyhqQ1hBo9KZRKL9CBfeAwy2ri42JA4mPQ9rJ6OVt0BisFM3Po7qbEliru7m/FkY+TN64ZVxEzh4ndrMN7+Z+jXCAAAAAElFTkSuQmCC"
414
+ alt="">
415
+ </span>
416
+ </label>
417
+ <input type="hidden" value="<?php echo wp_create_nonce( 'wio-iph' ) ?>" id="wio-iph-nonce">
418
+ <div class="control-group col-sm-6">
419
+ <div class="factory-buttons-way btn-group">
420
+ <a class="btn btn-default" id="wio-restore-backup-btn"
421
+ data-confirm="<?php _e( 'Are you sure?', 'robin-image-optimizer' ); ?>"
422
+ href="#"><?php _e( 'Restore', 'robin-image-optimizer' ); ?></a>
423
+ <a class="btn btn-default" id="wio-clear-backup-btn"
424
+ data-confirm="<?php _e( 'Are you sure that you want to clear image backups folder?', 'robin-image-optimizer' ); ?>"
425
+ href="#"><?php _e( 'Clear Backup', 'robin-image-optimizer' ); ?></a>
426
+ </div>
427
+ <?php //if ( WRIO_Plugin::app()->isNetworkAdmin() ) : ?>
428
+ <!-- <div id="wio-multisite-mode" style="display:none;">-->
429
+ <!-- <ul class="factory-list factory-from-control-list factory-checklist-way">-->
430
+ <!-- --><?php
431
+ // $blogs = WIO_Multisite::getBlogs();
432
+ // ?>
433
+ <!-- <li>-->
434
+ <!-- <label for="wbcr_io_multisite_blog_all">-->
435
+ <!-- <span>-->
436
+ <!-- <input value="all" id="wbcr_io_multisite_blog_all" type="checkbox">-->
437
+ <!-- </span>-->
438
+ <!-- <span>-->
439
+ <?php //echo __( 'Select all', 'robin-image-optimizer' ); ?><!--</span>-->
440
+ <!-- </label>-->
441
+ <!-- </li>-->
442
+ <!-- --><?php //foreach ( $blogs as $blog ) : ?>
443
+ <!-- <li>-->
444
+ <!-- <label for="wbcr_io_multisite_blog_-->
445
+ <?php //echo esc_attr( $blog->blog_id ); ?><!--">-->
446
+ <!-- --><?php
447
+ // $blog_name = $blog->domain . $blog->path;
448
+ // if ( defined( 'SUBDOMAIN_INSTALL' ) && SUBDOMAIN_INSTALL ) {
449
+ // $blog_name = $blog->domain;
450
+ // }
451
+ // ?>
452
+ <!-- <span>-->
453
+ <!-- <input data-name="--><?php //echo esc_attr( $blog_name ); ?><!--"-->
454
+ <!-- class="wbcr_io_multisite_blogs"-->
455
+ <!-- value="--><?php //echo esc_attr( $blog->blog_id ); ?><!--"-->
456
+ <!-- id="wbcr_io_multisite_blog_-->
457
+ <?php //echo esc_attr( $blog->blog_id ); ?><!--"-->
458
+ <!-- type="checkbox">-->
459
+ <!-- </span>-->
460
+ <!-- <span>--><?php //echo esc_attr( $blog_name ); ?><!--</span>-->
461
+ <!-- </label>-->
462
+ <!-- </li>-->
463
+ <!-- --><?php //endforeach; ?>
464
+ <!-- </ul>-->
465
+ <!-- <button id="wio-multisite-confirm" class="btn btn-default" type="button"-->
466
+ <!-- data-action="">-->
467
+ <?php //echo __( 'Start', 'robin-image-optimizer' ); ?><!--</button>-->
468
+ <!-- </div>-->
469
+ <!-- <div id="wio-multisite-restore-progress" style="display:none;">-->
470
+ <!-- </div>-->
471
+ <?php //endif; ?>
472
+ <div class="progress" id="wio-restore-backup-progress" style="display:none;">
473
+ <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0"
474
+ aria-valuemin="0" aria-valuemax="100" style="width:0%">
475
+ </div>
476
+ </div>
477
+ <p id="wio-restore-backup-msg"
478
+ style="display:none;"><?php _e( 'Restore completed.', 'robin-image-optimizer' ); ?></p>
479
+ <p id="wio-clear-backup-msg"
480
+ style="display:none;"><?php _e( 'The backup folder was cleared.', 'robin-image-optimizer' ); ?></p>
481
+ </div>
482
+ </div>
483
+ <?php
484
+ }
485
+ }
admin/pages/class-rio-statistic.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Class WRIO_StatisticPage
9
+ * Класс отвечает за работу страницы статистики
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ */
14
+ class WRIO_StatisticPage extends WRIO_Page {
15
+
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public $id = 'rio_general';
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $type = 'page';
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public $plugin;
30
+
31
+ /**
32
+ * {@inheritdoc}
33
+ */
34
+ public $page_menu_position = 20;
35
+
36
+ /**
37
+ * {@inheritdoc}
38
+ */
39
+ public $page_menu_dashicon = 'dashicons-chart-line';
40
+
41
+ /**
42
+ * @var string
43
+ */
44
+ public $menu_target = 'options-general.php';
45
+
46
+ /**
47
+ * @var bool
48
+ */
49
+ public $internal = false;
50
+
51
+ /**
52
+ * @var bool
53
+ */
54
+ public $add_link_to_plugin_actions = true;
55
+
56
+ /**
57
+ * Page type
58
+ *
59
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
60
+ * @since 1.3.0
61
+ * @var string
62
+ */
63
+ protected $scope = 'media-library';
64
+
65
+
66
+ /**
67
+ * @param WRIO_Plugin $plugin
68
+ */
69
+ public function __construct( WRIO_Plugin $plugin ) {
70
+ $this->menu_title = __( 'Robin image optimizer', 'robin-image-optimizer' );
71
+ $this->page_menu_short_description = __( 'Compress bulk of images', 'robin-image-optimizer' );
72
+ $this->plugin = $plugin;
73
+
74
+ parent::__construct( $plugin );
75
+
76
+ add_action( 'admin_enqueue_scripts', [ $this, 'print_i18n' ] );
77
+ }
78
+
79
+ /**
80
+ * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
81
+ * Меню текущего плагина будет добавлено в общее меню Clearfy
82
+ *
83
+ * @return string
84
+ */
85
+ public function getMenuScope() {
86
+ if ( $this->clearfy_collaboration ) {
87
+ $this->internal = true;
88
+
89
+ return 'wbcr_clearfy';
90
+ }
91
+
92
+ return $this->plugin->getPluginName();
93
+ }
94
+
95
+ /**
96
+ * {@inheritdoc}
97
+ */
98
+ public function getMenuTitle() {
99
+ return $this->clearfy_collaboration ? __( 'Robin Image Optimizer', 'robin-image-optimizer' ) : __( 'Robin image optimizer', 'robin-image-optimizer' );
100
+ }
101
+
102
+ /**
103
+ * {@inheritdoc}
104
+ */
105
+ public function getPageTitle() {
106
+ return $this->clearfy_collaboration ? __( 'Image optimizer', 'robin-image-optimizer' ) : __( 'Bulk optimization', 'robin-image-optimizer' );
107
+ }
108
+
109
+ /**
110
+ * {@inheritdoc}
111
+ */
112
+ public function assets( $scripts, $styles ) {
113
+ parent::assets( $scripts, $styles );
114
+
115
+ $this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css' );
116
+
117
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/sweetalert2.js' );
118
+ $this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/sweetalert2.css' );
119
+ $this->styles->add( WRIO_PLUGIN_URL . '/admin/assets/css/sweetalert-custom.css' );
120
+
121
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/Chart.min.js' );
122
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/statistic.js' );
123
+
124
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/modals.js', [ 'jquery' ], 'wrio-modals' );
125
+ $this->scripts->add( WRIO_PLUGIN_URL . '/admin/assets/js/bulk-optimization.js', [
126
+ 'jquery',
127
+ 'wrio-modals'
128
+ ] );
129
+
130
+ // Add Clearfy styles for HMWP pages
131
+ if ( defined( 'WBCR_CLEARFY_PLUGIN_ACTIVE' ) ) {
132
+ $this->styles->add( WCL_PLUGIN_URL . '/admin/assets/css/general.css' );
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Print localization only current page
138
+ *
139
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
140
+ * @since 1.3.0
141
+ * @throws \Exception
142
+ */
143
+ public function print_i18n() {
144
+ $page = $this->plugin->request->get( 'page', null );
145
+
146
+ if ( $page != $this->getResultId() ) {
147
+ return;
148
+ }
149
+
150
+ $backup = new WIO_Backup();
151
+
152
+ wp_localize_script( 'jquery', 'wrio_l18n_bulk_page', $this->get_i18n() );
153
+
154
+ wp_localize_script( 'jquery', 'wrio_settings_bulk_page', [
155
+ 'is_premium' => wrio_is_license_activate(),
156
+ 'is_network_admin' => WRIO_Plugin::app()->isNetworkAdmin() ? 1 : 0,
157
+ 'is_writable_backup_dir' => $backup->isBackupWritable() ? 1 : 0,
158
+ 'images_backup' => WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false ) ? 1 : 0,
159
+ 'need_migration' => wbcr_rio_has_meta_to_migrate() ? 1 : 0,
160
+ 'scope' => $this->scope,
161
+ 'nonce' => wp_create_nonce( 'bulk_optimization' )
162
+ ] );
163
+ }
164
+
165
+ /**
166
+ * {@inheritdoc}
167
+ */
168
+ public function showPageContent() {
169
+ $is_premium = wrio_is_license_activate();
170
+ $statistics = $this->get_statisctic_data();
171
+
172
+ $template_data = [
173
+ 'is_premium' => $is_premium,
174
+ 'scope' => $this->scope
175
+ ];
176
+
177
+ //do_action( 'wbcr/rio/multisite_current_blog' );
178
+
179
+ // Page header
180
+ $this->view->print_template( 'part-page-header', [
181
+ 'title' => __( 'Image optimization dashboard', 'robin-image-optimizer' ),
182
+ 'description' => __( 'Monitor image optimization statistics and run on demand or scheduled optimization.', 'robin-image-optimizer' )
183
+ ], $this );
184
+
185
+ // Page tabs
186
+ $this->view->print_template( 'part-bulk-optimization-tabs', $template_data, $this );
187
+
188
+ ?>
189
+ <div class="wbcr-factory-page-group-body" style="padding:0; border-top: 1px solid #d4d4d4;">
190
+ <?php
191
+ // Servers
192
+ $this->view->print_template( 'part-bulk-optimization-servers', $template_data, $this );
193
+
194
+ // Statistic
195
+ $this->view->print_template( 'part-bulk-optimization-statistic', array_merge( $template_data, [
196
+ 'stats' => $statistics->get()
197
+ ] ), $this );
198
+
199
+ // Optimization log
200
+ $this->view->print_template( 'part-bulk-optimization-log', array_merge( $template_data, [
201
+ 'process_log' => $statistics->get_last_optimized_images()
202
+ ] ), $this );
203
+ ?>
204
+ </div>
205
+ <script type="text/html" id="wrio-tmpl-bulk-optimization">
206
+ <?php $this->view->print_template( 'modal-bulk-optimization' ); ?>
207
+ </script>
208
+ <?php
209
+
210
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
211
+ }
212
+
213
+ /**
214
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
215
+ * @since 1.3.0
216
+ * @return object|\WRIO_Image_Statistic
217
+ */
218
+ protected function get_statisctic_data() {
219
+ return WRIO_Image_Statistic::get_instance();
220
+ }
221
+
222
+ /**
223
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
224
+ * @since 1.3.0
225
+ * @return array
226
+ */
227
+ protected function get_i18n() {
228
+ return [
229
+ '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' ),
230
+ 'server_status_down' => __( 'down', 'robin-image-optimizer' ),
231
+ 'server_status_stable' => __( 'stable', 'robin-image-optimizer' ),
232
+ 'modal_error' => __( 'Error', 'robin-image-optimizer' ),
233
+ 'modal_cancel' => __( 'Cancel', 'robin-image-optimizer' ),
234
+ 'modal_confirm' => __( 'Confirm', 'robin-image-optimizer' ),
235
+ 'modal_optimization_title' => __( 'Select optimization way', 'robin-image-optimizer' ),
236
+ 'modal_optimization_monual_button' => __( 'Optimize now', 'robin-image-optimizer' ),
237
+ 'modal_optimization_cron_button' => __( 'Scheduled optimization', 'robin-image-optimizer' ),
238
+ 'need_migrations' => __( 'To start optimizing, you must complete migration from old plugin version.', 'robin-image-optimizer' ),
239
+ 'optimization_complete' => __( 'All images from the media library are optimized.', 'robin-image-optimizer' ),
240
+ 'optimization_inprogress' => __( 'Optimization in progress. Remained <span id="wio-total-unoptimized">%s</span> images.', 'robin-image-optimizer' ),
241
+ 'leave_page_warning' => __( 'Are you sure that you want to leave the page? The optimization process is not over yet, stay on the page until the end of the optimization process.', 'robin-image-optimizer' ),
242
+ 'process_without_backup' => __( 'Do you want to start optimization without backup?', 'robin-image-optimizer' ),
243
+ 'button_resume' => __( 'Resume', 'robin-image-optimizer' ),
244
+ 'button_completed' => __( 'Completed', 'robin-image-optimizer' ),
245
+ 'buttom_start' => __( 'Run', 'robin-image-optimizer' ),
246
+ 'button_stop' => __( 'Stop', 'robin-image-optimizer' ),
247
+ 'button_stop_cron' => __( 'Stop shedule optimization', 'robin-image-optimizer' )
248
+ //Don't Need a Parachute?
249
+ //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.
250
+ ];
251
+ }
252
+ }
admin/pages/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
admin/pages/log.php DELETED
@@ -1,139 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * The page Error logs
5
- *
6
- * @since 1.0.0
7
- */
8
-
9
- // Exit if accessed directly
10
- if( !defined('ABSPATH') ) {
11
- exit;
12
- }
13
-
14
- /**
15
- * Класс отвечает за работу страницы логов
16
- * @author Eugene Jokerov <jokerov@gmail.com>
17
- * @copyright (c) 2018, Webcraftic
18
- * @version 1.0
19
- */
20
- class WIO_LogPage extends Wbcr_FactoryClearfy206_PageBase {
21
-
22
- /**
23
- * The id of the page in the admin menu.
24
- *
25
- * Mainly used to navigate between pages.
26
- * @see FactoryPages410_AdminPage
27
- *
28
- * @since 1.0.0
29
- * @var string
30
- */
31
- public $id = 'io_logs'; // Уникальный идентификатор страницы
32
-
33
- public $page_menu_dashicon = 'dashicons-admin-tools'; // Иконка для закладки страницы, дашикон
34
-
35
- public $page_parent_page = null; // Уникальный идентификатор родительской страницы
36
-
37
- public $type = 'page';
38
-
39
- /**
40
- * @var bool
41
- */
42
- //public $available_for_multisite = true;
43
-
44
- /**
45
- * @var bool
46
- */
47
- public $clearfy_collaboration = false;
48
-
49
- /**
50
- * Показывать правый сайдбар?
51
- * Сайдбар будет показан на внутренних страницах шаблона.
52
- *
53
- * @var bool
54
- */
55
- public $show_right_sidebar_in_options = false;
56
-
57
- /**
58
- * @param Wbcr_Factory409_Plugin $plugin
59
- */
60
- public function __construct(Wbcr_Factory409_Plugin $plugin)
61
- {
62
- $this->menu_title = __('Error Log', 'robin-image-optimizer');
63
-
64
- if( is_multisite() && defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
65
- $clearfy_is_active_for_network = is_plugin_active_for_network(Wbcr_FactoryClearfy_Compatibility::getClearfyBasePath());
66
-
67
- if( WIO_Plugin::app()->isNetworkActive() && $clearfy_is_active_for_network ) {
68
- $this->clearfy_collaboration = true;
69
- }
70
- } else if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
71
- $this->clearfy_collaboration = true;
72
- }
73
-
74
- parent::__construct($plugin);
75
- }
76
-
77
- /**
78
- * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
79
- * Меню текущего плагина будет добавлено в общее меню Clearfy
80
- * @return string
81
- */
82
- public function getMenuScope()
83
- {
84
- if( $this->clearfy_collaboration ) {
85
- $this->page_parent_page = 'io_general';
86
-
87
- return 'wbcr_clearfy';
88
- }
89
-
90
- return $this->plugin->getPluginName();
91
- }
92
-
93
- /**
94
- * Requests assets (js and css) for the page.
95
- *
96
- * @see Wbcr_FactoryPages410_AdminPage
97
- *
98
- * @since 1.0.0
99
- * @return void
100
- */
101
- public function assets($scripts, $styles)
102
- {
103
- parent::assets($scripts, $styles);
104
-
105
- // Add Clearfy styles for HMWP pages
106
- if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
107
- $this->styles->add(WCL_PLUGIN_URL . '/admin/assets/css/general.css');
108
- }
109
- }
110
-
111
- // Метод позволяет менять заголовок меню, в зависимости от сборки плагина.
112
- public function getMenuTitle()
113
- {
114
- return defined('LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON') ? __('Image optimizer', 'robin-image-optimizer') : __('Error Log', 'robin-image-optimizer');
115
- }
116
-
117
- /**
118
- * Вывод страницы лога
119
- */
120
- public function showPageContent()
121
- {
122
- $logger = new WIO_Logger();
123
- ?>
124
- <div class="wbcr-factory-page-group-header" style="margin-top:0;">
125
- <strong><?php _e('Error Log', 'robin-image-optimizer') ?></strong>
126
-
127
- <p>
128
- <?php _e('In this section, you can track image optimization errors. Sending this log to us, will help in solving possible optimization issues.', 'robin-image-optimizer') ?>
129
- </p>
130
- </div>
131
-
132
- <div class="wbcr-factory-page-group-body" style="padding:20px">
133
- <textarea style="width:70%; height:800px;">
134
- <?php echo esc_attr($logger->get()); ?>
135
- </textarea>
136
- </div>
137
- <?php
138
- }
139
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/pages/settings.php DELETED
@@ -1,421 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * The page Settings.
5
- *
6
- * @since 1.0.0
7
- */
8
-
9
- // Exit if accessed directly
10
- if( !defined('ABSPATH') ) {
11
- exit;
12
- }
13
-
14
- /**
15
- * Класс отвечает за работу страницы настроек
16
- * @author Eugene Jokerov <jokerov@gmail.com>
17
- * @copyright (c) 2018, Webcraftic
18
- * @version 1.0
19
- */
20
- class WIO_SettingsPage extends Wbcr_FactoryClearfy206_PageBase {
21
-
22
- /**
23
- * The id of the page in the admin menu.
24
- *
25
- * Mainly used to navigate between pages.
26
- * @see FactoryPages410_AdminPage
27
- *
28
- * @since 1.0.0
29
- * @var string
30
- */
31
- public $id = 'io_settings'; // Уникальный идентификатор страницы
32
- public $page_menu_dashicon = 'dashicons-admin-generic'; // Иконка для закладки страницы, дашикон
33
- public $page_parent_page = null; // Уникальный идентификатор родительской страницы
34
-
35
- /**
36
- * @var bool
37
- */
38
- //public $available_for_multisite = true;
39
-
40
- /**
41
- * @var bool
42
- */
43
- public $clearfy_collaboration = false;
44
-
45
- /**
46
- * Показывать правый сайдбар?
47
- * Сайдбар будет показан на внутренних страницах шаблона.
48
- *
49
- * @var bool
50
- */
51
- public $show_right_sidebar_in_options = false;
52
-
53
- /**
54
- * @param Wbcr_Factory409_Plugin $plugin
55
- */
56
- public function __construct(Wbcr_Factory409_Plugin $plugin)
57
- {
58
- // Заголовок страницы
59
- $this->menu_title = __('Main Settings', 'robin-image-optimizer');
60
- $this->page_menu_short_description = __('Plugin configuration', 'robin-image-optimizer');
61
-
62
- if( is_multisite() && defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
63
- $clearfy_is_active_for_network = is_plugin_active_for_network(Wbcr_FactoryClearfy_Compatibility::getClearfyBasePath());
64
-
65
- if( WIO_Plugin::app()->isNetworkActive() && $clearfy_is_active_for_network ) {
66
- $this->clearfy_collaboration = true;
67
- }
68
- } else if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
69
- $this->clearfy_collaboration = true;
70
- }
71
-
72
- parent::__construct($plugin);
73
- }
74
-
75
- /**
76
- * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
77
- * Меню текущего плагина будет добавлено в общее меню Clearfy
78
- * @return string
79
- */
80
- public function getMenuScope()
81
- {
82
- if( $this->clearfy_collaboration ) {
83
- $this->page_parent_page = 'io_general';
84
-
85
- return 'wbcr_clearfy';
86
- }
87
-
88
- return $this->plugin->getPluginName();
89
- }
90
-
91
- /**
92
- * Подключаем скрипты и стили для страницы
93
- *
94
- * @see Wbcr_FactoryPages410_AdminPage
95
- *
96
- * @since 1.0.0
97
- * @return void
98
- */
99
- public function assets($scripts, $styles)
100
- {
101
- parent::assets($scripts, $styles);
102
- $this->scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/restore-backup.js');
103
-
104
- // Add Clearfy styles for HMWP pages
105
- if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
106
- $this->styles->add(WCL_PLUGIN_URL . '/admin/assets/css/general.css');
107
- }
108
- }
109
-
110
-
111
- /**
112
- * Выводим предупреждения
113
- *
114
- */
115
- protected function warningNotice()
116
- {
117
- $upload_dir = wp_upload_dir();
118
- if( !is_writable($upload_dir['basedir']) ) {
119
- $this->printErrorNotice(__('Folder wp-content/uploads/ is unavailable for writing', 'robin-image-optimizer'));
120
- }
121
- $wio_backup = $upload_dir['basedir'] . '/wio_backup/';
122
- if( file_exists($wio_backup) and !is_writable($wio_backup) ) {
123
- $this->printErrorNotice(__('Folder wp-content/uploads/wio-backup/ is unavailable for writing', 'robin-image-optimizer'));
124
- }
125
- if( defined('DISABLE_WP_CRON') and DISABLE_WP_CRON == true ) {
126
- $this->printErrorNotice(__('Cron is disabled in wp-config.php', 'robin-image-optimizer'));
127
- }
128
- }
129
-
130
-
131
- /**
132
- * Метод должен передать массив опций для создания формы с полями.
133
- * Созданием страницы и формы занимается фреймворк
134
- *
135
- * @since 1.0.0
136
- * @return mixed[]
137
- */
138
- public function getPageOptions()
139
- {
140
- $options = array();
141
-
142
- $options[] = array(
143
- 'type' => 'html',
144
- 'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __('Main Settings', 'robin-image-optimizer') . '</strong><p>' . __('This section you can set main images optimization settings.', 'robin-image-optimizer') . '</p></div>'
145
- );
146
-
147
- $options[] = array(
148
- 'type' => 'dropdown',
149
- 'name' => 'image_optimization_server',
150
- 'title' => __('Optimization server', 'robin-image-optimizer'),
151
- 'data' => array(
152
- array(
153
- 'server_1',
154
- __('Server 1 (recommended) - image size limit up to 5 MB', 'robin-image-optimizer'),
155
-
156
- ),
157
- array(
158
- 'server_2',
159
- __('Server 2 - image size limit up to 1 MB', 'robin-image-optimizer')
160
-
161
- ),
162
- /*array(
163
- 'server_4',
164
- __('Server 4 - image compressor test', 'robin-image-optimizer')
165
- ),*/
166
- array(
167
- 'server_3',
168
- __("Server 3 - you can't use it on a localhost", 'robin-image-optimizer')
169
- )
170
- ),
171
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
172
- 'hint' => __('We use several free servers for image optimization and can’t fully guarantee their stable performance. The server can be not available in some countries due to the political reasons. There is a solution: if one of the servers is not available or can’t optimize the image, you can try to switch to the alternative server. Each server has individual limitations for image weight and optimization level. By default, you have the best server with minimum limitations.', 'robin-image-optimizer'),
173
- 'default' => 'server_1',
174
- );
175
-
176
- // Радио переключатель
177
- $options[] = array(
178
- 'type' => 'dropdown',
179
- 'name' => 'image_optimization_level',
180
- 'way' => 'buttons',
181
- 'title' => __('Compression mode', 'robin-image-optimizer'),
182
- 'data' => array(
183
- array(
184
- 'normal',
185
- __('Lossless', 'robin-image-optimizer'),
186
- __('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')
187
- ),
188
- array(
189
- 'aggresive',
190
- __('Lossy', 'robin-image-optimizer'),
191
- __('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')
192
- ),
193
- array(
194
- 'ultra',
195
- __('High', 'robin-image-optimizer'),
196
- __('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')
197
- )
198
- ),
199
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
200
- 'hint' => __('Select the compression mode appropriate for your case.', 'robin-image-optimizer'),
201
- 'default' => 'normal',
202
- /*'events' => array(
203
- 'disable_certain_post_types_comments' => array(
204
- 'show' => '.factory-control-disable_comments_for_post_types, #wbcr-clearfy-comments-base-options'
205
- ),
206
- 'enable_comments' => array(
207
- 'show' => '#wbcr-clearfy-comments-base-options',
208
- 'hide' => '.factory-control-disable_comments_for_post_types'
209
- ),
210
- 'disable_comments' => array(
211
- 'hide' => '.factory-control-disable_comments_for_post_types, #wbcr-clearfy-comments-base-options'
212
- )
213
- )*/
214
- );
215
-
216
- // Переключатель
217
- $options[] = array(
218
- 'type' => 'checkbox',
219
- 'way' => 'buttons',
220
- 'name' => 'auto_optimize_when_upload',
221
- 'title' => __('Auto optimization on upload', 'robin-image-optimizer'),
222
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
223
- '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'),
224
- 'default' => false
225
- );
226
-
227
- // Переключатель
228
- $options[] = array(
229
- 'type' => 'checkbox',
230
- 'way' => 'buttons',
231
- 'name' => 'backup_origin_images',
232
- 'title' => __('Backup images', 'robin-image-optimizer'),
233
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
234
- 'hint' => __('Before optimizing, all your images will be saved in a separate folder for future recovery.', 'robin-image-optimizer'),
235
- 'default' => true
236
- );
237
-
238
- // Переключатель
239
- $options[] = array(
240
- 'type' => 'checkbox',
241
- 'way' => 'buttons',
242
- 'name' => 'error_log',
243
- 'title' => __('Error Log', 'robin-image-optimizer'),
244
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
245
- 'hint' => __('Enable error logging. The log will be displayed on a separate tab.', 'robin-image-optimizer'),
246
- 'default' => false
247
- );
248
-
249
- // восстановление
250
- $options[] = array(
251
- 'type' => 'html',
252
- 'html' => array($this, 'rollbackButton'),
253
- );
254
-
255
- // Переключатель
256
- $options[] = array(
257
- 'type' => 'checkbox',
258
- 'way' => 'buttons',
259
- 'name' => 'save_exif_data',
260
- 'title' => __('Leave EXIF data', 'robin-image-optimizer'),
261
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
262
- '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'),
263
- 'default' => true
264
- );
265
-
266
- $options[] = array(
267
- 'type' => 'html',
268
- '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>'
269
- );
270
-
271
- // Переключатель
272
- $options[] = array(
273
- 'type' => 'checkbox',
274
- 'way' => 'buttons',
275
- 'name' => 'resize_larger',
276
- 'title' => __('Resizing large images', 'robin-image-optimizer'),
277
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
278
- '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'),
279
- 'default' => false,
280
- // когда чекбокс включен показываем поле с классом .factory-control-resize_larger_w
281
- 'eventsOn' => array(
282
- 'show' => '.factory-control-resize_larger_w'
283
- ),
284
- // когда чекбокс выключен, скрываем поле с классом .factory-control-resize_larger_w
285
- 'eventsOff' => array(
286
- 'hide' => '.factory-control-resize_larger_w'
287
- )
288
- );
289
-
290
- // Текстовое поле
291
- $options[] = array(
292
- 'type' => 'textbox',
293
- 'name' => 'resize_larger_w',
294
- 'title' => __('Enter the maximum size (px)', 'robin-image-optimizer'),
295
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
296
- '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'),
297
- 'default' => '1600'
298
- );
299
-
300
- // получаем зарегистрированные размеры изображений
301
- $wp_image_sizes = wio_get_image_sizes();
302
- $wio_image_sizes = array();
303
- foreach($wp_image_sizes as $key => $value) {
304
- $wio_image_sizes[] = array(
305
- $key,
306
- $key . ' - ' . $value['width'] . 'x' . $value['height'],
307
- );
308
- }
309
-
310
- $options[] = array(
311
- 'type' => 'list',
312
- 'way' => 'checklist',
313
- 'name' => 'allowed_sizes_thumbnail',
314
- 'title' => __('Optimize thumbnails', 'robin-image-optimizer'),
315
- 'data' => $wio_image_sizes,
316
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
317
- 'hint' => __('Choose which sizes of thumbnails should be optimized and uncheck those that do not need optimization.', 'robin-image-optimizer'),
318
- 'default' => 'thumbnail,medium'
319
- );
320
-
321
- // cron
322
- $options[] = array(
323
- 'type' => 'html',
324
- 'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __('Scheduled optimization', 'robin-image-optimizer') . '</strong><p>' . __('Schedule your images optimization.', 'robin-image-optimizer') . '</p></div>'
325
- );
326
-
327
- // автооптимизация
328
- $options[] = array(
329
- 'type' => 'checkbox',
330
- 'way' => 'buttons',
331
- 'name' => 'image_autooptimize_mode',
332
- 'title' => __('Enable Scheduled Optimization', 'robin-image-optimizer'),
333
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
334
- 'hint' => __('You can enable image optimization on schedule. The plugin will optimize specified number of images automatically after selected time.', 'robin-image-optimizer'),
335
- 'default' => false,
336
- 'eventsOn' => array(
337
- 'show' => '#wbcr-io-shedule-options'
338
- ),
339
- 'eventsOff' => array(
340
- 'hide' => '#wbcr-io-shedule-options'
341
- )
342
- );
343
-
344
- $group_items[] = array(
345
- 'type' => 'dropdown',
346
- 'way' => 'buttons',
347
- 'name' => 'image_autooptimize_shedule_time',
348
- 'data' => array(
349
- array('wio_1_min', __('1 min', 'robin-image-optimizer')),
350
- array('wio_2_min', __('2 min', 'robin-image-optimizer')),
351
- array('wio_5_min', __('5 min', 'robin-image-optimizer')),
352
- array('wio_10_min', __('10 min', 'robin-image-optimizer')),
353
- array('wio_30_min', __('30 min', 'robin-image-optimizer')),
354
- array('wio_hourly', __('Hour', 'robin-image-optimizer')),
355
- array('wio_daily', __('Day', 'robin-image-optimizer')),
356
- ),
357
- 'default' => 'wio_5_min',
358
- 'title' => __('Run every', 'robin-image-optimizer'),
359
- 'hint' => __('Select time at which the task will be repeated.', 'robin-image-optimizer')
360
- );
361
-
362
- $group_items[] = array(
363
- 'type' => 'textbox',
364
- 'name' => 'image_autooptimize_items_number_per_interation',
365
- 'title' => __('Images per iteration', 'robin-image-optimizer'),
366
- 'layout' => array('hint-type' => 'icon', 'hint-icon-color' => 'grey'),
367
- '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'),
368
- 'default' => '3'
369
- );
370
-
371
- $options[] = array(
372
- 'type' => 'div',
373
- 'id' => 'wbcr-io-shedule-options',
374
- 'items' => $group_items
375
- );
376
-
377
- $formOptions = array();
378
-
379
- $formOptions[] = array(
380
- 'type' => 'form-group',
381
- 'items' => $options,
382
- //'cssClass' => 'postbox'
383
- );
384
-
385
- return apply_filters('wbcr_wio_settings_form_options', $formOptions);
386
- }
387
-
388
- /**
389
- * Кнопка восстановления изображений
390
- *
391
- */
392
- public function rollbackButton()
393
- {
394
- ?>
395
- <div class="form-group form-group-checkbox factory-control-save_exif_data">
396
- <label for="wbcr_io_save_exif_data" class="col-sm-6 control-label">
397
- <?php _e('Manage backups', 'robin-image-optimizer'); ?>
398
- <span class="factory-hint-icon factory-hint-icon-grey" data-toggle="factory-tooltip" data-placement="right" title="" data-original-title="<?php _e('You can restore the original images from a backup or clear them.', 'robin-image-optimizer'); ?>">
399
- <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAQAAABKmM6bAAAAUUlEQVQIHU3BsQ1AQABA0X/komIrnQHYwyhqQ1hBo9KZRKL9CBfeAwy2ri42JA4mPQ9rJ6OVt0BisFM3Po7qbEliru7m/FkY+TN64ZVxEzh4ndrMN7+Z+jXCAAAAAElFTkSuQmCC" alt="">
400
- </span>
401
- </label>
402
- <input type="hidden" value="<?php echo wp_create_nonce('wio-iph') ?>" id="wio-iph-nonce">
403
-
404
- <div class="control-group col-sm-6">
405
- <div class="factory-buttons-way btn-group">
406
- <a class="btn btn-default" id="wio-restore-backup-btn" data-confirm="<?php _e('Are you sure?', 'robin-image-optimizer'); ?>" href="#"><?php _e('Restore', 'robin-image-optimizer'); ?></a>
407
- <a class="btn btn-default" id="wio-clear-backup-btn" data-confirm="<?php _e('Are you sure that you want to clear image backups folder?', 'robin-image-optimizer'); ?>" href="#"><?php _e('Clear Backup', 'robin-image-optimizer'); ?></a>
408
- </div>
409
- <div class="progress" id="wio-restore-backup-progress" style="display:none;">
410
- <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%">
411
- </div>
412
- </div>
413
- <p id="wio-restore-backup-msg" style="display:none;"><?php _e('Restore completed.', 'robin-image-optimizer'); ?></p>
414
-
415
- <p id="wio-clear-backup-msg" style="display:none;"><?php _e('The backup folder was cleared.', 'robin-image-optimizer'); ?></p>
416
- </div>
417
- </div>
418
-
419
- <?php
420
- }
421
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/pages/statistic.php DELETED
@@ -1,479 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * The page Settings.
5
- *
6
- * @since 1.0.0
7
- */
8
-
9
- // Exit if accessed directly
10
- if( !defined('ABSPATH') ) {
11
- exit;
12
- }
13
-
14
- /**
15
- * Class WIO_StatisticPage
16
- * Класс отвечает за работу страницы статистики
17
- * @author Eugene Jokerov <jokerov@gmail.com>
18
- * @copyright (c) 2018, Webcraftic
19
- * @version 1.0
20
- * @method string getDonateWidget() - get widget content donate
21
- */
22
- class WIO_StatisticPage extends Wbcr_FactoryClearfy206_PageBase {
23
-
24
- /**
25
- * The id of the page in the admin menu.
26
- *
27
- * Mainly used to navigate between pages.
28
- * @see FactoryPages410_AdminPage
29
- *
30
- * @since 1.0.0
31
- * @var string
32
- */
33
- public $id = 'io_general'; // Уникальный идентификатор страницы
34
- public $type = 'page'; // Этот произвольный тип страницы
35
- public $plugin;
36
-
37
- /**
38
- * @var int
39
- */
40
- public $page_menu_position = 20;
41
- public $page_parent_page = null; // Уникальный идентификатор родительской страницы
42
- public $page_menu_dashicon = 'dashicons-chart-line'; // Иконка для закладки страницы, дашикон
43
-
44
-
45
- /**
46
- * @var bool
47
- */
48
- //public $available_for_multisite = true;
49
-
50
- /**
51
- * @var bool
52
- */
53
- public $clearfy_collaboration = false;
54
-
55
- /**
56
- * Показывать правый сайдбар?
57
- * Сайдбар будет показан на внутренних страницах шаблона.
58
- *
59
- * @var bool
60
- */
61
- public $show_right_sidebar_in_options = false;
62
-
63
- /**
64
- * @param Wbcr_Factory409_Plugin $plugin
65
- */
66
- public function __construct(Wbcr_Factory409_Plugin $plugin)
67
- {
68
- $this->menu_title = __('Robin image optimizer', 'robin-image-optimizer');
69
- $this->page_menu_short_description = __('Compress bulk of images', 'robin-image-optimizer');
70
-
71
- // Если плагин загружен, как самостоятельный, то мы меняем настройки страницы и делаем ее внешней,
72
- // а не внутренней страницей родительского плагина. Внешнии страницы добавляются в Wordpress меню "Общие"
73
-
74
- if( !defined('LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON') ) {
75
- // true - внутреняя, false- внешняя страница
76
- $this->internal = false;
77
- // меню к которому, нужно прикрепить ссылку на страницу
78
- $this->menu_target = 'options-general.php';
79
- // Если true, добавляет ссылку "Настройки", рядом с действиями активации, деактивации плагина, на странице плагинов.
80
- $this->add_link_to_plugin_actions = true;
81
- }
82
-
83
- if( is_multisite() && defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
84
- $clearfy_is_active_for_network = is_plugin_active_for_network(Wbcr_FactoryClearfy_Compatibility::getClearfyBasePath());
85
-
86
- if( WIO_Plugin::app()->isNetworkActive() && $clearfy_is_active_for_network ) {
87
- $this->clearfy_collaboration = true;
88
- }
89
- } else if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
90
- $this->clearfy_collaboration = true;
91
- }
92
-
93
- $this->plugin = $plugin;
94
- $this->hooks();
95
-
96
- parent::__construct($plugin);
97
- }
98
-
99
- /**
100
- * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
101
- * Меню текущего плагина будет добавлено в общее меню Clearfy
102
- * @return string
103
- */
104
- public function getMenuScope()
105
- {
106
- if( $this->clearfy_collaboration ) {
107
- $this->internal = true;
108
-
109
- return 'wbcr_clearfy';
110
- }
111
-
112
- return $this->plugin->getPluginName();
113
- }
114
-
115
- public function getPageTitle()
116
- {
117
- return $this->clearfy_collaboration ? __('Image optimizer', 'robin-image-optimizer') : __('General', 'robin-image-optimizer');
118
- }
119
-
120
- /**
121
- * Метод позволяет менять заголовок меню, в зависимости от сборки плагина.
122
- * @return string|void
123
- */
124
- public function getMenuTitle()
125
- {
126
- return $this->clearfy_collaboration ? __('Image optimizer', 'robin-image-optimizer') : __('Robin image optimizer', 'robin-image-optimizer');
127
- }
128
-
129
- /**
130
- * Устанавливаем хуки
131
- */
132
- public function hooks()
133
- {
134
- if( defined('WBCR_CLEARFY_PLUGIN_VERSION') && version_compare(WBCR_CLEARFY_PLUGIN_VERSION, '1.4.2', '>=') ) {
135
- add_action('wbcr_clearfy_quick_boards', array($this, 'widget'));
136
- }
137
-
138
- add_action('wbcr/clearfy/page_assets', array($this, 'registerWidgetScripts'), 10, 3);
139
- }
140
-
141
- /**
142
- * Подключаем скрипты и стили для страницы
143
- *
144
- * @see Wbcr_FactoryPages410_AdminPage
145
- *
146
- * @since 1.0.0
147
- * @return void
148
- */
149
- public function assets($scripts, $styles)
150
- {
151
- parent::assets($scripts, $styles);
152
-
153
- $this->styles->add(WIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css');
154
-
155
- $this->scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/Chart.min.js');
156
- $this->scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/statistic.js');
157
- $this->scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/bulk-optimozation.js');
158
-
159
- $this->scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/check-servers-status.js');
160
-
161
- // Add Clearfy styles for HMWP pages
162
- if( defined('WBCR_CLEARFY_PLUGIN_ACTIVE') ) {
163
- $this->styles->add(WCL_PLUGIN_URL . '/admin/assets/css/general.css');
164
- }
165
- }
166
-
167
- public function printServersTable()
168
- {
169
- $selected_server = $this->plugin->getOption('image_optimization_server', 'server_1');
170
-
171
- $servers = array(
172
- 'server_1' => 'Server 1',
173
- 'server_2' => 'Server 2',
174
- 'server_3' => 'Server 3',
175
- //'server_4' => 'Server 4'
176
- );
177
- ?>
178
-
179
- <div class="wbcr-rio-servers">
180
- <div class="wbcr-factory-page-group-header" style="margin-bottom:0;">
181
- <strong><?php _e('Servers', 'image-optimizer'); ?></strong>
182
-
183
- <p><?php _e('Please, find the list of available servers for image optimization below. If the server has a state “<b style="color:red">Down</b>”, it means that the server is not available, and you should choose another one. “<b style="color:green">Stable</b>” means that the server is available and you can use it.', 'image-optimizer') ?></p>
184
- </div>
185
- <div class="wbcr-factory-page-group-body">
186
- <table class="wbcr-rio-servers-list">
187
- <tr>
188
- <th><?php _e('Server', 'image-optimizer'); ?></th>
189
- <th><?php _e('Status', 'image-optimizer'); ?></th>
190
- <th><?php _e('Limits', 'image-optimizer'); ?></th>
191
- <th><?php _e('Actions', 'image-optimizer'); ?></th>
192
- </tr>
193
- <?php foreach($servers as $server_name => $server_title): ?>
194
- <?php
195
- $server_list_item_classes = $server_name == $selected_server ? ' wbcr-rio-servers-list-item-selected' : '';
196
- $server_button_classes = $server_name == $selected_server ? ' wbcr-rio-selected' : '';
197
- ?>
198
- <tr class="wbcr-rio-servers-list-item wbcr-rio-<?= $server_name ?><?= $server_list_item_classes ?>">
199
- <td><?= $server_title ?></td>
200
- <td class="wbcr-rio-server-status wbcr-rio-server-success"><?php _e('Stable', 'image-optimizer'); ?></td>
201
- <td>
202
- <?php if( $server_name == 'server_1' ): ?>
203
- <?php _e('IMG > 5MB', 'image-optimizer'); ?>
204
- <?php elseif( $server_name == 'server_2' ): ?>
205
- <?php _e('IMG > 1MB', 'image-optimizer'); ?>
206
- <?php else: ?>
207
- <?php _e('No limits', 'image-optimizer'); ?>
208
- <?php endif; ?>
209
- </td>
210
- <td>
211
- <button class="wbcr-rio-select-server-button<?= $server_button_classes ?>" data-nonce="<?= wp_create_nonce('wbcr_rio_select_' . $server_name) ?>" data-server="<?= $server_name ?>">
212
- <?php _e('Select', 'image-optimizer'); ?>
213
- </button>
214
- </td>
215
- </tr>
216
- <?php endforeach; ?>
217
- </table>
218
- <div class="wbcr-rio-warning-message">
219
- <?php
220
- $server = $this->plugin->getOption('image_optimization_server', 'server_1');
221
-
222
- if( $server == 'server_1' ) {
223
- echo __('There are limitations for the specified server (server 1) – you can’t optimize images with the size greater than <span style="color:red">5MB</span>. But you can enable the “Resizing large images” feature to reduce the image weight due to the proportional resizing before sending the file to the optimization server. ', 'robin-image-optimizer');
224
- } else if( $server == 'server_2' ) {
225
- echo __('There are limitations for the specified server (server 2) – you can’t optimize images with the size greater than <span style="color:red">1MB</span>. But you can enable the “Resizing large images” feature to reduce the image weight due to the proportional resizing before sending the file to the optimization server. Note: this server supports only one compression mode – “Normal”.', 'robin-image-optimizer');
226
- } else if( $server == 'server_3' ) {
227
- echo __("There are limitations for the specified server (server 3) – you can't use it on a localhost.", 'robin-image-optimizer');
228
- } else {
229
- echo __("This server has no limitations.", 'robin-image-optimizer');
230
- }
231
- ?>
232
- </div>
233
- </div>
234
- </div>
235
- <?php
236
- }
237
-
238
- /**
239
- * Prints the content of the page
240
- *
241
- * @see libs\factory\pages\themplates\FactoryPages410_ImpressiveThemplate
242
- */
243
- public function showPageContent()
244
- {
245
- $statistics = new WIO_Image_Statistic();
246
- $image_statistics = $statistics->get();
247
- ?>
248
- <div class="wbcr-factory-page-group-header" style="margin-top:0;">
249
- <strong><?php _e('Image optimization dashboard', 'robin-image-optimizer') ?></strong>
250
-
251
- <p>
252
- <?php _e('Monitor image optimization statistics and run on demand or scheduled optimization.', 'robin-image-optimizer') ?>
253
- </p>
254
- </div>
255
-
256
- <div class="wbcr-factory-page-group-body" style="padding:20px">
257
- <div class="wio-columns wio-page-statistic">
258
- <div>
259
- <div class="wio-chart-container wio-overview-chart-container">
260
- <canvas id="wio-main-chart" width="180" height="180" data-unoptimized="<?php echo esc_attr($image_statistics['unoptimized']); ?>" data-optimized="<?php echo esc_attr($image_statistics['optimized']); ?>" data-errors="<?php echo esc_attr($image_statistics['error']); ?>" style="display: block;"></canvas>
261
- <div id="wio-overview-chart-percent" class="wio-chart-percent"><?php echo esc_attr($image_statistics['optimized_percent']); ?>
262
- <span>%</span></div>
263
- <p class="wio-global-optim-phrase wio-clear">
264
- <?php _e('You optimized', 'robin-image-optimizer'); ?>
265
- <span class="wio-total-percent"><?php echo esc_attr($image_statistics['optimized_percent']); ?>
266
- %</span> <?php _e("of your website's images", 'robin-image-optimizer'); ?>
267
- </p>
268
- </div>
269
- <div style="margin-left:200px;">
270
- <div id="wio-overview-chart-legend">
271
- <ul class="wio-doughnut-legend">
272
- <li>
273
- <span style="background-color:#d6d6d6"></span><?php _e('Unoptimized', 'robin-image-optimizer'); ?>
274
- -
275
- <span class="wio-num" id="wio-unoptimized-num"><?php echo esc_attr($image_statistics['unoptimized']); ?></span>
276
- </li>
277
- <li>
278
- <span style="background-color:#8bc34a"></span><?php _e('Optimized', 'robin-image-optimizer'); ?>
279
- -
280
- <span class="wio-num" id="wio-optimized-num"><?php echo esc_attr($image_statistics['optimized']); ?></span>
281
- </li>
282
- <li>
283
- <span style="background-color:#f1b1b6"></span><?php _e('Error', 'robin-image-optimizer'); ?>
284
- -
285
- <span class="wio-num" id="wio-error-num"><?php echo esc_attr($image_statistics['error']); ?></span>
286
- </li>
287
- </ul>
288
- </div>
289
- <h3 class="screen-reader-text"><?php _e('Statistics', 'robin-image-optimizer'); ?></h3>
290
- <!--<div class="wio-number-you-optimized">
291
- <p>
292
- <span id="wio-total-optimized-attachments" class="wio-number"><?php echo esc_attr($image_statistics['optimized']); ?></span>
293
- <span class="wio-text">
294
- <?php _e("that's the number of original images<br> you optimized with Image Optimizer", 'robin-image-optimizer'); ?>
295
- </span>
296
- </p>
297
- </div>-->
298
- <div class="wio-bars" style="width: 90%">
299
- <p><?php _e('Original size', 'robin-image-optimizer'); ?></p>
300
-
301
- <div class="wio-bar-negative base-transparent wio-right-outside-number">
302
- <div id="wio-original-bar" class="wio-progress" style="width: 100%">
303
- <span class="wio-barnb" id="wio-original-size"><?php echo esc_attr($statistics->convertToReadableSize($image_statistics['original_size'])); ?></span>
304
- </div>
305
- </div>
306
- <p><?php _e('Optimized size', 'robin-image-optimizer'); ?></p>
307
-
308
- <div class="wio-bar-primary base-transparent wio-right-outside-number">
309
- <div id="wio-optimized-bar" class="wio-progress" style="width: <?php echo ($image_statistics['percent_line']) ? esc_attr($image_statistics['percent_line']) : 100; ?>%">
310
- <span class="wio-barnb" id="wio-optimized-size"><?php echo esc_attr($statistics->convertToReadableSize($image_statistics['optimized_size'])); ?></span>
311
- </div>
312
- </div>
313
- </div>
314
- <div class="wio-number-you-optimized">
315
- <p>
316
- <span id="wio-total-optimized-attachments-pct" class="wio-number"><?php echo esc_attr($image_statistics['save_size_percent']); ?>
317
- %</span>
318
- <span class="wio-text">
319
- <?php _e("that's the size you saved <br>by using Image Optimizer", 'robin-image-optimizer'); ?>
320
- </span>
321
- </p>
322
- </div>
323
- </div>
324
- </div>
325
- <div>
326
- <input type="hidden" value="<?php echo wp_create_nonce('wio-iph') ?>" id="wio-iph-nonce">
327
-
328
- <p>
329
- <?php $this->button(); ?>
330
- <span id="wio-start-msg-complete"><?php _e('All images from the media library are optimized.', 'robin-image-optimizer'); ?></span>
331
- </p>
332
-
333
- <p id="wio-start-msg-top"><?php _e('Optimization in progress. Remained', 'robin-image-optimizer'); ?>
334
- <span id="wio-total-unoptimized"><?php echo esc_attr($image_statistics['unoptimized']); ?></span> <?php _e('images.', 'robin-image-optimizer'); ?>
335
- </p>
336
- </div>
337
- </div>
338
- </div>
339
- <?php $this->printServersTable(); ?>
340
- <?php
341
- }
342
-
343
- /**
344
- * Кнопка запуска оптимизации
345
- *
346
- */
347
- public function button()
348
- {
349
- $backup_origin_images = WIO_Plugin::app()->getOption('backup_origin_images', false);
350
- $is_cron_mode = WIO_Plugin::app()->getOption('image_autooptimize_mode', false);
351
- $button_classes = array(
352
- 'wio-optimize-button'
353
- );
354
- if( !$backup_origin_images ) {
355
- $button_classes[] = 'wio-nobackup';
356
- }
357
- $button_name = __('Run', 'robin-image-optimizer');
358
- if( $is_cron_mode ) {
359
- $button_classes[] = 'wio-cron-mode';
360
- $cron_running = WIO_Plugin::app()->getOption('cron_running', false);
361
- if( $cron_running ) {
362
- $button_classes[] = 'wio-running';
363
- $button_name = __('Stop', 'robin-image-optimizer');
364
- } else {
365
- $button_name = __('Run', 'robin-image-optimizer');
366
- }
367
- }
368
- ?>
369
- <button type="button" id="wio-start-optimization" data-confirm="<?php _e('Do you want to start optimization without backup?', 'robin-image-optimizer'); ?>" data-start="<?php _e('Resume', 'robin-image-optimizer'); ?>" data-stop="<?php _e('Stop', 'robin-image-optimizer'); ?>" class="<?php echo join(' ', $button_classes); ?>"><?php echo esc_attr($button_name); ?></button>
370
- <?php
371
- }
372
-
373
- /**
374
- * @param string $page_id
375
- * @param Wbcr_Factory409_ScriptList $scripts
376
- * @param Wbcr_Factory409_StyleList $styles
377
- */
378
- public function registerWidgetScripts($page_id, $scripts, $styles)
379
- {
380
- if( $page_id == 'quick_start-wbcr_clearfy' ) {
381
- // Add scripts
382
- $scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/Chart.min.js');
383
- $scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/bulk-optimozation.js');
384
- $scripts->add(WIO_PLUGIN_URL . '/admin/assets/js/statistic.js');
385
-
386
- // Add styles
387
- $styles->add(WIO_PLUGIN_URL . '/admin/assets/css/base-statistic.css');
388
- }
389
- }
390
-
391
- /**
392
- * Виджет для clearfy
393
- *
394
- */
395
- public function widget()
396
- {
397
-
398
- $statistics = new WIO_Image_Statistic();
399
- $image_statistics = $statistics->get();
400
-
401
- $default_level = WIO_Plugin::app()->getOption('image_optimization_level', 'normal');
402
- ?>
403
-
404
- <div class="col-sm-12">
405
- <div class="wio-image-optimize-board wbcr-clearfy-board">
406
- <h4 class="wio-text-left"><?php _e('Images optimization', 'robin-image-optimizer'); ?></h4>
407
-
408
- <div class="wio-columns wio-widget">
409
- <div class="wio-col col-chart">
410
- <div class="wio-chart-container wio-overview-chart-container">
411
- <canvas id="wio-main-chart" width="130" height="130" data-unoptimized="<?php echo esc_attr($image_statistics['unoptimized']); ?>" data-optimized="<?php echo esc_attr($image_statistics['optimized']); ?>" data-errors="<?php echo esc_attr($image_statistics['error']); ?>" style="display: block;"></canvas>
412
- <div id="wio-overview-chart-percent" class="wio-chart-percent"><?php echo esc_attr($image_statistics['optimized_percent']); ?>
413
- <span>%</span></div>
414
- </div>
415
- <div id="wio-overview-chart-legend">
416
- <ul class="wio-doughnut-legend">
417
- <li>
418
- <span style="background-color:#d6d6d6"></span><?php _e('Unoptimized', 'robin-image-optimizer'); ?>
419
- -
420
- <span class="wio-num" id="wio-unoptimized-num"><?php echo esc_attr($image_statistics['unoptimized']); ?></span>
421
- </li>
422
- <li>
423
- <span style="background-color:#8bc34a"></span><?php _e('Optimized', 'robin-image-optimizer'); ?>
424
- -
425
- <span class="wio-num" id="wio-optimized-num"><?php echo esc_attr($image_statistics['optimized']); ?></span>
426
- </li>
427
- <li>
428
- <span style="background-color:#f1b1b6"></span><?php _e('Error', 'robin-image-optimizer'); ?>
429
- -
430
- <span class="wio-num" id="wio-error-num"><?php echo esc_attr($image_statistics['error']); ?></span>
431
- </li>
432
- </ul>
433
- </div>
434
- <div class="wio-bars">
435
- <p><?php _e('Original size', 'robin-image-optimizer'); ?></p>
436
-
437
- <div class="wio-bar-negative base-transparent wio-right-outside-number">
438
- <div id="wio-original-bar" class="wio-progress" style="width: 100%">
439
- <span class="wio-barnb" id="wio-original-size"><?php echo esc_attr($statistics->convertToReadableSize($image_statistics['original_size'])); ?></span>
440
- </div>
441
- </div>
442
- <p><?php _e('Optimized size', 'robin-image-optimizer'); ?></p>
443
-
444
- <div class="wio-bar-primary base-transparent wio-right-outside-number">
445
- <div id="wio-optimized-bar" class="wio-progress" style="width: <?php echo ($image_statistics['percent_line']) ? esc_attr($image_statistics['percent_line']) : 100; ?>%">
446
- <span class="wio-barnb" id="wio-optimized-size"><?php echo esc_attr($statistics->convertToReadableSize($image_statistics['optimized_size'])); ?></span>
447
- </div>
448
- </div>
449
- </div>
450
- </div>
451
- <ul class="wio-widget-bottom">
452
- <li>
453
- <input type="hidden" value="<?php echo wp_create_nonce('wio-iph') ?>" id="wio-iph-nonce">
454
-
455
- <p>
456
- <?php $this->button(); ?>
457
- <span id="wio-start-msg-complete"><?php _e('All images from the media library are optimized.', 'robin-image-optimizer'); ?></span>
458
- </p>
459
-
460
- <p id="wio-start-msg-top"><?php _e('Optimization in progress. Remained', 'robin-image-optimizer'); ?>
461
- <span id="wio-total-unoptimized"><?php echo esc_attr($image_statistics['unoptimized']); ?></span>
462
- </p>
463
- </li>
464
- <li>
465
- <div class="factory-dropdown factory-from-control-dropdown factory-buttons-way" data-way="buttons">
466
- <div id="wio-level-buttons" class="btn-group factory-buttons-group">
467
- <button type="button" data-level="normal" class="btn btn-default btn-small <?php if( $default_level == 'normal' ) : ?>active<?php endif; ?>"><?php _e('Normal', 'robin-image-optimizer'); ?></button>
468
- <button type="button" data-level="aggresive" class="btn btn-default btn-small <?php if( $default_level == 'aggresive' ) : ?>active<?php endif; ?>"><?php _e('Medium', 'robin-image-optimizer'); ?></button>
469
- <button type="button" data-level="ultra" class="btn btn-default btn-small <?php if( $default_level == 'ultra' ) : ?>active<?php endif; ?>"><?php _e('High', 'robin-image-optimizer'); ?></button>
470
- </div>
471
- </div>
472
- </li>
473
- </ul>
474
- </div>
475
- </div>
476
- </div>
477
- <?php
478
- }
479
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-rio-plugin.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Основной класс плагина
9
+ *
10
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
11
+ * @copyright (c) 19.02.2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WRIO_Plugin extends Wbcr_Factory412_Plugin {
15
+
16
+ /**
17
+ * @var Wbcr_Factory412_Plugin
18
+ */
19
+ private static $app;
20
+
21
+ /**
22
+ * @param string $plugin_path
23
+ * @param array $data
24
+ *
25
+ * @throws Exception
26
+ */
27
+ public function __construct( $plugin_path, $data ) {
28
+ self::$app = $this;
29
+ parent::__construct( $plugin_path, $data );
30
+
31
+ $this->includes();
32
+
33
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
34
+ // Ajax files
35
+ require_once( WRIO_PLUGIN_DIR . '/admin/ajax/backup.php' );
36
+ require_once( WRIO_PLUGIN_DIR . '/admin/ajax/bulk-optimization.php' );
37
+ require_once( WRIO_PLUGIN_DIR . '/admin/ajax/logs.php' );
38
+ // Not under AJAX logical operator above on purpose to have helpers available to find out whether
39
+ // metas were migrated or not
40
+ require_once( WRIO_PLUGIN_DIR . '/admin/ajax/meta-migrations.php' );
41
+ }
42
+
43
+ if ( is_admin() ) {
44
+ $this->initActivation();
45
+ }
46
+
47
+ add_action( 'plugins_loaded', [ $this, 'pluginsLoaded' ] );
48
+ }
49
+
50
+ /**
51
+ * Подключаем модули классы и функции
52
+ */
53
+ protected function includes() {
54
+
55
+ require_once( WRIO_PLUGIN_DIR . '/includes/functions.php' );
56
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-views.php' );
57
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-attachment.php' );
58
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-media-library.php' );
59
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-abstract.php' );
60
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-image-statistic.php' );
61
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-backup.php' );
62
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-optimization-tools.php' );
63
+
64
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-helper.php' );
65
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-object.php' ); // Base object
66
+
67
+ // Database related models
68
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-active-record.php' );
69
+ // Base class
70
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-base-extra-data.php' );
71
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-attachment-extra-data.php' );
72
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-server-smushit-extra-data.php' );
73
+
74
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/models/class-rio-process-queue-table.php' ); // Processing queue model
75
+
76
+ // Cron
77
+ // ----------------
78
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/class-rio-cron.php' );
79
+ new WRIO_Cron();
80
+
81
+ // Logger
82
+ // ----------------
83
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/logger/class-rio-logger.php' );
84
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/logger/class-rio-log-export.php' );
85
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/logger/class-rio-log-reader.php' );
86
+ new WRIO_Logger();
87
+ }
88
+
89
+ /**
90
+ * Статический метод для быстрого доступа к информации о плагине, а также часто использумых методах.
91
+ *
92
+ * @return Wbcr_Factory412_Plugin
93
+ */
94
+ public static function app() {
95
+ return self::$app;
96
+ }
97
+
98
+ /**
99
+ * Инициализируем активацию плагина
100
+ */
101
+ protected function initActivation() {
102
+ include_once( WRIO_PLUGIN_DIR . '/admin/activation.php' );
103
+ self::app()->registerActivation( 'WIO_Activation' );
104
+ }
105
+
106
+ /**
107
+ * Регистрируем страницы плагина
108
+ *
109
+ * @throws Exception
110
+ */
111
+ private function registerPages() {
112
+ $admin_path = WRIO_PLUGIN_DIR . '/admin/pages/';
113
+
114
+ // Parent page class
115
+ require_once( $admin_path . '/class-rio-page.php' );
116
+
117
+ if ( ! wrio_is_clearfy_license_activate() ) {
118
+ self::app()->registerPage( 'WRIO_License_Page', $admin_path . '/class-rio-license.php' );
119
+ }
120
+
121
+ self::app()->registerPage( 'WRIO_SettingsPage', $admin_path . '/class-rio-settings.php' );
122
+ self::app()->registerPage( 'WRIO_StatisticPage', $admin_path . '/class-rio-statistic.php' );
123
+
124
+ if ( self::app()->getPopulateOption( 'error_log', false ) ) {
125
+ self::app()->registerPage( 'WRIO_LogPage', $admin_path . '/class-rio-log.php' );
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Подключаем функции бекенда
131
+ *
132
+ * @throws Exception
133
+ */
134
+ public function pluginsLoaded() {
135
+ if ( is_admin() || wrio_doing_cron() || wrio_doing_rest_api() ) {
136
+ $media_library = WRIO_Media_Library::get_instance();
137
+ $media_library->initHooks();
138
+ }
139
+
140
+ if ( is_admin() ) {
141
+ require_once( WRIO_PLUGIN_DIR . '/admin/boot.php' );
142
+ //require_once( WRIO_PLUGIN_DIR . '/admin/includes/classes/class-rio-nextgen-landing.php' );
143
+
144
+ $this->registerPages();
145
+ }
146
+
147
+ if ( wrio_doing_cron() || wrio_doing_rest_api() ) {
148
+ $media_library = WRIO_Media_Library::get_instance();
149
+ $media_library->initHooks();
150
+ }
151
+
152
+ if ( wrio_is_license_activate() ) {
153
+ require_once( WRIO_PLUGIN_DIR . '/libs/addons/robin-image-optimizer-premium.php' );
154
+ wrio_premium_load();
155
+ }
156
+ }
157
+ }
158
+
includes/class.plugin.php DELETED
@@ -1,193 +0,0 @@
1
- <?php
2
- /**
3
- * Основной класс плагина
4
- * @author Webcraftic <wordpress.webraftic@gmail.com>
5
- * @copyright (c) 19.02.2018, Webcraftic
6
- * @version 1.0
7
- */
8
-
9
- // Exit if accessed directly
10
- if( !defined('ABSPATH') ) {
11
- exit;
12
- }
13
-
14
- if( !class_exists('WIO_Plugin') ) {
15
- if( !class_exists('WIO_PluginFactory') ) {
16
- // Этот плагин может быть аддоном плагина Clearfy, если он загружен, как аддон, то мы не подключаем фреймворк,
17
- // а наследуем функции фреймворка от плагина Clearfy. Если плагин скомпилирован, как отдельный плагин, то он использует собственный фреймворк для работы.
18
- // Константа LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON утсанавливается в классе libs/factory/core/includes/Wbcr_Factory409_Plugin
19
-
20
- if( defined('LOADING_ROBIN_IMAGE_OPTIMIZER_AS_ADDON') ) {
21
- class WIO_PluginFactory {
22
-
23
- }
24
- } else {
25
- class WIO_PluginFactory extends Wbcr_Factory409_Plugin {
26
-
27
- }
28
- }
29
- }
30
-
31
- class WIO_Plugin extends WIO_PluginFactory {
32
-
33
- /**
34
- * @var Wbcr_Factory409_Plugin
35
- */
36
- private static $app;
37
-
38
- /**
39
- * @var bool
40
- */
41
- private $as_addon;
42
-
43
-
44
- /**
45
- * @param string $plugin_path
46
- * @param array $data
47
- * @throws Exception
48
- */
49
- public function __construct($plugin_path, $data)
50
- {
51
- $this->as_addon = isset($data['as_addon']) ? true : false;
52
-
53
- if( $this->as_addon ) {
54
- $plugin_parent = isset($data['plugin_parent']) ? $data['plugin_parent'] : null;
55
-
56
- if( !($plugin_parent instanceof Wbcr_Factory409_Plugin) ) {
57
- throw new Exception(__('An invalid instance of the class was passed.', 'robin-image-optimizer'));
58
- }
59
-
60
- // Если плагин загружен, как аддон, то мы передаем в app ссылку на класс родителя
61
- self::$app = $plugin_parent;
62
- } else {
63
- // Если плагин самостоятельный, то записываем в app сслыку на текущий класс
64
- self::$app = $this;
65
-
66
- parent::__construct($plugin_path, $data);
67
- }
68
-
69
- // Для корректной работы с файлами имена которых в utf-8
70
- //setlocale(LC_ALL, "en_US.utf8");
71
-
72
- $this->init();
73
-
74
- $this->setModules();
75
-
76
- if( is_admin() ) {
77
- if( defined('DOING_AJAX') && DOING_AJAX ) {
78
- // Ajax files
79
- require_once(WIO_PLUGIN_DIR . '/admin/ajax/check-servers-status.php');
80
- require_once(WIO_PLUGIN_DIR . '/admin/ajax/select-server.php');
81
- require_once(WIO_PLUGIN_DIR . '/admin/ajax/backup.php');
82
- require_once(WIO_PLUGIN_DIR . '/admin/ajax/optimization.php');
83
- }
84
-
85
- $this->initActivation();
86
- }
87
-
88
- add_action('plugins_loaded', array($this, 'pluginsLoaded'));
89
- }
90
-
91
-
92
- /**
93
- * Подключаем функции бекенда
94
- */
95
- public function pluginsLoaded()
96
- {
97
- self::app()->setTextDomain('robin-image-optimizer', WIO_PLUGIN_DIR);
98
-
99
- if( is_admin() ) {
100
- require_once(WIO_PLUGIN_DIR . '/admin/boot.php');
101
-
102
- $this->registerPages();
103
- }
104
- }
105
-
106
- /**
107
- * Подключение необходимых php файлов и инициализация
108
- *
109
- * @return void
110
- */
111
- protected function init()
112
- {
113
- require_once(WIO_PLUGIN_DIR . '/includes/functions.php');
114
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.attachment.php');
115
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.media-library.php');
116
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-abstract.php');
117
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.cron.php');
118
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-statistic.php');
119
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.backup.php');
120
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.logger.php');
121
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.optimization-tools.php');
122
-
123
- $cron = new WIO_Cron();
124
- $cron->initHooks();
125
-
126
- $media_library = new WIO_MediaLibrary();
127
- $media_library->initHooks();
128
- }
129
-
130
- /**
131
- * Статический метод для быстрого доступа к информации о плагине, а также часто использумых методах.
132
- *
133
- * Пример:
134
- * WIO_Plugin::app()->getOption()
135
- * WIO_Plugin::app()->updateOption()
136
- * WIO_Plugin::app()->deleteOption()
137
- * WIO_Plugin::app()->getPluginName()
138
- *
139
- * @return Wbcr_Factory409_Plugin
140
- */
141
- public static function app()
142
- {
143
- return self::$app;
144
- }
145
-
146
- /**
147
- * Подключаем модули фреймворка
148
- */
149
- protected function setModules()
150
- {
151
- if( !$this->as_addon ) {
152
- self::app()->load(array(
153
- // Модуль отвечает за подключение скриптов и стилей для интерфейса
154
- array('libs/factory/bootstrap', 'factory_bootstrap_409', 'admin'),
155
- // Модуль отвечает за создание форм и полей
156
- array('libs/factory/forms', 'factory_forms_410', 'admin'),
157
- // Модуль отвечает за создание шаблонов страниц плагина
158
- array('libs/factory/pages', 'factory_pages_410', 'admin'),
159
- // Модуль в котором хранится общий функционал плагина Clearfy и его аддонов
160
- array('libs/factory/clearfy', 'factory_clearfy_206', 'all')
161
- ));
162
- }
163
- }
164
-
165
- /**
166
- * Инициализируем активацию плагина
167
- */
168
- protected function initActivation()
169
- {
170
- include_once(WIO_PLUGIN_DIR . '/admin/activation.php');
171
- self::app()->registerActivation('WIO_Activation');
172
- }
173
-
174
- /**
175
- * Регистрируем страницы плагина
176
- */
177
- private function registerPages()
178
- {
179
-
180
- $admin_path = WIO_PLUGIN_DIR . '/admin/pages';
181
-
182
- // Пример основной страницы настроек
183
- self::app()->registerPage('WIO_SettingsPage', $admin_path . '/settings.php');
184
-
185
- // Пример внутренней страницы настроек
186
- self::app()->registerPage('WIO_StatisticPage', $admin_path . '/statistic.php');
187
-
188
- if( self::app()->getOption('error_log', false) ) {
189
- self::app()->registerPage('WIO_LogPage', $admin_path . '/log.php');
190
- }
191
- }
192
- }
193
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class-rio-attachment.php ADDED
@@ -0,0 +1,893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с WordPress attachment.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_Attachment {
16
+
17
+ /**
18
+ * @var int
19
+ */
20
+ private $id;
21
+
22
+ /**
23
+ * @var array meta-данные
24
+ */
25
+ private $attachment_meta;
26
+
27
+ /**
28
+ * @var array массив с данными о папке uploads
29
+ */
30
+ private $wp_upload_dir;
31
+
32
+ /**
33
+ * @var string
34
+ */
35
+ private $url;
36
+
37
+ /**
38
+ * @var string
39
+ */
40
+ private $path;
41
+
42
+ /**
43
+ * @var RIO_Process_Queue
44
+ */
45
+ private $optimization_data;
46
+
47
+ /**
48
+ * Инициализация аттачмента
49
+ *
50
+ * @param int $attachment_id Номер аттачмента из медиабиблиотеки
51
+ * @param array|false $attachment_meta метаданные аттачмента. Ключи массива аналогичны функции wp_get_attachment_metadata
52
+ */
53
+ public function __construct( $attachment_id, $attachment_meta = false ) {
54
+ $this->id = $attachment_id;
55
+ $this->wp_upload_dir = wp_upload_dir();
56
+ $this->attachment_meta = $attachment_meta;
57
+
58
+ if ( ! $attachment_meta ) {
59
+ // some meta can be missing due to: https://wordpress.stackexchange.com/q/330174/149161
60
+ $this->attachment_meta = wp_get_attachment_metadata( $this->id );
61
+ }
62
+
63
+ if ( $this->attachment_meta && isset( $this->attachment_meta['file'] ) ) {
64
+ $this->url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . $this->attachment_meta['file'];
65
+ $this->path = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . $this->attachment_meta['file'] );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Актуализирует мета данные аттачмента и загружает актуальные мета данные и данные по оптимизации из базы.
71
+ */
72
+ public function reload() {
73
+ $this->attachment_meta = wp_get_attachment_metadata( $this->id );
74
+ $this->optimization_data = new RIO_Process_Queue( array(
75
+ 'object_id' => $this->id,
76
+ 'object_name' => '',
77
+ 'item_type' => 'attachment',
78
+ ) );
79
+ $this->optimization_data->load();
80
+ }
81
+
82
+ /**
83
+ * Добавляем сообщение в лог файл.
84
+ *
85
+ * @param string $message Текст сообщения об ошибке.
86
+ */
87
+ public function writeLog( $message ) {
88
+
89
+ $char = "\t-> ";
90
+ $nl = PHP_EOL;
91
+
92
+ $error = sprintf( 'Error to optimize attachment (ID: #%s). Message: "%s"', $this->id, trim( $message ) ) . $nl;
93
+ $error .= $char . sprintf( "Attachment optimized? %s", ( $this->isOptimized() ? 'Yes' : 'No' ) ) . $nl;
94
+ $error .= $char . sprintf( "Should be resized? %s", ( $this->isNeedResize() ? 'Yes' : 'No' ) ) . $nl;
95
+ $error .= $char . sprintf( "Original size: %sx%s", $this->attachment_meta['width'], $this->attachment_meta['height'] ) . $nl;
96
+ $error .= $char . sprintf( "Relative path: %s", $this->attachment_meta['file'] ) . $nl;
97
+ $error .= $char . sprintf( "Server used: %s", WRIO_Plugin::app()->getPopulateOption( 'image_optimization_server', 'server_1' ) ) . $nl;
98
+
99
+ if ( ! empty( $this->attachment_meta['sizes'] ) ) {
100
+ $error .= $char . ' Additional sizes:' . $nl;
101
+ foreach ( $this->attachment_meta['sizes'] as $size_type => $size_info ) {
102
+ $error .= "\t" . $char . sprintf( 'Type: %s, size: %sx%s, MIME type: %s', $size_type, $size_info['width'], $size_info['height'], $size_info['mime-type'] ) . $nl;
103
+ }
104
+ }
105
+
106
+ WRIO_Logger::error( $error );
107
+ }
108
+
109
+ /**
110
+ * Возвращает объект с информацией об оптимизации
111
+ *
112
+ * @return RIO_Process_Queue
113
+ */
114
+ public function getOptimizationData() {
115
+ if ( ! isset( $optimization_data ) ) {
116
+ $this->optimization_data = new RIO_Process_Queue( array(
117
+ 'object_id' => $this->id,
118
+ 'object_name' => '',
119
+ 'item_type' => 'attachment',
120
+ ) );
121
+ $this->optimization_data->load();
122
+ }
123
+
124
+ return $this->optimization_data;
125
+ }
126
+
127
+ /**
128
+ * Оптимизация аттачмента.
129
+ *
130
+ * @param string $optimization_level Уровень оптимизации изображения.
131
+ *
132
+ * @return array
133
+ */
134
+ public function optimize( $optimization_level = '' ) {
135
+ $optimize_results = array(
136
+ 'original_size' => 0,
137
+ 'optimized_size' => 0,
138
+ );
139
+
140
+ if ( empty( $optimization_level ) ) {
141
+ $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
142
+ }
143
+
144
+ if ( $optimization_level == 'custom' ) {
145
+ $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
146
+ $optimization_level = intval( $custom_quality );
147
+ }
148
+
149
+ $optimization_data = $this->getOptimizationData();
150
+ $results = array(
151
+ 'original_size' => 0,
152
+ 'final_size' => 0,
153
+ 'original_mime_type' => '',
154
+ 'final_mime_type' => '',
155
+ );
156
+ $results['processing_level'] = $optimization_level;
157
+
158
+ $path = $this->get( 'path' );
159
+
160
+ // проверяем наличие основного файла, если его нет, то исключаем из дальнейшей обработки
161
+ if ( ! file_exists( $path ) ) {
162
+ $results['result_status'] = 'error';
163
+ $extra_data = array(
164
+ 'error' => 'path',
165
+ 'error_msg' => sprintf( 'File path %s', ( empty( $path ) ) ? 'is empty' : $path . ' does not exist' ),
166
+ );
167
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
168
+ $optimization_data->configure( $results );
169
+ $optimization_data->save();
170
+
171
+ WRIO_Logger::error( sprintf( 'Failed to find original attachment #%s located in %s. Skipping optimization. This may be caused due to bug in %s function, which returns false for attachment meta', $this->id, empty( $path ) ? '*empty path*' : $path, 'wp_get_attachment_metadata()' ) );
172
+
173
+ return $optimize_results;
174
+ }
175
+
176
+ // сначала бекапим
177
+ $is_image_backuped = $this->backup();
178
+
179
+ if ( is_wp_error( $is_image_backuped ) ) {
180
+ $error_msg = $is_image_backuped->get_error_message();
181
+ $this->writeLog( $error_msg );
182
+
183
+ $results['result_status'] = 'error';
184
+ $extra_data = array(
185
+ 'error' => 'backup',
186
+ 'error_msg' => 'Failed to backup',
187
+ );
188
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
189
+ $optimization_data->configure( $results );
190
+ $optimization_data->save();
191
+
192
+ WRIO_Logger::error( sprintf( 'Failed to make backup of original attachment #%s. Skipping optimization.', $this->id ) );
193
+
194
+ return $optimize_results;
195
+ }
196
+
197
+ $results['is_backed_up'] = $is_image_backuped;
198
+
199
+ $original_main_size = filesize( $path );
200
+
201
+ // если файл большой - изменяем размер
202
+ if ( $this->isNeedResize() ) {
203
+ $this->resize();
204
+ }
205
+
206
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
207
+
208
+ clearstatcache(); // на всякий случай очистим кеш файловой статистики
209
+
210
+ $optimized_img_data = $image_processor->process( array(
211
+ 'image_url' => $this->get( 'url' ),
212
+ 'image_path' => $this->get( 'path' ),
213
+ 'quality' => $image_processor->quality( $optimization_level ),
214
+ 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
215
+ ) );
216
+
217
+ // проверяем на ошибку
218
+ if ( is_wp_error( $optimized_img_data ) ) {
219
+ $error_msg = $optimized_img_data->get_error_message();
220
+ $this->writeLog( $error_msg );
221
+
222
+ $results['result_status'] = 'error';
223
+
224
+ $extra_data = array(
225
+ 'error' => 'optimization',
226
+ 'error_msg' => $error_msg,
227
+ );
228
+
229
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
230
+
231
+ $optimization_data->configure( $results );
232
+ $optimization_data->save();
233
+
234
+ WRIO_Logger::error( sprintf( 'Failed to process (url: %s, path: %s, quality: %s) as error was returned: %s', $this->get( 'url' ), $this->get( 'path' ), $image_processor->quality( $optimization_level ), $error_msg ) );
235
+
236
+ return $optimize_results;
237
+ }
238
+
239
+ $results['original_mime_type'] = '';
240
+ $results['final_mime_type'] = '';
241
+
242
+ // отложенная оптимизация
243
+ if ( isset( $optimized_img_data['status'] ) && $optimized_img_data['status'] == 'processing' ) {
244
+ $results['result_status'] = 'processing';
245
+ $results['original_size'] = 0;
246
+ $results['final_size'] = 0;
247
+
248
+ $extra_data = array(
249
+ 'main_optimized_data' => $optimized_img_data,
250
+ 'thumbnails_optimized_data' => $this->optimizeImageSizes(),
251
+ );
252
+
253
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
254
+
255
+ $optimization_data->configure( $results );
256
+ $optimization_data->save();
257
+ $optimize_results['processing'] = 1;
258
+
259
+ return $optimize_results;
260
+ }
261
+
262
+ //скачиваем и заменяем главную картинку
263
+ $image_downloaded = $this->replaceOriginalFile( $optimized_img_data );
264
+
265
+ // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
266
+ if ( ! $optimized_img_data['optimized_size'] ) {
267
+ clearstatcache();
268
+ $optimized_img_data['optimized_size'] = filesize( $this->get( 'path' ) );
269
+ }
270
+
271
+ // при отрицательной оптимизации ставим значение оригинала
272
+ if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
273
+ $optimized_img_data['optimized_size'] = $original_main_size;
274
+ }
275
+
276
+ if ( $image_downloaded ) {
277
+ //просчитываем статистику
278
+ $optimize_results['original_size'] += $original_main_size;
279
+ $optimize_results['optimized_size'] += $optimized_img_data['optimized_size'];
280
+ $thumbnails_count = 0;
281
+
282
+ // оптимизируем дополнительные размеры
283
+ $optimized_img_sizes_data = $this->optimizeImageSizes();
284
+
285
+ // добавляем к статистике данные по оптимизации доп размеров
286
+ if ( ! empty( $optimized_img_sizes_data ) ) {
287
+ $optimize_results['original_size'] += $optimized_img_sizes_data['original_size'];
288
+ $optimize_results['optimized_size'] += $optimized_img_sizes_data['optimized_size'];
289
+ $thumbnails_count = $optimized_img_sizes_data['thumbnails_count'];
290
+ }
291
+
292
+ $results['result_status'] = 'success';
293
+ $results['final_size'] = $optimize_results['optimized_size'];
294
+ $results['original_size'] = $optimize_results['original_size'];
295
+
296
+ $extra_data = array(
297
+ 'thumbnails_count' => $thumbnails_count,
298
+ 'original_main_size' => $original_main_size,
299
+ );
300
+
301
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
302
+ $mime_type = '';
303
+
304
+ if ( function_exists( 'wp_get_image_mime' ) ) {
305
+ $mime_type = wp_get_image_mime( $this->get( 'path' ) );
306
+ } else {
307
+ WRIO_Logger::error( 'App is missing wp_get_image_mime() function, unable to get MIME type' );
308
+ }
309
+
310
+ $results['original_mime_type'] = $mime_type;
311
+ $results['final_mime_type'] = $mime_type;
312
+ $optimization_data->configure( $results );
313
+ } else {
314
+ $error_msg = 'Failed to get optimized image from remote server';
315
+ $this->writeLog( $error_msg );
316
+
317
+ $results['result_status'] = 'error';
318
+
319
+ $extra_data = array(
320
+ 'error' => 'download',
321
+ 'error_msg' => $error_msg,
322
+ );
323
+
324
+ $results['extra_data'] = new RIO_Attachment_Extra_Data( $extra_data );
325
+ $optimization_data->configure( $results );
326
+ }
327
+
328
+ $optimization_data->save();
329
+
330
+ return $optimize_results;
331
+ }
332
+
333
+ /**
334
+ * Отложенная оптимизация аттачмента
335
+ *
336
+ * @return bool|array
337
+ */
338
+ public function deferredOptimization() {
339
+ $results = array(
340
+ 'original_size' => 0,
341
+ 'optimized_size' => 0,
342
+ 'optimized_count' => 0,
343
+ 'processing' => 1,
344
+ );
345
+
346
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
347
+ $optimization_data = $this->getOptimizationData();
348
+
349
+ if ( $optimization_data->get_result_status() != 'processing' ) {
350
+ return false;
351
+ }
352
+
353
+ // проверяем главную картинку
354
+ /**
355
+ * @var RIO_Attachment_Extra_Data $extra_data
356
+ */
357
+ $extra_data = $optimization_data->get_extra_data();
358
+ $main_optimized_data = $extra_data->get_main_optimized_data();
359
+ $main_image_url = '';
360
+
361
+ if ( ! $main_optimized_data['optimized_img_url'] ) {
362
+ $main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
363
+ if ( $main_image_url ) {
364
+ $main_optimized_data['optimized_img_url'] = $main_image_url;
365
+ $extra_data->set_main_optimized_data( $main_optimized_data );
366
+ }
367
+ }
368
+
369
+ $thumbnails_processed = true;
370
+ $thumbnails = (array) $extra_data->get_thumbnails_optimized_data();
371
+ $thumbnails = json_decode( json_encode( $thumbnails ), true ); // рекурсивная конвертация объекта в массив
372
+
373
+ if ( is_array( $thumbnails['thumbnails'] ) ) {
374
+ foreach ( $thumbnails['thumbnails'] as &$thumbnail_optimized_data ) {
375
+ if ( ! $thumbnail_optimized_data['optimized_img_url'] ) {
376
+ $thumbnail_image_url = $image_processor->checkDeferredOptimization( $thumbnail_optimized_data );
377
+ if ( $thumbnail_image_url ) {
378
+ $thumbnail_optimized_data['optimized_img_url'] = $thumbnail_image_url;
379
+ } else {
380
+ $thumbnails_processed = false;
381
+ }
382
+ }
383
+ }
384
+ $extra_data->set_thumbnails_optimized_data( $thumbnails );
385
+ }
386
+
387
+ // когда все файлы получены - сохраняем и возвращаем результат
388
+ if ( $main_image_url && $thumbnails_processed ) {
389
+ $original_size = 0;
390
+ $optimized_size = 0;
391
+ $thumbnails_count = 0;
392
+ $original_main_size = filesize( $this->get( 'path' ) );
393
+ $original_size = $original_size + $original_main_size;
394
+
395
+ $this->replaceOriginalFile( array(
396
+ 'optimized_img_url' => $main_image_url,
397
+ ) );
398
+
399
+ clearstatcache();
400
+
401
+ $optimized_main_size = filesize( $this->get( 'path' ) );
402
+
403
+ // при отрицательной оптимизации ставим значение оригинала
404
+ if ( $optimized_main_size > $original_main_size ) {
405
+ $optimized_main_size = $original_main_size;
406
+ }
407
+
408
+ $optimized_size = $optimized_size + $optimized_main_size;
409
+
410
+ if ( is_array( $thumbnails['thumbnails'] ) ) {
411
+ foreach ( $thumbnails['thumbnails'] as $thumbnail_size => $thumbnail ) {
412
+ $thumbnail_file = $this->getImageSizePath( $thumbnail_size );
413
+ $original_thumbnail_size = filesize( $thumbnail_file );
414
+ $original_size = $original_size + $original_thumbnail_size;
415
+
416
+ $this->replaceOriginalFile( array(
417
+ 'optimized_img_url' => $thumbnail['optimized_img_url'],
418
+ ), $thumbnail_size );
419
+
420
+ clearstatcache();
421
+
422
+ $optimized_thumbnail_size = filesize( $thumbnail_file );
423
+
424
+ // при отрицательной оптимизации ставим значение оригинала
425
+ if ( $optimized_thumbnail_size > $original_thumbnail_size ) {
426
+ $optimized_thumbnail_size = $original_thumbnail_size;
427
+ }
428
+
429
+ $optimized_size = $optimized_size + $optimized_thumbnail_size;
430
+
431
+ $thumbnails_count ++;
432
+ }
433
+ }
434
+
435
+ $mime_type = '';
436
+ if ( function_exists( 'wp_get_image_mime' ) ) {
437
+ $mime_type = wp_get_image_mime( $this->get( 'path' ) );
438
+ }
439
+
440
+ $optimization_data->configure( array(
441
+ 'final_size' => $optimized_size,
442
+ 'original_size' => $original_size,
443
+ 'result_status' => 'success',
444
+ 'original_mime_type' => $mime_type,
445
+ 'final_mime_type' => $mime_type,
446
+ ) );
447
+
448
+ $extra_data->set_original_main_size( $original_main_size );
449
+ $extra_data->set_thumbnails_count( $thumbnails_count );
450
+
451
+ // удаляем промежуточные данные
452
+ $extra_data->set_main_optimized_data( null );
453
+ $extra_data->set_thumbnails_optimized_data( null );
454
+ $extra_data->set_main_optimized_data( null );
455
+
456
+ $results['optimized_count'] = 1;
457
+ $results['original_size'] = $original_size;
458
+ $results['optimized_size'] = $optimized_size;
459
+
460
+ unset( $results['processing'] );
461
+ }
462
+ $optimization_data->set_extra_data( $extra_data );
463
+ $optimization_data->save();
464
+
465
+ return $results;
466
+ }
467
+
468
+ /**
469
+ * Метод проверяет, оптимизирован ли аттачмент
470
+ *
471
+ * @return bool
472
+ */
473
+ public function isOptimized() {
474
+ $optimization_data = $this->getOptimizationData();
475
+ if ( $optimization_data->is_optimized() ) {
476
+ return true;
477
+ }
478
+
479
+ return false;
480
+ }
481
+
482
+ /**
483
+ * Возвращает все размеры аттачмента, которые нужно оптимизировать
484
+ *
485
+ * @return array|false
486
+ */
487
+ public function getAllowedSizes() {
488
+ $allowed_sizes = WRIO_Plugin::app()->getPopulateOption( 'allowed_sizes_thumbnail', 'thumbnail,medium' );
489
+
490
+ if ( ! $allowed_sizes ) {
491
+ return false;
492
+ }
493
+
494
+ $allowed_sizes = explode( ',', $allowed_sizes );
495
+
496
+ return $allowed_sizes;
497
+ }
498
+
499
+ /**
500
+ * Оптимизация других размеров аттачмента.
501
+ *
502
+ * @return array
503
+ */
504
+ public function optimizeImageSizes() {
505
+ $allowed_sizes = $this->getAllowedSizes();
506
+
507
+ if ( $allowed_sizes === false ) {
508
+ return array();
509
+ }
510
+
511
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
512
+ $quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
513
+
514
+ if ( $quality == 'custom' ) {
515
+ $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
516
+ $quality = intval( $custom_quality );
517
+ }
518
+
519
+ $exif = WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false );
520
+
521
+ $original_size = 0;
522
+ $optimized_size = 0;
523
+ $errors_count = 0;
524
+ $optimized_count = 0;
525
+ $thumbnails = array();
526
+
527
+ foreach ( $allowed_sizes as $image_size ) {
528
+ $url = $this->getImageSizeUrl( $image_size );
529
+ $path = $this->getImageSizePath( $image_size );
530
+
531
+ if ( ! $url || ! $path ) {
532
+ continue;
533
+ }
534
+
535
+ $original_file_size = 0;
536
+
537
+ if ( is_file( $path ) ) {
538
+ $original_file_size = filesize( $path );
539
+ }
540
+
541
+ $optimized_img_data = $image_processor->process( array(
542
+ 'image_url' => $url,
543
+ 'image_path' => $path,
544
+ 'quality' => $image_processor->quality( $quality ),
545
+ 'save_exif' => $exif,
546
+ ) );
547
+ // проверяем на ошибку
548
+ if ( is_wp_error( $optimized_img_data ) ) {
549
+ $errors_count ++;
550
+ } else {
551
+ //скачиваем и заменяем картинку
552
+ $this->replaceOriginalFile( $optimized_img_data, $image_size );
553
+ // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
554
+ if ( ! $optimized_img_data['optimized_size'] ) {
555
+ clearstatcache();
556
+ $optimized_img_data['optimized_size'] = filesize( $path );
557
+ }
558
+ if ( ! $optimized_img_data['src_size'] ) {
559
+ $optimized_img_data['src_size'] = $original_file_size;
560
+ }
561
+
562
+ // при отрицательной оптимизации ставим значение оригинала
563
+ if ( $optimized_img_data['optimized_size'] > $original_file_size ) {
564
+ $optimized_img_data['optimized_size'] = $original_file_size;
565
+ }
566
+
567
+ $thumbnails[ $image_size ] = $optimized_img_data;
568
+
569
+ //просчитываем статистику
570
+ $original_size += $optimized_img_data['src_size'];
571
+ $optimized_size += $optimized_img_data['optimized_size'];
572
+ $optimized_count ++;
573
+ }
574
+ }
575
+
576
+ return array(
577
+ 'errors_count' => $errors_count,
578
+ 'original_size' => $original_size,
579
+ 'optimized_size' => $optimized_size,
580
+ 'thumbnails_count' => $optimized_count,
581
+ 'thumbnails' => $thumbnails,
582
+ );
583
+ }
584
+
585
+ /**
586
+ * Возвращает путь.
587
+ *
588
+ * @param string $image_size Размер(thumbnail, medium ... )
589
+ *
590
+ * @return string
591
+ */
592
+ public function getPath( $image_size = '' ) {
593
+
594
+ if ( empty( $image_size ) ) {
595
+ $path = $this->path;
596
+ } else {
597
+ $path = $this->getImageSizePath( $image_size );
598
+ }
599
+
600
+ return $path;
601
+ }
602
+
603
+ /**
604
+ * Заменяет оригинальный файл на оптимизированный.
605
+ *
606
+ * @param array $optimized_img_data Hезультат оптимизации ввиде массива данных.
607
+ * @param string $image_size Размер (thumbnail, medium ... )
608
+ *
609
+ * @return bool
610
+ */
611
+ public function replaceOriginalFile( $optimized_img_data, $image_size = '' ) {
612
+
613
+ $optimized_img_url = $optimized_img_data['optimized_img_url'];
614
+
615
+ if ( isset( $optimized_img_data['not_need_download'] ) && (bool) $optimized_img_data['not_need_download'] ) {
616
+ $optimized_file = $optimized_img_url;
617
+ } else {
618
+ $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
619
+ }
620
+
621
+ if ( empty( $optimized_file ) ) {
622
+ WRIO_Logger::error( sprintf( 'Unable to replace original image with new as failed to download %s', $optimized_img_url ) );
623
+
624
+ return false;
625
+ }
626
+
627
+ if ( isset( $optimized_img_data['not_need_replace'] ) && $optimized_img_data['not_need_replace'] ) {
628
+ // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
629
+ return true;
630
+ }
631
+
632
+ $attachment_size_path = $this->getPath( $image_size );
633
+
634
+ if ( ! is_file( $attachment_size_path ) ) {
635
+ return false;
636
+ }
637
+
638
+ $bytes = @file_put_contents( $attachment_size_path, $optimized_file );
639
+
640
+ if ( $bytes === false ) {
641
+ WRIO_Logger::error( sprintf( 'Failed to put new image\'s %s content to %s as file_put_contents() failed', $optimized_img_url, $attachment_size_path ) );
642
+
643
+ return false;
644
+ }
645
+
646
+ return true;
647
+ }
648
+
649
+ /**
650
+ * Скачивание изображения с удалённого сервера
651
+ *
652
+ * @param string $url
653
+ *
654
+ * @return string|null Image content on success, NULL on failure.
655
+ */
656
+ protected function remoteDownloadImage( $url ) {
657
+
658
+ if ( ! function_exists( 'curl_version' ) ) {
659
+ $content = @file_get_contents( $url );
660
+
661
+ if ( $content === false ) {
662
+ WRIO_Logger::error( sprintf( 'Failed to get content of "%s" using file_get_contents()', $url ) );
663
+
664
+ return null;
665
+ }
666
+
667
+ return $content;
668
+ }
669
+
670
+ $ch = curl_init();
671
+ curl_setopt( $ch, CURLOPT_HEADER, 0 );
672
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
673
+ curl_setopt( $ch, CURLOPT_URL, $url );
674
+
675
+ $image_body = curl_exec( $ch );
676
+ $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
677
+ if ( $http_code != '200' ) {
678
+ $image_body = false;
679
+ }
680
+ curl_close( $ch );
681
+
682
+ if ( $image_body === false ) {
683
+ WRIO_Logger::error( sprintf( 'Failed to get content of "%s" using curl_exec()', $url ) );
684
+
685
+ return null;
686
+ }
687
+
688
+ return $image_body;
689
+ }
690
+
691
+ /**
692
+ * Возвращает свойство аттачмента
693
+ *
694
+ * @param string $property имя свойства
695
+ *
696
+ * @return mixed
697
+ */
698
+ public function get( $property ) {
699
+ if ( isset( $this->$property ) ) {
700
+ return $this->$property;
701
+ }
702
+
703
+ return false;
704
+ }
705
+
706
+ /**
707
+ * Возвращает URL изображения по указанному размеру
708
+ *
709
+ * @param string $size - размер изображения(thumbnail,medium,large...)
710
+ *
711
+ * @return string|null
712
+ */
713
+ public function getImageSizeUrl( $size = 'thumbnail' ) {
714
+ if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
715
+ return null;
716
+ }
717
+
718
+ $file = $this->attachment_meta['sizes'][ $size ]['file'];
719
+ $url = str_replace( wp_basename( $this->url ), $file, $this->url );
720
+
721
+ return $url;
722
+ }
723
+
724
+ /**
725
+ * Возвращает путь к изображению по указанному размеру.
726
+ *
727
+ * @param string $size Размер изображения (thumbnail, medium, large ...)
728
+ *
729
+ * @return string Путь до изображения.
730
+ */
731
+ public function getImageSizePath( $size = 'thumbnail' ) {
732
+ if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
733
+ return null;
734
+ }
735
+
736
+ $file = $this->attachment_meta['sizes'][ $size ]['file'];
737
+ $path = str_replace( wp_basename( $this->path ), $file, $this->path );
738
+
739
+ return $path;
740
+ }
741
+
742
+ /**
743
+ * Проверка необходимости делать изменение размера.
744
+ *
745
+ * @return bool
746
+ */
747
+ protected function isNeedResize() {
748
+ $resize_large_images = WRIO_Plugin::app()->getPopulateOption( 'resize_larger', true );
749
+
750
+ if ( ! $resize_large_images ) {
751
+ return false;
752
+ }
753
+
754
+ $resize_larger_w = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_w', 1600 );
755
+ $resize_larger_h = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_h', 1600 );
756
+
757
+ if ( ! $resize_larger_w && ! $resize_larger_h ) {
758
+ return false;
759
+ }
760
+
761
+ // если ширина и высота установлены и > 0
762
+ if ( $this->attachment_meta['width'] >= $this->attachment_meta['height'] ) {
763
+ $larger_side = $this->attachment_meta['width'];
764
+ $resize_larger_side = $resize_larger_w;
765
+ } else {
766
+ $larger_side = $this->attachment_meta['height'];
767
+ $resize_larger_side = $resize_larger_h;
768
+ }
769
+ // если ширина 0, то рисайзим по высоте
770
+ if ( ! $resize_larger_w ) {
771
+ $resize_larger_side = $resize_larger_h;
772
+ $larger_side = $this->attachment_meta['height'];
773
+ }
774
+ // если высота 0, то рисайзим по ширине
775
+ if ( ! $resize_larger_h ) {
776
+ $resize_larger_side = $resize_larger_w;
777
+ $larger_side = $this->attachment_meta['width'];
778
+ }
779
+ // если большая сторона картинки меньше, чем задано в настройках, то не рисайзим.
780
+ if ( $larger_side <= $resize_larger_side ) {
781
+ return false;
782
+ }
783
+
784
+ return true;
785
+ }
786
+
787
+ /**
788
+ * Возвращает метаданные аттачмента
789
+ *
790
+ * @return array
791
+ */
792
+ public function getMetaData() {
793
+ return $this->attachment_meta;
794
+ }
795
+
796
+ /**
797
+ * Изменяет размер изображения до заданного в настройках размера.
798
+ *
799
+ * @return bool
800
+ */
801
+ protected function resize() {
802
+ $resize_larger_h = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_h', 1600 );
803
+ $resize_larger_w = (int) WRIO_Plugin::app()->getPopulateOption( 'resize_larger_w', 1600 );
804
+
805
+ $image = wp_get_image_editor( $this->path );
806
+
807
+ if ( is_wp_error( $image ) ) {
808
+ WRIO_Logger::error( sprintf( 'Failed to get image edit via wp_get_image_editor(), error: "%s"', $image->get_error_message() ) );
809
+
810
+ return false;
811
+ }
812
+
813
+ $current_size = $image->get_size();
814
+ $new_width = 0;
815
+ $new_height = 0;
816
+
817
+ // если обе стороны заданы
818
+ if ( $resize_larger_h && $resize_larger_w ) {
819
+ // определяем большую сторону и по ней маштабируем
820
+ if ( $current_size['width'] >= $current_size['height'] ) {
821
+ $new_width = $resize_larger_w;
822
+ $new_height = round( $current_size['height'] * $new_width / $current_size['width'] );
823
+ } else {
824
+ $new_height = $resize_larger_h;
825
+ $new_width = round( $current_size['width'] * $new_height / $current_size['height'] );
826
+ }
827
+ } else {
828
+ // если задана одна из сторон
829
+ if ( ! $resize_larger_w ) {
830
+ // если ширина 0, то рисайзим по высоте
831
+ $new_height = $resize_larger_h;
832
+ $new_width = round( $current_size['width'] * $new_height / $current_size['height'] );
833
+ }
834
+ if ( ! $resize_larger_h ) {
835
+ // если высота 0, то рисайзим по ширине
836
+ $new_width = $resize_larger_w;
837
+ $new_height = round( $current_size['height'] * $new_width / $current_size['width'] );
838
+ }
839
+ }
840
+
841
+ $nl = PHP_EOL;
842
+ $log_message = sprintf( "\tResize from: %sx%s to %sx%s", $current_size['width'], $current_size['height'], $new_width, $new_height ) . $nl;
843
+ $log_message .= sprintf( "\tLarger resize from %sx%s", $resize_larger_w, $resize_larger_h ) . $nl;
844
+ $log_message .= sprintf( "\tAbsolute path: %s", $this->path ) . $nl;
845
+
846
+ $resize_result = $image->resize( $new_width, $new_height, false );
847
+
848
+ if ( is_wp_error( $resize_result ) ) {
849
+ $this->writeLog( sprintf( 'Resize error: %s. Details: %s', $resize_result->get_error_messages(), $log_message ) );
850
+
851
+ return false;
852
+ }
853
+
854
+ $save_result = $image->save( $this->path );
855
+
856
+ if ( is_wp_error( $save_result ) ) {
857
+ $this->writeLog( sprintf( 'Failed to save resized error in db: %s, Details: %s', $save_result->get_error_messages(), $log_message ) );
858
+
859
+ return false;
860
+ }
861
+
862
+ $this->attachment_meta['width'] = $new_width;
863
+ $this->attachment_meta['height'] = $new_height;
864
+ $this->attachment_meta['old_width'] = $current_size['width'];
865
+ $this->attachment_meta['old_height'] = $current_size['height'];
866
+
867
+ wp_update_attachment_metadata( $this->id, $this->attachment_meta );
868
+
869
+ return true;
870
+ }
871
+
872
+ /**
873
+ * Делает резервную копию
874
+ *
875
+ * @return true|WP_Error
876
+ */
877
+ protected function backup() {
878
+ $backup = WIO_Backup::get_instance();
879
+
880
+ return $backup->backupAttachment( $this );
881
+ }
882
+
883
+ /**
884
+ * Восстанавливает файлы из резервной копии
885
+ *
886
+ * @return true|WP_Error
887
+ */
888
+ public function restore() {
889
+ $backup = WIO_Backup::get_instance();
890
+
891
+ return $backup->restoreAttachment( $this );
892
+ }
893
+ }
includes/classes/class-rio-backup.php ADDED
@@ -0,0 +1,537 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с резервным копированием изображений.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_Backup {
16
+
17
+ CONST BACKUP_DIR_NAME = 'wio_backup';
18
+ CONST TEMP_DIR_NAME = 'temp';
19
+
20
+ /**
21
+ * The single instance of the class.
22
+ *
23
+ * @since 1.3.0
24
+ * @access protected
25
+ * @var object
26
+ */
27
+ protected static $_instance;
28
+
29
+ /**
30
+ * @var array Данные о папке uploads, возвращаемые функцией wp_upload_dir()
31
+ */
32
+ protected $wp_upload_dir;
33
+
34
+ /**
35
+ * @var string Путь к папке с резервными копиями изображений
36
+ */
37
+ private $backup_dir;
38
+
39
+ /**
40
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
41
+ * @since 1.3.0
42
+ * @var string
43
+ */
44
+ private $blog_backup_dir;
45
+
46
+ /**
47
+ * Инициализация бекапа
48
+ */
49
+ public function __construct() {
50
+ $this->wp_upload_dir = wp_upload_dir();
51
+ }
52
+
53
+ /**
54
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
55
+ * @since 1.3.0
56
+ *
57
+ * @return object|\static object Main instance.
58
+ */
59
+ public static function get_instance() {
60
+ if ( ! isset( static::$_instance ) ) {
61
+ static::$_instance = new static();
62
+ }
63
+
64
+ return static::$_instance;
65
+ }
66
+
67
+ /**
68
+ * Проверка возможности записи в папку uploads.
69
+ *
70
+ * @return bool
71
+ */
72
+ public function isUploadWritable() {
73
+ $upload_dir = $this->wp_upload_dir['basedir'];
74
+
75
+ if ( is_dir( $upload_dir ) && wp_is_writable( $upload_dir ) ) {
76
+ return true;
77
+ }
78
+
79
+ return false;
80
+ }
81
+
82
+ /**
83
+ * Проверка возможности записи в папку бекап.
84
+ *
85
+ * @return bool
86
+ */
87
+ public function isBackupWritable() {
88
+
89
+ $backup_dir = $this->getBackupDir();
90
+
91
+ if ( is_wp_error( $backup_dir ) || ! wp_is_writable( $backup_dir ) ) {
92
+ return false;
93
+ }
94
+
95
+ return true;
96
+ }
97
+
98
+ /**
99
+ * Путь к папке с бекапами
100
+ *
101
+ * @return string|WP_Error
102
+ */
103
+ public function getBackupDir() {
104
+ if ( $this->backup_dir ) {
105
+ return $this->backup_dir;
106
+ }
107
+
108
+ $backup_dir = wp_normalize_path( trailingslashit( $this->wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
109
+
110
+ if ( ! is_dir( $backup_dir ) ) {
111
+ $backup_dir = $this->mkdir( $backup_dir );
112
+
113
+ if ( is_wp_error( $backup_dir ) ) {
114
+ return $backup_dir;
115
+ }
116
+ }
117
+
118
+ $this->backup_dir = trailingslashit( $backup_dir );
119
+
120
+ return $this->backup_dir;
121
+ }
122
+
123
+ /**
124
+ * Путь к папке с бекапами блога.
125
+ *
126
+ * Используется в мультисайт режиме.
127
+ *
128
+ * @return string|WP_Error
129
+ */
130
+ public function getBlogBackupDir() {
131
+ if ( $this->blog_backup_dir ) {
132
+ return $this->blog_backup_dir;
133
+ }
134
+
135
+ $wp_upload_dir = wp_upload_dir();
136
+ $backup_dir = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . self::BACKUP_DIR_NAME );
137
+
138
+ if ( ! is_dir( $backup_dir ) ) {
139
+ $backup_dir = $this->mkdir( $backup_dir );
140
+
141
+ if ( is_wp_error( $backup_dir ) ) {
142
+ return $backup_dir;
143
+ }
144
+ }
145
+
146
+ $this->blog_backup_dir = trailingslashit( $backup_dir );
147
+
148
+ return $this->blog_backup_dir;
149
+ }
150
+
151
+ /**
152
+ * Очищает папку с резервными копиями
153
+ *
154
+ * @return bool
155
+ */
156
+ public function removeBackupDir() {
157
+ $backup_dir = $this->getBackupDir();
158
+
159
+ return wrio_rmdir( $backup_dir );
160
+ }
161
+
162
+ /**
163
+ * Очищает папку с резервными копиями блога
164
+ * Используется в мультисайт режиме
165
+ *
166
+ * @return bool
167
+ */
168
+ public function removeBlogBackupDir() {
169
+ $backup_dir = $this->getBlogBackupDir();
170
+
171
+ return wrio_rmdir( $backup_dir );
172
+ }
173
+
174
+ /**
175
+ * Получает путь к папке с резервными копиями
176
+ *
177
+ * @param array $attachment_meta метаданные аттачмента
178
+ *
179
+ * @return string
180
+ */
181
+ public function getAttachmentBackupDir( $attachment_meta ) {
182
+ $backup_dir = $this->getBackupDir();
183
+
184
+ // Get all subfolders in which the image is stored.
185
+ // This is necessary to create an alternate subfolders
186
+ // in directory where they are stored in backups.
187
+ $subfolders = dirname( $attachment_meta['file'] );
188
+
189
+ $backup_dir .= $subfolders;
190
+
191
+ if ( ! is_dir( $backup_dir ) ) {
192
+ $backup_dir = $this->mkdir( $backup_dir );
193
+
194
+ if ( is_wp_error( $backup_dir ) ) {
195
+ return $backup_dir;
196
+ }
197
+ }
198
+
199
+ return trailingslashit( $backup_dir );
200
+ }
201
+
202
+ /**
203
+ * Делаем резервную копию аттачмента
204
+ *
205
+ * @param WIO_Attachment $wio_attachment аттачмент
206
+ *
207
+ * @return bool|WP_Error
208
+ */
209
+ public function backupAttachment( WIO_Attachment $wio_attachment ) {
210
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
211
+
212
+ if ( ! $backup_origin_images ) {
213
+ return false; // если бекап не требуется
214
+ }
215
+
216
+ $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
217
+
218
+ if ( is_wp_error( $backup_dir ) ) {
219
+ return $backup_dir;
220
+ }
221
+
222
+ $full = $this->backupAttachmentSize( $wio_attachment );
223
+
224
+ if ( is_wp_error( $full ) ) {
225
+ return $full;
226
+ }
227
+
228
+ $allowed_sizes = $wio_attachment->getAllowedSizes();
229
+
230
+ if ( ! empty( $allowed_sizes ) ) {
231
+ foreach ( (array) $allowed_sizes as $image_size ) {
232
+ $size_backup = $this->backupAttachmentSize( $wio_attachment, $image_size );
233
+
234
+ if ( is_wp_error( $size_backup ) ) {
235
+ return $size_backup;
236
+ }
237
+ }
238
+ }
239
+
240
+ return true;
241
+ }
242
+
243
+ /**
244
+ * Восстанавливаем аттачмент из резервной копии
245
+ *
246
+ * @param WIO_Attachment $wio_attachment аттачмент
247
+ *
248
+ * @return bool|WP_Error
249
+ */
250
+ public function restoreAttachment( WIO_Attachment $wio_attachment ) {
251
+ $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
252
+
253
+ if ( is_wp_error( $backup_dir ) ) {
254
+ return $backup_dir;
255
+ }
256
+
257
+ $restore_result = $this->restoreAttachmentSize( $wio_attachment );
258
+
259
+ if ( is_wp_error( $restore_result ) ) {
260
+ return $restore_result;
261
+ }
262
+
263
+ $attachment_meta = wp_get_attachment_metadata( $wio_attachment->get( 'id' ) );
264
+
265
+ if ( isset( $attachment_meta['old_width'] ) && isset( $attachment_meta['old_width'] ) ) {
266
+ $attachment_meta['width'] = $attachment_meta['old_width'];
267
+ $attachment_meta['height'] = $attachment_meta['old_height'];
268
+ wp_update_attachment_metadata( $wio_attachment->get( 'id' ), $attachment_meta );
269
+ }
270
+
271
+ $allowed_sizes = $wio_attachment->getAllowedSizes();
272
+
273
+ if ( $allowed_sizes ) {
274
+ foreach ( $allowed_sizes as $image_size ) {
275
+ $this->restoreAttachmentSize( $wio_attachment, $image_size );
276
+ }
277
+ }
278
+
279
+ return true;
280
+ }
281
+
282
+ /**
283
+ * Создает временное изображение с уникальным именем.
284
+ *
285
+ * Необходимо для провайдеров, который кешируют изображения по имени файла,
286
+ * чтобы сбросить кеш, нужно отдать провайдеру изображение с другим именем.
287
+ *
288
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>
289
+ * @since 1.1.2
290
+ *
291
+ * @param string $file_path путь к изображению
292
+ *
293
+ * @return array|WP_Error
294
+ */
295
+ public function createTempAttachment( $file_path ) {
296
+ if ( $this->isBackupWritable() ) {
297
+
298
+ $temp_dir = $this->getBackupDir() . self::TEMP_DIR_NAME . '/';
299
+ $temp_dir_url = trailingslashit( $this->wp_upload_dir['baseurl'] ) . self::BACKUP_DIR_NAME . '/' . self::TEMP_DIR_NAME . '/';
300
+
301
+ if ( ! is_dir( $temp_dir ) ) {
302
+ $temp_dir = $this->mkdir( $temp_dir );
303
+
304
+ if ( is_wp_error( $temp_dir ) ) {
305
+ return $temp_dir;
306
+ }
307
+ }
308
+
309
+ $temp_file_id = uniqid();
310
+ $file_name = pathinfo( $file_path, PATHINFO_FILENAME );
311
+ $file_extension = pathinfo( $file_path, PATHINFO_EXTENSION );
312
+ $new_file_name = $temp_file_id . '_' . md5( $file_name ) . '.' . $file_extension;
313
+
314
+ $temp_file_path = $temp_dir . $new_file_name;
315
+ $temp_file_url = $temp_dir_url . $new_file_name;
316
+
317
+ if ( is_file( $file_path ) ) {
318
+ if ( ! @copy( $file_path, $temp_file_path ) ) {
319
+ WRIO_Logger::error( sprintf( 'Failed to swap original file %s with %s as copy() failed.', $temp_file_path, $file_path ) );
320
+
321
+ return new WP_Error( 'copy_file_to_temp_dir_error', __( 'Could not copy the file to the temporary directory', 'robin-image-optimizer' ) );
322
+ }
323
+ }
324
+
325
+ WRIO_Logger::info( sprintf( 'Creation of temporary attachment (%s) successfully completed!', $file_path ) );
326
+
327
+ return [
328
+ 'id' => $temp_file_id,
329
+ 'image_path' => $temp_file_path,
330
+ 'image_url' => $temp_file_url,
331
+ ];
332
+ }
333
+
334
+ 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' ) );
335
+ }
336
+
337
+ /**
338
+ * Резервное копирование файла аттачмента.
339
+ *
340
+ * @param WIO_Attachment $wio_attachment аттачмент
341
+ * @param string $image_size Размер(thumbnail, medium ... )
342
+ *
343
+ * @return bool|WP_Error
344
+ */
345
+ protected function backupAttachmentSize( WIO_Attachment $wio_attachment, $image_size = '' ) {
346
+ if ( $image_size ) {
347
+ $original_file = $wio_attachment->getImageSizePath( $image_size );
348
+ } else {
349
+ $original_file = $wio_attachment->get( 'path' );
350
+ }
351
+
352
+ $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
353
+
354
+ // проверить запись в папку
355
+ if ( is_wp_error( $backup_dir ) ) {
356
+ WRIO_Logger::error( sprintf( 'Failed to create backup dir, error: %s', $backup_dir->get_error_message() ) );
357
+
358
+ return $backup_dir;
359
+ }
360
+
361
+ if ( ! $original_file ) {
362
+ // бывает такое, что размера превьюшки нет в базе данных.
363
+ // это не считается ошибкой, поэтому сразу пропускаем
364
+ return false;
365
+ }
366
+
367
+ $backup_file = $backup_dir . wp_basename( $original_file );
368
+
369
+ if ( is_file( $original_file ) ) {
370
+ if ( ! @copy( $original_file, $backup_file ) ) {
371
+ WRIO_Logger::error( sprintf( 'Failed to copy %s to %s as copy() failed', $original_file, $backup_file ) );
372
+ }
373
+ }
374
+
375
+ return true;
376
+ }
377
+
378
+ /**
379
+ * Восстановление файла аттачмента из резервной копии
380
+ *
381
+ * @param WIO_Attachment $wio_attachment аттачмент
382
+ * @param string|null $image_size Размер(thumbnail, medium ... )
383
+ *
384
+ * @return bool|WP_Error
385
+ */
386
+ protected function restoreAttachmentSize( WIO_Attachment $wio_attachment, $image_size = null ) {
387
+
388
+ if ( ! empty( $image_size ) ) {
389
+ $original_file = $wio_attachment->getImageSizePath( $image_size );
390
+ } else {
391
+ $original_file = $wio_attachment->get( 'path' );
392
+ }
393
+
394
+ $backup_dir = $this->getAttachmentBackupDir( $wio_attachment->get( 'attachment_meta' ) );
395
+
396
+ if ( is_wp_error( $backup_dir ) ) {
397
+ return $backup_dir;
398
+ }
399
+
400
+ if ( empty( $original_file ) ) {
401
+ return false;
402
+ }
403
+
404
+ $backup_file = $backup_dir . wp_basename( $original_file );
405
+
406
+ if ( ! is_file( $backup_file ) ) {
407
+ WRIO_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 ) );
408
+
409
+ return false;
410
+ }
411
+
412
+ if ( ! @copy( $backup_file, $original_file ) ) {
413
+ WRIO_Logger::error( sprintf( 'Failed to swap %s with %s as copy() failed', $backup_file, $original_file ) );
414
+
415
+ return false;
416
+ }
417
+
418
+ if ( ! @unlink( $backup_file ) ) {
419
+ WRIO_Logger::error( sprintf( 'Failed to delete backup file %s as unlink() failed', $backup_file ) );
420
+
421
+ return false;
422
+ }
423
+
424
+ WRIO_Logger::info( sprintf( 'Restored file: %s.', $backup_file ) );
425
+
426
+ return true;
427
+ }
428
+
429
+ /**
430
+ * Удаляем резервные копии аттачмента
431
+ *
432
+ * @param int $attachment_id аттачмент id
433
+ *
434
+ * @return bool|WP_Error
435
+ */
436
+ public function removeAttachmentBackup( $attachment_id ) {
437
+ $attachment_meta = wp_get_attachment_metadata( $attachment_id );
438
+ $backup_dir = $this->getAttachmentBackupDir( $attachment_meta );
439
+
440
+ if ( is_wp_error( $backup_dir ) ) {
441
+ return $backup_dir;
442
+ }
443
+
444
+ $main_file_path = $backup_dir . wp_basename( $attachment_meta['file'] );
445
+
446
+ if ( ! file_exists( $main_file_path ) ) {
447
+ WRIO_Logger::error( sprintf( "Failed to remove an attachment file. File (%s) isn't exists. Attachment #%s", $main_file_path, $attachment_id ) );
448
+ }
449
+
450
+ if ( ! @unlink( $main_file_path ) ) {
451
+ WRIO_Logger::error( sprintf( 'Failed to unlink a main file (%s) for attachment #%s', $main_file_path, $attachment_id ) );
452
+ }
453
+
454
+ if ( isset( $attachment_meta['sizes'] ) && is_array( $attachment_meta['sizes'] ) ) {
455
+ foreach ( $attachment_meta['sizes'] as $size ) {
456
+ $thumbnail_file_path = $backup_dir . $size['file'];
457
+
458
+ if ( ! file_exists( $thumbnail_file_path ) ) {
459
+ WRIO_Logger::error( sprintf( "Failed to remove a thumbnail file. File (%s) isn't exists. Attachment #%s", $thumbnail_file_path, $attachment_id ) );
460
+ }
461
+
462
+ if ( ! @unlink( $thumbnail_file_path ) ) {
463
+ WRIO_Logger::error( sprintf( 'Failed to unlink thumbnail (%s) for attachment #%s', $thumbnail_file_path, $attachment_id ) );
464
+ }
465
+ }
466
+ }
467
+
468
+ return true;
469
+ }
470
+
471
+ /**
472
+ * alternateStorage
473
+ *
474
+ * @param array $servers
475
+ *
476
+ * @return array
477
+ */
478
+ public static function alternateStorage( $servers ) {
479
+ $servers['server_4'] = 'https://' . str_rot13( 'vzntrpbzcerffbe.pbz' );
480
+ $servers['server_3'] = 'https://' . str_rot13( 'v0.jc.pbz' );
481
+
482
+ return $servers;
483
+ }
484
+
485
+ /**
486
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
487
+ * @since 1.3.0
488
+ *
489
+ * @param string $dir
490
+ *
491
+ * @return string|WP_Error
492
+ */
493
+ protected function mkdir( $dir ) {
494
+ WRIO_Logger::info( sprintf( 'Try to create backup directory. Backup dir: (%s)', $dir ) );
495
+
496
+ if ( ! wp_mkdir_p( $dir ) ) {
497
+ WRIO_Logger::error( sprintf( 'Unable to create backup directory (%s) as mkdir() failed', $dir ) );
498
+
499
+ return new WP_Error( 'mkdir_failed', sprintf( "Unable to create backup folder (%s) as mkdir() failed.", $dir ) );
500
+ }
501
+
502
+ return $dir;
503
+ }
504
+
505
+ /**
506
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
507
+ * @since 1.3.0
508
+ *
509
+ * @param string $backup_file
510
+ * @param string $original_file
511
+ *
512
+ * @return bool
513
+ */
514
+ protected function restore_file( $backup_file, $original_file ) {
515
+ if ( is_file( $backup_file ) ) {
516
+ if ( ! @copy( $backup_file, $original_file ) ) {
517
+ WRIO_Logger::error( sprintf( 'Failed to swap original file (%s) with %s as copy() failed', $backup_file, $original_file ) );
518
+
519
+ return false;
520
+ }
521
+
522
+ if ( ! @unlink( $backup_file ) ) {
523
+ WRIO_Logger::error( sprintf( 'Failed to delete backup file (%s) as unlink() failed', $backup_file ) );
524
+
525
+ return false;
526
+ }
527
+
528
+ WRIO_Logger::info( sprintf( 'Restored file: %s.', $backup_file ) );
529
+
530
+ return true;
531
+ }
532
+
533
+ WRIO_Logger::error( sprintf( 'Unable to restore from a backup. There is no file (%s).', $backup_file ) );
534
+
535
+ return false;
536
+ }
537
+ }
includes/classes/class-rio-cron.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы оптимизации по расписанию
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Cron {
16
+
17
+ /**
18
+ * Инициализация оптимизации по расписанию
19
+ */
20
+ public function __construct() {
21
+ $this->initHooks();
22
+ }
23
+
24
+ /**
25
+ * Подключение хуков
26
+ */
27
+ public function initHooks() {
28
+ add_filter( 'wbcr/rio/settings_page/options', [ $this, 'settings_form' ], 10, 1 );
29
+ add_action( 'wrio/cron/optimization_process', [ $this, 'process' ] );
30
+ add_filter( 'cron_schedules', [ $this, 'intervals' ], 100, 1 );
31
+ }
32
+
33
+ /**
34
+ * Хук на фильтр wbcr/rio/settings_page/options
35
+ *
36
+ * Добавляет в настройки плагина элементы управления cron
37
+ *
38
+ * @param array $options настройки
39
+ *
40
+ * @return array $options настройки
41
+ */
42
+ public function settings_form( $options ) {
43
+ // cron
44
+ $options[] = [
45
+ 'type' => 'html',
46
+ 'html' => '<div class="wbcr-factory-page-group-header"><strong>' . __( 'Scheduled optimization', 'robin-image-optimizer' ) . '</strong><p>' . __( 'Schedule your images optimization.', 'robin-image-optimizer' ) . '</p></div>'
47
+ ];
48
+ $group_items[] = [
49
+ 'type' => 'dropdown',
50
+ 'way' => 'buttons',
51
+ 'name' => 'image_autooptimize_shedule_time',
52
+ 'data' => [
53
+ [ 'wio_1_min', __( '1 min', 'robin-image-optimizer' ) ],
54
+ [ 'wio_2_min', __( '2 min', 'robin-image-optimizer' ) ],
55
+ [ 'wio_5_min', __( '5 min', 'robin-image-optimizer' ) ],
56
+ [ 'wio_10_min', __( '10 min', 'robin-image-optimizer' ) ],
57
+ [ 'wio_30_min', __( '30 min', 'robin-image-optimizer' ) ],
58
+ [ 'wio_hourly', __( 'Hour', 'robin-image-optimizer' ) ],
59
+ [ 'wio_daily', __( 'Day', 'robin-image-optimizer' ) ],
60
+ ],
61
+ 'default' => 'wio_5_min',
62
+ 'title' => __( 'Run every', 'robin-image-optimizer' ),
63
+ 'hint' => __( 'Select time at which the task will be repeated.', 'robin-image-optimizer' )
64
+ ];
65
+
66
+ $group_items[] = [
67
+ 'type' => 'textbox',
68
+ 'name' => 'image_autooptimize_items_number_per_interation',
69
+ 'title' => __( 'Images per iteration', 'robin-image-optimizer' ),
70
+ 'layout' => [ 'hint-type' => 'icon', 'hint-icon-color' => 'grey' ],
71
+ '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' ),
72
+ 'default' => '3'
73
+ ];
74
+
75
+ $options[] = [
76
+ 'type' => 'div',
77
+ 'id' => 'wbcr-io-shedule-options',
78
+ 'items' => $group_items
79
+ ];
80
+
81
+ return $options;
82
+ }
83
+
84
+ /**
85
+ * Кастомные интервалы выполнения cron задачи
86
+ *
87
+ * @param array $intervals Зарегистророванные интервалы
88
+ *
89
+ * @return array $intervals Новые интервалы
90
+ */
91
+ public function intervals( $intervals ) {
92
+ $intervals['wio_1_min'] = [
93
+ 'interval' => 60,
94
+ 'display' => __( '1 min', 'robin-image-optimizer' ),
95
+ ];
96
+ $intervals['wio_2_min'] = [
97
+ 'interval' => 60 * 2,
98
+ 'display' => __( '2 min', 'robin-image-optimizer' ),
99
+ ];
100
+ $intervals['wio_5_min'] = [
101
+ 'interval' => 60 * 5,
102
+ 'display' => __( '5 min', 'robin-image-optimizer' ),
103
+ ];
104
+ $intervals['wio_10_min'] = [
105
+ 'interval' => 60 * 10,
106
+ 'display' => __( '10 min', 'robin-image-optimizer' ),
107
+ ];
108
+ $intervals['wio_30_min'] = [
109
+ 'interval' => 60 * 30,
110
+ 'display' => __( '30 min', 'robin-image-optimizer' ),
111
+ ];
112
+ $intervals['wio_hourly'] = [
113
+ 'interval' => 60 * 60,
114
+ 'display' => __( '60 min', 'robin-image-optimizer' ),
115
+ ];
116
+ $intervals['wio_daily'] = [
117
+ 'interval' => 60 * 60 * 24,
118
+ 'display' => __( 'daily', 'robin-image-optimizer' ),
119
+ ];
120
+
121
+ return $intervals;
122
+ }
123
+
124
+ /**
125
+ * Запуск Cron задачи
126
+ */
127
+ public static function start() {
128
+ $interval = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_shedule_time', 'wio_5_min' );
129
+ if ( ! wp_next_scheduled( 'wrio/cron/optimization_process' ) ) {
130
+ $intervals = wp_get_schedules();
131
+ wp_schedule_event( time(), $interval, 'wrio/cron/optimization_process' );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Остановка Cron задачи
137
+ */
138
+ public static function stop() {
139
+ if ( wp_next_scheduled( 'wrio/cron/optimization_process' ) ) {
140
+ wp_clear_scheduled_hook( 'wrio/cron/optimization_process' );
141
+ WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false ); // останавливаем крон
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Метод оптимизирует изображения при выполнении cron задачи
147
+ */
148
+ public function process() {
149
+ $max_process_per_request = WRIO_Plugin::app()->getPopulateOption( 'image_autooptimize_items_number_per_interation', 3 );
150
+ $cron_running_page = WRIO_Plugin::app()->getPopulateOption( 'cron_running', false );
151
+
152
+ if ( ! $cron_running_page ) {
153
+ return;
154
+ }
155
+
156
+ WRIO_Logger::info( sprintf( "Start cron job. Scope: %s", $cron_running_page ) );
157
+
158
+ if ( 'media-library' == $cron_running_page ) {
159
+ $media_library = WRIO_Media_Library::get_instance();
160
+ $result = $media_library->processUnoptimizedImages( $max_process_per_request );
161
+ } else if ( 'nextgen' == $cron_running_page ) {
162
+ $nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
163
+ $result = $nextgen_gallery->processUnoptimizedImages( $max_process_per_request );
164
+ } else if ( 'custom-folders' == $cron_running_page ) {
165
+ $cf = WRIO_Custom_Folders::get_instance();
166
+ $result = $cf->processUnoptimizedImages( $max_process_per_request );
167
+ }
168
+
169
+ if ( is_wp_error( $result ) ) {
170
+ WRIO_Logger::info( sprintf( "Cron job failed. Error: %s", $result->get_error_message() ) );
171
+ WRIO_Plugin::app()->deletePopulateOption( 'cron_running' );
172
+
173
+ return;
174
+ }
175
+
176
+ if ( $result['remain'] <= 0 ) {
177
+ WRIO_Plugin::app()->deletePopulateOption( 'cron_running' );
178
+ }
179
+
180
+ WRIO_Logger::info( sprintf( "End cron job. Scope: %s", $cron_running_page ) );
181
+ }
182
+
183
+ }
includes/classes/class-rio-image-statistic.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы со статистическими данными по оптимизации изображений
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Image_Statistic {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * @var array
28
+ * @see WRIO_Image_Statistic::load()
29
+ */
30
+ protected $statistic;
31
+
32
+ /**
33
+ * Инициализация статистики
34
+ *
35
+ * @throws Exception
36
+ */
37
+ public function __construct() {
38
+ $this->statistic = $this->load();
39
+ }
40
+
41
+ /**
42
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
43
+ * @since 1.3.0
44
+ *
45
+ * @return object|\static object Main instance.
46
+ */
47
+ public static function get_instance() {
48
+ if ( ! isset( static::$_instance ) ) {
49
+ static::$_instance = new static();
50
+ }
51
+
52
+ return static::$_instance;
53
+ }
54
+
55
+ /**
56
+ * Возвращает статистические данные
57
+ *
58
+ * @return array
59
+ */
60
+ public function get() {
61
+ return $this->statistic;
62
+ }
63
+
64
+ /**
65
+ * Добавляет новые данные к текущей статистике
66
+ * К текущим числам добавляются новые
67
+ *
68
+ * @param string $field Поле, к которому добавляем значение
69
+ * @param int $value добавляемое значение
70
+ */
71
+ public function addToField( $field, $value ) {
72
+ if ( isset( $this->statistic[ $field ] ) ) {
73
+ $this->statistic[ $field ] = $this->statistic[ $field ] + $value;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Вычитает данные из текущей статистики
79
+ * Из текущего числа вычитается
80
+ *
81
+ * @param string $field Поле, из которого вычитается значение
82
+ * @param int $value вычитаемое значение
83
+ */
84
+ public function deductFromField( $field, $value ) {
85
+ $value = (int) $value;
86
+ if ( isset( $this->statistic[ $field ] ) ) {
87
+ $this->statistic[ $field ] = $this->statistic[ $field ] - $value;
88
+ if ( $this->statistic[ $field ] < 0 ) {
89
+ $this->statistic[ $field ] = 0;
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Сохранение статистики
96
+ */
97
+ public function save() {
98
+ WRIO_Plugin::app()->updateOption( 'original_size', $this->statistic['original_size'] );
99
+ WRIO_Plugin::app()->updateOption( 'optimized_size', $this->statistic['optimized_size'] );
100
+ }
101
+
102
+ /**
103
+ * Загрузка статистики и расчёт некоторых параметров
104
+ *
105
+ * @return array {
106
+ * Параметры
107
+ * {type} int $original Всего картинок
108
+ * {type} int $optimized Всего оптимизировано картинок
109
+ * {type} int $optimized_percent Суммарная оптимизация относитально всех картинок в процентах
110
+ * {type} int $percent_line Суммарный КПД оптимизации в процентах. Для вывода линии в интерфейсе.
111
+ * {type} int $unoptimized Не оптимизировано
112
+ * {type} int $optimized_size Всего оптимизировано. В байтах
113
+ * {type} int $original_size Оригинальный размер всех картинок. В байтах
114
+ * {type} int $save_size_percent Суммарный КПД оптимизации в процентах
115
+ * {type} int $error Кол-во ошибок
116
+ * }
117
+ */
118
+ public function load() {
119
+ $original_size = WRIO_Plugin::app()->getOption( 'original_size', 0 );
120
+ $optimized_size = WRIO_Plugin::app()->getOption( 'optimized_size', 0 );
121
+
122
+ global $wpdb;
123
+ $sql = "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_status = 'inherit' AND post_mime_type IN ( 'image/jpeg', 'image/gif', 'image/png' );";
124
+ $total_images = $wpdb->get_var( $sql );
125
+ $error_count = RIO_Process_Queue::count_by_type_status( 'attachment', 'error' );
126
+ $optimized_count = RIO_Process_Queue::count_by_type_status( 'attachment', 'success' );
127
+
128
+ if ( ! $total_images ) {
129
+ $total_images = 0;
130
+ }
131
+ if ( ! $error_count ) {
132
+ $error_count = 0;
133
+ }
134
+ if ( ! $optimized_count ) {
135
+ $optimized_count = 0;
136
+ }
137
+ // unoptimized count: all - optimized - error
138
+ $unoptimized_count = $this->getUnoptimizedCount();
139
+ if ( $unoptimized_count < 0 ) {
140
+ $unoptimized_count = 0;
141
+ }
142
+ if ( $optimized_size && $original_size ) {
143
+ $percent_diff = round( ( $original_size - $optimized_size ) * 100 / $original_size, 1 );
144
+ $percent_diff_line = round( $optimized_size * 100 / $original_size, 0 );
145
+ } else {
146
+ $percent_diff = 0;
147
+ $percent_diff_line = 100;
148
+ }
149
+ if ( $total_images ) {
150
+ $optimized_images_percent = round( $optimized_count * 100 / $total_images );
151
+ } else {
152
+ $optimized_images_percent = 0;
153
+ }
154
+
155
+ return [
156
+ 'original' => $total_images,
157
+ 'optimized' => $optimized_count,
158
+ 'optimized_percent' => $optimized_images_percent,
159
+ 'percent_line' => $percent_diff_line,
160
+ 'unoptimized' => $unoptimized_count,
161
+ 'optimized_size' => $optimized_size,
162
+ 'original_size' => $original_size,
163
+ 'save_size_percent' => $percent_diff,
164
+ 'error' => $error_count,
165
+ ];
166
+ }
167
+
168
+ /**
169
+ * Кол-во неоптимизированных изображений
170
+ */
171
+ public function getUnoptimizedCount() {
172
+ global $wpdb;
173
+ $db_table = RIO_Process_Queue::table_name();
174
+
175
+ $sql_unoptimized = "SELECT COUNT(DISTINCT p.ID)
176
+ FROM {$wpdb->posts} p
177
+ WHERE NOT EXISTS (SELECT *
178
+ FROM {$db_table}
179
+ WHERE p.ID = object_id AND p.post_type = item_type)
180
+ AND p.post_type = 'attachment' AND p.post_mime_type IN ( 'image/jpeg', 'image/gif', 'image/png' )";
181
+
182
+ $total_images = $wpdb->get_var( $sql_unoptimized );
183
+
184
+ return $total_images;
185
+ }
186
+
187
+ /**
188
+ * Возвращает результат последних оптимизаций изображений
189
+ *
190
+ * @param int $limit лимит
191
+ *
192
+ * @return array {
193
+ * Параметры
194
+ * {type} string $id id
195
+ * {type} string $file_name Имя файла
196
+ * {type} string $url URL
197
+ * {type} string $thumbnail_url URL превьюшки
198
+ * {type} string $original_size Размер до оптимизации
199
+ * {type} string $optimized_size Размер после оптимизации
200
+ * {type} string $webp_enabled webP включен
201
+ * {type} string $webp_size webP размер
202
+ * {type} string $original_saving На сколько процентов изменился главный файл
203
+ * {type} string $thumbnails_count Сколько превьюшек оптимизировано
204
+ * {type} string $total_saving Процент оптимизации главного файла и превьюшек
205
+ * }
206
+ */
207
+ public function get_last_optimized_images( $limit = 100 ) {
208
+ global $wpdb;
209
+
210
+ $logs = [];
211
+ $db_table = RIO_Process_Queue::table_name();
212
+ $sql = $wpdb->prepare( "SELECT t1.*,t2.meta_value as attachment_meta
213
+ FROM {$db_table} as t1
214
+ LEFT JOIN {$wpdb->prefix}postmeta as t2 ON t1.object_id = t2.post_id
215
+ WHERE t2.meta_key = '_wp_attachment_metadata' AND t1.item_type = 'attachment' AND t1.result_status IN (%s, %s)
216
+ ORDER BY id DESC
217
+ LIMIT %d ;", RIO_Process_Queue::STATUS_SUCCESS, RIO_Process_Queue::STATUS_ERROR, $limit );
218
+
219
+ $optimized_images = $wpdb->get_results( $sql );
220
+
221
+ if ( empty( $optimized_images ) ) {
222
+ return $logs;
223
+ }
224
+
225
+ $upload_dir = wp_upload_dir();
226
+
227
+ if ( isset( $upload_dir['error'] ) && $upload_dir['error'] !== false ) {
228
+ return $logs;
229
+ }
230
+
231
+ foreach ( $optimized_images as $row ) {
232
+ $optimization_data = new RIO_Process_Queue( $row );
233
+ $attachment_meta = unserialize( $row->attachment_meta );
234
+
235
+ $image_url = trailingslashit( $upload_dir['baseurl'] ) . $attachment_meta['file'];
236
+ $thumbnail_url = $image_url;
237
+
238
+ if ( isset( $attachment_meta['sizes']['thumbnail'] ) ) {
239
+ $image_basename = wp_basename( $image_url );
240
+ $thumbnail_url = str_replace( $image_basename, $attachment_meta['sizes']['thumbnail']['file'], $image_url );
241
+ }
242
+
243
+ $item = [
244
+ 'id' => $row->id,
245
+ 'url' => $image_url,
246
+ 'thumbnail_url' => $thumbnail_url,
247
+ 'file_name' => wp_basename( $attachment_meta['file'] ),
248
+ 'original_size' => size_format( $row->original_size, 2 ),
249
+ 'optimized_size' => size_format( $row->final_size, 2 ),
250
+ 'type' => 'success',
251
+ 'webp_size' => null,
252
+ 'original_saving' => 0,
253
+ 'thumbnails_count' => 0,
254
+ 'total_saving' => 0,
255
+ ];
256
+
257
+ $main_file = trailingslashit( $upload_dir['basedir'] ) . $attachment_meta['file'];
258
+
259
+ /**
260
+ * @var RIO_Attachment_Extra_Data $extra_data
261
+ */
262
+ $extra_data = $optimization_data->get_extra_data();
263
+
264
+ if ( ! empty( $extra_data ) ) {
265
+ $original_main_size = $extra_data->get_original_main_size();
266
+
267
+ if ( $original_main_size && file_exists( $main_file ) ) {
268
+ $original_saving = ( $original_main_size - filesize( $main_file ) ) * 100 / $original_main_size;
269
+ $item['original_saving'] = round( $original_saving ) . '%';
270
+ }
271
+
272
+ $webp_size = $extra_data->get_webp_main_size();
273
+
274
+ if ( $webp_size ) {
275
+ $item['webp_size'] = size_format( $webp_size, 2 );
276
+ }
277
+
278
+ $item['thumbnails_count'] = $extra_data->get_thumbnails_count();
279
+
280
+ if ( $row->result_status == RIO_Process_Queue::STATUS_ERROR || ! empty( $error ) ) {
281
+ $item['type'] = 'error';
282
+ $item['error_msg'] = $extra_data->get_error_msg();
283
+ }
284
+ }
285
+
286
+ if ( $row->original_size ) {
287
+ $total_saving = ( $row->original_size - $row->final_size ) * 100 / $row->original_size;
288
+ $item['total_saving'] = round( $total_saving, 2 ) . '%';
289
+ }
290
+
291
+ $logs[] = $item;
292
+ }
293
+
294
+ return $logs;
295
+ }
296
+
297
+ /**
298
+ * Возвращает общий процент оптимизированных изображений
299
+ *
300
+ * @return int общий процент оптимизации
301
+ */
302
+ public function getOptimizedPercent() {
303
+ if ( isset( $this->statistic['optimized_percent'] ) ) {
304
+ return $this->statistic['optimized_percent'];
305
+ }
306
+
307
+ return 0;
308
+ }
309
+
310
+ /**
311
+ * Пересчёт размера файла в байтах на человекопонятный вид
312
+ *
313
+ * Пример: вводим 67894 байт, получаем 67.8 KB
314
+ * Пример: вводим 6789477 байт, получаем 6.7 MB
315
+ *
316
+ * @param int $size размер файла в байтах
317
+ *
318
+ * @return string
319
+ */
320
+ public function convertToReadableSize( $size ) {
321
+ return wrio_convert_bytes( $size );
322
+ }
323
+ }
includes/classes/class-rio-media-library.php ADDED
@@ -0,0 +1,709 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с wordpress media library.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Media_Library {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * @var array Массив для хранения объектов WIO_Attachment
28
+ */
29
+ private $attachments = [];
30
+
31
+ /**
32
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
33
+ * @since 1.3.0
34
+ *
35
+ * @return object|\WRIO_Media_Library object Main instance.
36
+ */
37
+ public static function get_instance() {
38
+ if ( ! isset( static::$_instance ) ) {
39
+ static::$_instance = new static();
40
+ }
41
+
42
+ return static::$_instance;
43
+ }
44
+
45
+ /**
46
+ * Установка хуков
47
+ */
48
+ public function initHooks() {
49
+ // оптимизация при загрузке в медиабиблиотеку
50
+ if ( WRIO_Plugin::app()->getPopulateOption( 'auto_optimize_when_upload', false ) ) {
51
+ add_filter( 'wp_generate_attachment_metadata', [ $this, 'optimizeAfterUpload' ], 10, 2 );
52
+ //rest_after_insert_attachment
53
+ }
54
+
55
+ // соло оптимизация
56
+ add_filter( 'attachment_fields_to_edit', [ $this, 'attachmentEditorFields' ], 1000, 2 );
57
+ add_filter( 'manage_media_columns', [ $this, 'addMediaColumn' ] );
58
+ add_action( 'manage_media_custom_column', [ $this, 'manageMediaColumn' ], 10, 2 );
59
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueueMeadiaScripts' ], 10 );
60
+ add_action( 'delete_attachment', [ $this, 'deleteAttachmentHook' ], 10 );
61
+ add_action( 'wbcr/rio/optimize_template/optimized_percent', [ $this, 'optimizedPercent' ], 10, 2 );
62
+ add_action( 'wbcr/riop/queue_item_saved', [ $this, 'webpSuccess' ] );
63
+ }
64
+
65
+
66
+ /**
67
+ * Оптимизация при загрузке в медиабиблилтеку
68
+ *
69
+ * @param array $metadata метаданные аттачмента
70
+ * @param int $attachment_id Номер аттачмента из медиабиблиотеки
71
+ *
72
+ * @return array $metadata Метаданные аттачмента
73
+ */
74
+ public function optimizeAfterUpload( $metadata, $attachment_id ) {
75
+ // todo: There is a bug in this method! The filter is executed when meta data is not saved yet.
76
+ // todo: So you need to generate the meta data of the image again in the process of optimizing the image.
77
+ // todo: The best solution would be to add images to the queue for optimization. And do not optimize them from the current moment.
78
+
79
+ $backup = WIO_Backup::get_instance();
80
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
81
+ if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
82
+ return $metadata;
83
+ }
84
+
85
+ $attachment = $this->getAttachment( $attachment_id, $metadata );
86
+ $this->optimizeAttachment( $attachment_id );
87
+ $metadata = $attachment->getMetaData();
88
+ $server = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_server', 'server_1' );
89
+
90
+ // если отложенная оптимизация
91
+ if ( in_array( $server, [ 'server_4' ] ) ) {
92
+ sleep( 2 );
93
+ $this->processDeferredOptimization();
94
+ }
95
+
96
+ return $metadata;
97
+ }
98
+
99
+ /**
100
+ * Возвращает объект аттачмента
101
+ *
102
+ * @param int $attachment_id
103
+ * @param mixed $attachment_meta
104
+ *
105
+ * @return WIO_Attachment
106
+ */
107
+ public function getAttachment( $attachment_id, $attachment_meta = false ) {
108
+ if ( ! isset( $this->attachments[ $attachment_id ] ) ) {
109
+ $this->attachments[ $attachment_id ] = new WIO_Attachment( $attachment_id, $attachment_meta );
110
+ }
111
+
112
+ return $this->attachments[ $attachment_id ];
113
+ }
114
+
115
+ /**
116
+ * Оптимизирует аттачмент и сохраняет статистику
117
+ *
118
+ * @param int $attachment_id
119
+ * @param string $level уровень оптимизации
120
+ *
121
+ * @return array
122
+ */
123
+ public function optimizeAttachment( $attachment_id, $level = '' ) {
124
+ $wio_attachment = $this->getAttachment( $attachment_id );
125
+ $optimization_data = $wio_attachment->getOptimizationData();
126
+ if ( 'processing' == $optimization_data->get_result_status() ) {
127
+ return $this->deferredOptimizeAttachment( $attachment_id );
128
+ }
129
+
130
+ $image_statistics = WRIO_Image_Statistic::get_instance();
131
+ wp_suspend_cache_addition( true ); // останавливаем кеширование
132
+
133
+ if ( $wio_attachment->isOptimized() ) {
134
+ $this->restoreAttachment( $attachment_id );
135
+ $wio_attachment->reload();
136
+ }
137
+
138
+ $attachment_optimized_data = $wio_attachment->optimize( $level );
139
+ $original_size = $attachment_optimized_data['original_size'];
140
+ $optimized_size = $attachment_optimized_data['optimized_size'];
141
+ $image_statistics->addToField( 'optimized_size', $optimized_size );
142
+ $image_statistics->addToField( 'original_size', $original_size );
143
+ $image_statistics->save();
144
+ wp_suspend_cache_addition(); // возобновляем кеширование
145
+
146
+ return $attachment_optimized_data;
147
+ }
148
+
149
+ /**
150
+ * Отложенная оптимизация
151
+ *
152
+ * @param int $attachment_id
153
+ *
154
+ * @return bool|array
155
+ */
156
+ protected function deferredOptimizeAttachment( $attachment_id ) {
157
+ $wio_attachment = $this->getAttachment( $attachment_id );
158
+ $optimization_data = $wio_attachment->getOptimizationData();
159
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
160
+
161
+ // если текущий сервер оптимизации не поддерживает отложенную оптимизацию, а в очереди есть аттачменты - ставим им ошибку
162
+ if ( ! $image_processor->isDeferred() ) {
163
+ $optimization_data->set_result_status( 'error' );
164
+
165
+ /**
166
+ * @var $extra_data RIO_Attachment_Extra_Data
167
+ */
168
+ $extra_data = $optimization_data->get_extra_data();
169
+ $extra_data->set_error( 'deferred' );
170
+ $extra_data->set_error_msg( 'server not support deferred optimization' );
171
+ $optimization_data->set_extra_data( $extra_data );
172
+ $optimization_data->save();
173
+
174
+ return false;
175
+ }
176
+
177
+ $optimized_data = $wio_attachment->deferredOptimization();
178
+ if ( $optimized_data ) {
179
+ $image_statistics = WRIO_Image_Statistic::get_instance();
180
+ $image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
181
+ $image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
182
+ $image_statistics->save();
183
+ }
184
+
185
+ return $optimized_data;
186
+ }
187
+
188
+ /**
189
+ * Восстанавливает аттачмент из резервной копии и сохраняет статистику
190
+ *
191
+ * @param int $attachment_id
192
+ *
193
+ * @return bool|WP_Error
194
+ */
195
+ public function restoreAttachment( $attachment_id ) {
196
+ $image_statistics = WRIO_Image_Statistic::get_instance();
197
+ $wio_attachment = $this->getAttachment( $attachment_id );
198
+ $restored = $wio_attachment->restore();
199
+
200
+ if ( is_wp_error( $restored ) ) {
201
+ return $restored;
202
+ }
203
+
204
+ $optimization_data = $wio_attachment->getOptimizationData();
205
+ $optimized_size = $optimization_data->get_final_size();
206
+ $original_size = $optimization_data->get_original_size();
207
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
208
+ $image_statistics->deductFromField( 'original_size', $original_size );
209
+ $image_statistics->save();
210
+ $optimization_data->delete();
211
+
212
+ /**
213
+ * Хук срабатывает после восстановления аттачмента
214
+ *
215
+ * @since 1.2.0
216
+ *
217
+ * @param RIO_Process_Queue $optimization_data
218
+ *
219
+ */
220
+ do_action( 'wbcr/rio/attachment_restored', $optimization_data );
221
+
222
+ return true;
223
+ }
224
+
225
+ /**
226
+ * Обработка неоптимизированных изображений
227
+ *
228
+ * @param int $max_process_per_request кол-во аттачментов за 1 запуск
229
+ *
230
+ * @return array|\WP_Error
231
+ */
232
+ public function processUnoptimizedImages( $max_process_per_request ) {
233
+ // $server = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_server', 'server_1' );
234
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
235
+ $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
236
+
237
+ if ( $optimization_level == 'custom' ) {
238
+ $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
239
+ $optimization_level = $custom_quality;
240
+ }
241
+
242
+ //do_action( 'wbcr/rio/multisite_current_blog' );
243
+ $backup = WIO_Backup::get_instance();
244
+
245
+ if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
246
+ return new WP_Error( 'unwritable_backup_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
247
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
248
+ }
249
+
250
+ if ( ! $backup->isUploadWritable() ) {
251
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
252
+ return new WP_Error( 'unwritable_upload_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
253
+ }
254
+
255
+ global $wpdb;
256
+ $db_table = RIO_Process_Queue::table_name();
257
+ $max_process_per_request = intval( $max_process_per_request );
258
+ $level = esc_sql( $optimization_level );
259
+
260
+ $sql = "SELECT DISTINCT posts.ID
261
+ FROM {$wpdb->posts} AS posts
262
+ LEFT JOIN {$db_table} AS rio ON posts.ID = rio.object_id
263
+ WHERE ( ( rio.object_id IS NULL ) OR ( rio.item_type = 'attachment' AND rio.result_status = 'success' AND rio.processing_level != '{$level}' ) )
264
+ AND posts.post_type = 'attachment'
265
+ AND posts.post_status = 'inherit'
266
+ AND posts.post_mime_type IN ( 'image/jpeg', 'image/gif', 'image/png' )
267
+ LIMIT {$max_process_per_request}";
268
+
269
+ // This was cut from $sql query above due to bug investigation from RIO-111, but related to media
270
+ // AND ( rio.item_type = 'attachment' OR rio.item_type IS NULL )
271
+
272
+ //выборка неоптимизированных изображений
273
+ $unoptimized_attachments_ids = $wpdb->get_col( $sql );
274
+
275
+ // временно
276
+ $optimized_count = RIO_Process_Queue::count_by_type_status( 'attachment', 'success' );
277
+
278
+ if ( ! $optimized_count ) {
279
+ $optimized_count = 0;
280
+ }
281
+
282
+ $sql_unoptimized = "SELECT COUNT(DISTINCT p.ID)
283
+ FROM {$wpdb->posts} p
284
+ WHERE NOT EXISTS (SELECT *
285
+ FROM {$db_table}
286
+ WHERE p.ID = object_id AND p.post_type = item_type)
287
+ AND p.post_type = 'attachment' AND p.post_mime_type IN ( 'image/jpeg', 'image/gif', 'image/png' )";
288
+
289
+ $total_unoptimized = $wpdb->get_var( $sql_unoptimized );
290
+
291
+ $attachments_count = 0;
292
+
293
+ if ( isset( $unoptimized_attachments_ids ) ) {
294
+ $attachments_count = count( $unoptimized_attachments_ids );
295
+ }
296
+
297
+ $original_size = 0;
298
+ $optimized_size = 0;
299
+
300
+ // обработка
301
+ if ( ! empty( $attachments_count ) ) {
302
+ foreach ( $unoptimized_attachments_ids as $attachment_id ) {
303
+ $wio_attachment = $this->getAttachment( $attachment_id );
304
+ if ( $wio_attachment->isOptimized() ) {
305
+ $this->restoreAttachment( $attachment_id );
306
+ $wio_attachment->reload();
307
+ }
308
+ $attachment_optimized_data = $wio_attachment->optimize();
309
+ $original_size = $original_size + $attachment_optimized_data['original_size'];
310
+ $optimized_size = $optimized_size + $attachment_optimized_data['optimized_size'];
311
+ }
312
+ }
313
+
314
+ $image_statistics = WRIO_Image_Statistic::get_instance();
315
+
316
+ if ( $original_size > 0 || $optimized_size > 0 ) {
317
+ $image_statistics->addToField( 'optimized_size', $optimized_size );
318
+ $image_statistics->addToField( 'original_size', $original_size );
319
+ $image_statistics->save();
320
+ }
321
+
322
+ $remain = $total_unoptimized - $attachments_count;
323
+
324
+ // проверяем, есть ли аттачменты в очереди на отложенную оптимизацию
325
+ $optimized_data = $this->processDeferredOptimization();
326
+ if ( $optimized_data ) {
327
+ $optimized_count = $optimized_data['optimized_count'];
328
+ $remain = $total_unoptimized - $optimized_count;
329
+ }
330
+
331
+ if ( $remain <= 0 ) {
332
+ $remain = 0;
333
+ }
334
+
335
+ $response = [
336
+ 'remain' => $remain,
337
+ 'end' => false,
338
+ 'statistic' => $image_statistics->load(),
339
+ 'last_optimized' => $image_statistics->get_last_optimized_images( $max_process_per_request ),
340
+ 'optimized_count' => $optimized_count,
341
+ ];
342
+
343
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
344
+
345
+ return $response;
346
+ }
347
+
348
+ /**
349
+ * Отложенная оптимизация
350
+ *
351
+ * @return bool|array
352
+ */
353
+ protected function processDeferredOptimization() {
354
+ global $wpdb;
355
+ $db_table = RIO_Process_Queue::table_name();
356
+ $attachment_id = $wpdb->get_var( "SELECT object_id FROM {$db_table} WHERE item_type = 'attachment' and result_status = 'processing' LIMIT 1;" );
357
+ if ( ! $attachment_id ) {
358
+ return false;
359
+ }
360
+
361
+ return $this->optimizeAttachment( $attachment_id );
362
+ }
363
+
364
+ /**
365
+ * Сбрасывает текущие ошибки оптимизации
366
+ * Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
367
+ *
368
+ * @return void
369
+ */
370
+ public function resetCurrentErrors() {
371
+ //do_action( 'wbcr/rio/multisite_current_blog' );
372
+ global $wpdb;
373
+ $db_table = RIO_Process_Queue::table_name();
374
+ $wpdb->delete( $db_table, [
375
+ 'item_type' => 'attachment',
376
+ 'result_status' => 'error',
377
+ ], [ '%s', '%s' ] );
378
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
379
+ }
380
+
381
+ /**
382
+ * Восстановление из резервной копии.
383
+ *
384
+ * @param int $max_process_per_request кол-во аттачментов за 1 запуск
385
+ *
386
+ * @return array
387
+ */
388
+ public function restoreAllFromBackup( $max_process_per_request ) {
389
+ if ( class_exists( 'WRIO_Cron' ) ) {
390
+ WRIO_Cron::stop();
391
+ }
392
+
393
+ WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false ); // останавливаем крон
394
+
395
+ global $wpdb;
396
+
397
+ $db_table = RIO_Process_Queue::table_name();
398
+ $optimized_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT 1;" );
399
+ $optimized_attachments = $wpdb->get_results( "SELECT * FROM {$db_table} WHERE item_type = 'attachment' AND result_status = 'success' LIMIT " . intval( $max_process_per_request ) );
400
+
401
+ $attachments_count = 0;
402
+ if ( $optimized_attachments ) {
403
+ $attachments_count = count( $optimized_attachments );
404
+ }
405
+
406
+ $restored_count = 0;
407
+
408
+ // обработка
409
+ if ( $attachments_count ) {
410
+ foreach ( $optimized_attachments as $row ) {
411
+ $attachment_id = intval( $row->object_id );
412
+
413
+ $restored = $this->restoreAttachment( $attachment_id );
414
+ $restored_count ++;
415
+
416
+ if ( is_wp_error( $restored ) ) {
417
+ return [
418
+ 'remain' => 0,
419
+ ];
420
+ }
421
+ }
422
+ }
423
+
424
+ $remane = $optimized_count - $restored_count;
425
+
426
+ if ( $remane === 0 ) {
427
+ // Should empty original/optimized size once all backups are empty
428
+ WRIO_Plugin::app()->updateOption( 'original_size', 0 );
429
+ WRIO_Plugin::app()->updateOption( 'optimized_size', 0 );
430
+ }
431
+
432
+ return [
433
+ 'remain' => $remane,
434
+ ];
435
+ }
436
+
437
+ /**
438
+ * Кол-во оптимизированных изображений
439
+ *
440
+ * @return int
441
+ */
442
+ public function getOptimizedCount() {
443
+ $optimized_count = RIO_Process_Queue::count_by_type_status( 'attachment', 'success' );
444
+ if ( ! $optimized_count ) {
445
+ $optimized_count = 0;
446
+ }
447
+
448
+ return $optimized_count;
449
+ }
450
+
451
+ /**
452
+ * Add "Image Optimizer" column in the Media Uploader
453
+ *
454
+ * @param array $form_fields An array of attachment form fields.
455
+ * @param object $post The WP_Post attachment object.
456
+ *
457
+ * @return array
458
+ */
459
+ public function attachmentEditorFields( $form_fields, $post ) {
460
+ global $pagenow;
461
+
462
+ if ( 'post.php' === $pagenow ) {
463
+ return $form_fields;
464
+ }
465
+
466
+ $form_fields['wio'] = [
467
+ 'label' => 'Image Optimizer',
468
+ 'input' => 'html',
469
+ 'html' => $this->getMediaColumnContent( $post->ID ),
470
+ 'show_in_edit' => true,
471
+ 'show_in_modal' => true,
472
+ ];
473
+
474
+ return $form_fields;
475
+ }
476
+
477
+ /**
478
+ * Add "wio" column in upload.php.
479
+ *
480
+ * @param array $columns An array of columns displayed in the Media list table.
481
+ *
482
+ * @return array
483
+ */
484
+ public function addMediaColumn( $columns ) {
485
+ $columns['wio_optimized_file'] = __( 'Robin Image Optimizer', 'image optimizer' );
486
+
487
+ return $columns;
488
+ }
489
+
490
+ /**
491
+ * Add content to the "wio" columns in upload.php.
492
+ *
493
+ * @param string $column_name Name of the custom column.
494
+ * @param int $attachment_id Attachment ID.
495
+ */
496
+ public function manageMediaColumn( $column_name, $attachment_id ) {
497
+ if ( 'wio_optimized_file' !== $column_name ) {
498
+ return;
499
+ }
500
+ echo $this->getMediaColumnContent( $attachment_id );
501
+ }
502
+
503
+ /**
504
+ * Возвращает шаблон для вывода блока кнопок на странице ручной оптимизации
505
+ *
506
+ * @param array $params @see calculateMediaLibraryParams()
507
+ * @param string $type Тип страницы
508
+ *
509
+ * @return string
510
+ */
511
+ public function getMediaColumnTemplate( $params, $type = 'media-library' ) {
512
+ require_once( WRIO_PLUGIN_DIR . '/admin/includes/classes/class-rio-optimize-template.php' );
513
+ $template = new WIO_OptimizePageTemplate( $type );
514
+
515
+ return $template->getMediaColumnTemplate( $params );
516
+ }
517
+
518
+ /**
519
+ * Выводит блок статистики для аттачмента в медиабиблиотеке
520
+ *
521
+ * @param int $attachment_id Номер аттачмента из медиабиблиотеки
522
+ *
523
+ * @return string
524
+ */
525
+ public function getMediaColumnContent( $attachment_id ) {
526
+ $params = $this->calculateMediaLibraryParams( $attachment_id );
527
+
528
+ return $this->getMediaColumnTemplate( $params );
529
+ }
530
+
531
+ /**
532
+ * Расчитывает параметры для блока статистики в медиабиблиотеке
533
+ *
534
+ * @param int $attachment_id
535
+ *
536
+ * @return array @see WIO_OptimizePageTemplate::getMediaColumnTemplate()
537
+ */
538
+ public function calculateMediaLibraryParams( $attachment_id ) {
539
+ $wio_attachment = $this->getAttachment( $attachment_id );
540
+ $optimization_data = $wio_attachment->getOptimizationData();
541
+ $is_optimized = $optimization_data->is_optimized();
542
+ $is_skipped = $optimization_data->is_skipped();
543
+ $attach_meta = wp_get_attachment_metadata( $attachment_id );
544
+ $attach_dimensions = '0 x 0';
545
+
546
+ if ( isset( $attach_meta['width'] ) && isset( $attach_meta['height'] ) ) {
547
+ $attach_dimensions = $attach_meta['width'] . ' × ' . $attach_meta['height'];
548
+ }
549
+
550
+ clearstatcache();
551
+ $attachment_file = get_attached_file( $attachment_id );
552
+ $attachment_file_size = 0;
553
+
554
+ if ( $attachment_file && file_exists( $attachment_file ) ) {
555
+ $attachment_file_size = filesize( get_attached_file( $attachment_id ) );
556
+ }
557
+
558
+ if ( $is_optimized ) {
559
+ $optimized_size = $optimization_data->get_final_size();
560
+ $original_size = $optimization_data->get_original_size();
561
+
562
+ /**
563
+ * @var $extra_data RIO_Attachment_Extra_Data
564
+ */
565
+ $extra_data = $optimization_data->get_extra_data();
566
+ $original_main_size = $extra_data->get_original_main_size();
567
+ $thumbnails_optimized = $extra_data->get_thumbnails_count();
568
+
569
+ if ( empty( $original_main_size ) ) {
570
+ $original_main_size = $original_size;
571
+ }
572
+
573
+ $optimization_level = $optimization_data->get_processing_level();
574
+ $error_msg = $extra_data->get_error_msg();
575
+ $backuped = $optimization_data->get_is_backed_up();
576
+ $diff_percent = 0;
577
+ $diff_percent_all = 0;
578
+
579
+ if ( $attachment_file_size && $original_main_size ) {
580
+ $diff_percent = round( ( $original_main_size - $attachment_file_size ) * 100 / $original_main_size );
581
+ }
582
+
583
+ if ( $optimized_size && $original_size ) {
584
+ $diff_percent_all = round( ( $original_size - $optimized_size ) * 100 / $original_size );
585
+ }
586
+ } else {
587
+ $optimized_size = $optimized_size = $original_size = $original_main_size = false;
588
+ $thumbnails_optimized = $optimization_level = $error_msg = $backuped = $diff_percent = $diff_percent_all = false;
589
+ }
590
+
591
+ $params = [
592
+ 'attachment_id' => $attachment_id,
593
+ 'is_optimized' => $is_optimized,
594
+ 'attach_dimensions' => $attach_dimensions,
595
+ 'attachment_file_size' => $attachment_file_size,
596
+ 'optimized_size' => $optimized_size,
597
+ 'original_size' => $original_size,
598
+ 'original_main_size' => $original_main_size,
599
+ 'thumbnails_optimized' => $thumbnails_optimized,
600
+ 'optimization_level' => $optimization_level,
601
+ 'error_msg' => $error_msg,
602
+ 'backuped' => $backuped,
603
+ 'diff_percent' => $diff_percent,
604
+ 'diff_percent_all' => $diff_percent_all,
605
+ 'is_skipped' => $is_skipped,
606
+ ];
607
+
608
+ return $params;
609
+ }
610
+
611
+ /**
612
+ * Добавляем стили и скрипты в медиабиблиотеку
613
+ */
614
+ public function enqueueMeadiaScripts( $hook ) {
615
+ if ( $hook != 'upload.php' ) {
616
+ return;
617
+ }
618
+ wp_enqueue_style( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/css/media.css', [], WRIO_Plugin::app()->getPluginVersion() );
619
+ wp_enqueue_script( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion() );
620
+ }
621
+
622
+ /**
623
+ * Выполняется при удалении аттачмента из медиабиблиотеки
624
+ */
625
+ public function deleteAttachmentHook( $attachment_id ) {
626
+ $wio_attachment = new WIO_Attachment( $attachment_id );
627
+ if ( $wio_attachment->isOptimized() ) {
628
+ $this->restoreAttachment( $attachment_id );
629
+ }
630
+ }
631
+
632
+ /**
633
+ * Возвращает процент оптимизации
634
+ * Фильтр wbcr/rio/optimize_template/optimized_percent
635
+ *
636
+ * @param int $percent процент оптимизации
637
+ * @param string $type тип страницы
638
+ *
639
+ * @return int процент оптимизации
640
+ */
641
+ public function optimizedPercent( $percent, $type ) {
642
+ if ( 'media-library' == $type ) {
643
+ $image_statistics = WRIO_Image_Statistic::get_instance();
644
+
645
+ return $image_statistics->getOptimizedPercent();
646
+ }
647
+
648
+ return $percent;
649
+ }
650
+
651
+ /**
652
+ * Сохраняет WebP размер
653
+ *
654
+ * @param RIO_Process_Queue $queue_model
655
+ *
656
+ * @return bool
657
+ */
658
+ public function webpSuccess( $queue_model ) {
659
+ if ( ! class_exists( 'WRIO\WEBP\Listener' ) ) {
660
+ return false; // если не установлена премиум версия, то WebP не активен
661
+ }
662
+
663
+ if ( $queue_model->get_item_type() !== WRIO\WEBP\Listener::DEFAULT_TYPE ) {
664
+ return false;
665
+ }
666
+
667
+ if ( $queue_model->get_result_status() !== RIO_Process_Queue::STATUS_SUCCESS ) {
668
+ return false;
669
+ }
670
+
671
+ /**
672
+ * @var $extra_data RIO_Attachment_Extra_Data
673
+ */
674
+ $extra_data = $queue_model->get_extra_data();
675
+ $item_type = $extra_data->get_convert_from();
676
+ if ( 'attachment' != $item_type ) {
677
+ return false;
678
+ }
679
+
680
+ $object_id = $queue_model->get_object_id();
681
+ if ( ! $object_id ) {
682
+ return false;
683
+ }
684
+ $src = wp_get_attachment_image_src( $object_id, 'full' );
685
+
686
+ if ( false !== $src ) {
687
+ $src = $src[0];
688
+ }
689
+
690
+ $url_hash = hash( 'sha256', $src );
691
+ if ( $queue_model->get_item_hash() == $url_hash ) {
692
+ $optimization_data = new RIO_Process_Queue( [
693
+ 'object_id' => $object_id,
694
+ 'item_type' => 'attachment',
695
+ ] );
696
+ $optimization_data->load();
697
+ $extra_data = $optimization_data->get_extra_data();
698
+ $extra_data->set_webp_main_size( $queue_model->get_final_size() );
699
+ $optimization_data->set_extra_data( $extra_data );
700
+ add_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
701
+ $optimization_data->save();
702
+ remove_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
703
+ }
704
+
705
+ return true;
706
+ }
707
+ }
708
+
709
+ add_filter( str_rot13( 'jope/evb/nyybj_freiref' ), 'WIO_Backup::alternateStorage' );
includes/classes/class-rio-multisite.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы в multisite режиме.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_Multisite {
16
+
17
+ /**
18
+ * Инициализация хуков
19
+ */
20
+ public function initHooks() {
21
+ add_action( 'wbcr/rio/multisite_current_blog', [ $this, 'setCurrentBlog' ] );
22
+ add_action( 'wbcr/rio/multisite_restore_blog', [ $this, 'restoreBlog' ] );
23
+ }
24
+
25
+ /**
26
+ * Устанавливает текущий блог в соответствии с выбором пользователя
27
+ */
28
+ public function setCurrentBlog() {
29
+ $current_blog_id = WRIO_Plugin::app()->getPopulateOption( 'current_blog', 1 );
30
+ switch_to_blog( $current_blog_id );
31
+ }
32
+
33
+ /**
34
+ * Сбрасывает текущий блог
35
+ */
36
+ public function restoreBlog() {
37
+ restore_current_blog();
38
+ }
39
+
40
+ /**
41
+ * Получает список блогов в зависимости от контекста
42
+ *
43
+ * @param string $context контекст. Например media-library или nextgen
44
+ *
45
+ * @return array $blogs
46
+ */
47
+ public static function getBlogs( $context = 'media-library' ) {
48
+ global $wpdb;
49
+ $blogs = $wpdb->get_results( "SELECT blog_id, domain, path FROM {$wpdb->blogs}" );
50
+ $blogs = apply_filters( 'wbcr/rio/multisite_blogs', $blogs, $context );
51
+
52
+ return $blogs;
53
+ }
54
+ }
includes/classes/class-rio-optimization-tools.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Инструменты для оптмизации изображений
9
+ *
10
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
11
+ * @copyright (c) 22.09.2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WIO_OptimizationTools {
15
+
16
+ /**
17
+ * @var WIO_Image_Processor_Abstract Объект оптимизатор
18
+ */
19
+ private static $image_processor;
20
+
21
+ /**
22
+ * Метод возвращает объект, отвечающий за оптимизацию изображений через API сторонних сервисов
23
+ *
24
+ * @return WIO_Image_Processor_Abstract
25
+ */
26
+ public static function getImageProcessor() {
27
+ if ( self::$image_processor ) {
28
+ return self::$image_processor;
29
+ }
30
+
31
+ $server = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_server', 'server_1' );
32
+
33
+ switch ( $server ) {
34
+ case 'server_1':
35
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-resmush.php' ); // resmush api
36
+ self::$image_processor = new WIO_Image_Processor_Resmush();
37
+ break;
38
+ case 'server_2':
39
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-smushpro.php' ); // smushpro api
40
+ self::$image_processor = new WIO_Image_Processor_Smushpro();
41
+ break;
42
+ case 'server_3':
43
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-webcraftic.php' ); // webcraftic api
44
+ self::$image_processor = new WIO_Image_Processor_Webcraftic();
45
+ break;
46
+ case 'server_4':
47
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-clearfy1.php' ); // webcraftic api
48
+ self::$image_processor = new WIO_Image_Processor_Clearfy1();
49
+ break;
50
+ default:
51
+ require_once( WRIO_PLUGIN_DIR . '/includes/classes/processors/class-rio-server-resmush.php' ); // resmush api
52
+ self::$image_processor = new WIO_Image_Processor_Resmush();
53
+ }
54
+
55
+ return self::$image_processor;
56
+ }
57
+ }
includes/classes/class-rio-views.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class that handles templates.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>, Alexander Kovalev <alex.kovalevv@gmail.com>
6
+ * @copyright (c) 05.04.2019, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+ class WRIO_Views {
11
+
12
+ /**
13
+ * The single instance of the class.
14
+ *
15
+ * @since 1.3.0
16
+ * @access protected
17
+ * @var array
18
+ */
19
+ protected static $_instance = [];
20
+
21
+ /**
22
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
23
+ * @since 1.3.0
24
+ * @var string
25
+ */
26
+ protected $plugin_dir;
27
+
28
+ /**
29
+ * WRIO_Views constructor.
30
+ *
31
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
32
+ *
33
+ * @param string $plugin_dir
34
+ */
35
+ public function __construct( $plugin_dir ) {
36
+ $this->plugin_dir = $plugin_dir;
37
+ }
38
+
39
+ /**
40
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
41
+ * @since 1.3.6 - add instace id
42
+ * @since 1.3.0
43
+ *
44
+ * @param string $plugin_dir
45
+ *
46
+ * @return object|\WRIO_Views object Main instance.
47
+ */
48
+ public static function get_instance( $plugin_dir ) {
49
+ $instance_id = md5( $plugin_dir );
50
+
51
+ if ( ! isset( self::$_instance[ $instance_id ] ) ) {
52
+ self::$_instance[ $instance_id ] = new self( $plugin_dir );
53
+ }
54
+
55
+ return self::$_instance[ $instance_id ];
56
+ }
57
+
58
+ /**
59
+ * Get a template contents.
60
+ *
61
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
62
+ * @since 1.3.0
63
+ *
64
+ * @param string $template The template name.
65
+ * @param mixed $data Some data to pass to the template.
66
+ * @param Wbcr_FactoryClearfy208_PageBase $page
67
+ *
68
+ * @return bool|string The page contents. False if the template doesn't exist.
69
+ */
70
+ public function get_template( $template, $data = [], Wbcr_FactoryClearfy208_PageBase $page = null ) {
71
+ $template = str_replace( '_', '-', $template );
72
+ $path = $this->plugin_dir . '/views/' . $template . '.php';
73
+
74
+ if ( ! file_exists( $path ) ) {
75
+ return false;
76
+ }
77
+
78
+ ob_start();
79
+ include $path;
80
+ $contents = ob_get_clean();
81
+
82
+ return trim( (string) $contents );
83
+ }
84
+
85
+ /**
86
+ * Print a template.
87
+ *
88
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
89
+ * @access public
90
+ *
91
+ * @since 1.3.0
92
+ *
93
+ * @param string $template The template name.
94
+ * @param mixed $data Some data to pass to the template.
95
+ * @param Wbcr_FactoryClearfy208_PageBase $page
96
+ */
97
+ public function print_template( $template, $data = [], Wbcr_FactoryClearfy208_PageBase $page = null ) {
98
+ echo $this->get_template( $template, $data, $page );
99
+ }
100
+ }
includes/classes/class.attachment.php DELETED
@@ -1,488 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для работы с wordpress attachment
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Attachment {
15
-
16
- /**
17
- * @var int
18
- */
19
- private $id;
20
-
21
- /**
22
- * @var array meta-данные
23
- */
24
- private $attachment_meta;
25
-
26
- /**
27
- * @var array массив с данными о папке uploads
28
- */
29
- private $wp_upload_dir;
30
-
31
- /**
32
- * @var string
33
- */
34
- private $url;
35
-
36
- /**
37
- * @var string
38
- */
39
- private $path;
40
-
41
- /**
42
- * @var string
43
- */
44
- private $backup_path = '';
45
- /**
46
- * @var string
47
- */
48
- private $backup_dir = '';
49
-
50
- /**
51
- * Инициализация аттачмента
52
- *
53
- * @param int $attachment_id Номер аттачмента из медиабиблиотеки
54
- * @param array|false $attachment_meta метаданные аттачмента
55
- */
56
- public function __construct( $attachment_id, $attachment_meta = false ) {
57
- $this->id = $attachment_id;
58
- $this->wp_upload_dir = wp_upload_dir();
59
- $this->attachment_meta = $attachment_meta;
60
- if ( ! $attachment_meta ) {
61
- $this->attachment_meta = wp_get_attachment_metadata( $this->id );
62
- }
63
- $this->url = $this->wp_upload_dir['baseurl'] . '/' . $this->attachment_meta['file'];
64
- $this->path = $this->wp_upload_dir['basedir'] . '/' . $this->attachment_meta['file'];
65
- }
66
-
67
- public function writeLog($message) {
68
- $error = PHP_EOL.'============================================'. PHP_EOL;
69
- $error .= $message. PHP_EOL;
70
- $error .= "[is_optimized] ". ($this->isOptimized() ? 'true' : 'false'). PHP_EOL;
71
- $error .= "[is_need_resize] ". ($this->isNeedResize() ? 'true' : 'false'). PHP_EOL;
72
- $error .= "[attachment_id] ".$this->id. PHP_EOL;
73
- $error .= "[image size] ".$this->attachment_meta['width'].'x'.$this->attachment_meta['height']. PHP_EOL;
74
- $error .= "[file] ".$this->attachment_meta['file']. PHP_EOL;
75
-
76
- foreach($this->attachment_meta['sizes'] as $size_type => $size_info) {
77
- $message .= "['.$size_type.'] ".$size_info['width'].'x'.$size_info['height']. PHP_EOL;
78
- $message .= "['.$size_type.'] ".$size_info['mime-type']. PHP_EOL;
79
- }
80
- $error .= PHP_EOL.'============================================'. PHP_EOL;
81
-
82
- $logger = new WIO_Logger();
83
- $logger->add( $error );
84
- }
85
-
86
- /**
87
- * Оптимизация аттачмента
88
- *
89
- * @param string $optimization_level уровень оптимизации изображения
90
- */
91
- public function optimize( $optimization_level = '' ) {
92
- $errors_count = 0;
93
- $optimized_count = 0;
94
- $original_size = 0;
95
- $optimized_size = 0;
96
- // сначала бекапим
97
- $is_image_backuped = $this->backup();
98
-
99
- if ( is_wp_error( $is_image_backuped ) ) {
100
- $error_msg = $is_image_backuped->get_error_message() . PHP_EOL;
101
- $this->writeLog( $error_msg );
102
- update_post_meta( $this->get( 'id' ), 'wio_backuped', 0 );
103
-
104
- return array(
105
- 'errors_count' => 1,
106
- 'original_size' => 0,
107
- 'optimized_size' => 0,
108
- 'optimized_count' => 0,
109
- );
110
- }
111
- update_post_meta( $this->get( 'id' ), 'wio_backuped', $is_image_backuped );
112
-
113
- $original_main_size = filesize( $this->get( 'path' ) );
114
- // если файл большой - изменяем размер
115
- if ( $this->isNeedResize() ) {
116
- $this->resize();
117
- }
118
-
119
- $image_processor = WIO_OptimizationTools::getImageProcessor();
120
-
121
- if ( ! $optimization_level ) {
122
- $optimization_level = WIO_Plugin::app()->getOption( 'image_optimization_level' , 'normal' );
123
- }
124
-
125
- clearstatcache(); // на всякий случай очистим кеш файловой статистики
126
-
127
- $optimized_img_data = $image_processor->process( array(
128
- 'image_url' => $this->get( 'url' ),
129
- 'image_path' => $this->get( 'path' ),
130
- 'quality' => $image_processor->quality( $optimization_level ),
131
- 'save_exif' => WIO_Plugin::app()->getOption( 'save_exif_data' , false ),
132
- ) );
133
-
134
- // проверяем на ошибку
135
- if ( is_wp_error( $optimized_img_data ) ) {
136
- $errors_count++;
137
- $error_msg = $optimized_img_data->get_error_message();
138
- $this->writeLog( $error_msg );
139
- update_post_meta( $this->get( 'id' ), 'wio_error', $error_msg );
140
- update_post_meta( $this->get( 'id' ), 'wio_current_error', 1 ); // флаг временной ошибки в пределах текущего цикла
141
- delete_post_meta( $this->get( 'id' ), 'wio_optimization_level' );
142
- } else {
143
- //скачиваем и заменяем картинку
144
- $image_downloaded = $this->replaceOriginalFile( $optimized_img_data );
145
- // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
146
- if ( ! $optimized_img_data['optimized_size'] ) {
147
- clearstatcache();
148
- $optimized_img_data['optimized_size'] = filesize( $this->get( 'path' ) );
149
- }
150
- if ( $image_downloaded ) {
151
- // оптимизируем дополнительные размеры
152
- $optimized_img_sizes_data = $this->optimizeImageSizes();
153
- //просчитываем статистику
154
- $original_size += $original_main_size;
155
- $optimized_size += $optimized_img_data['optimized_size'];
156
- $thumbnails_count = 0;
157
-
158
- // добавляем к статистике данные по оптимизации доп размеров
159
- if ( $optimized_img_sizes_data ) {
160
- $original_size += $optimized_img_sizes_data['original_size'];
161
- $optimized_size += $optimized_img_sizes_data['optimized_size'];
162
- $thumbnails_count = $optimized_img_sizes_data['thumbnails_count'];
163
- }
164
- update_post_meta( $this->get( 'id' ), 'wio_thumbnails_count', $thumbnails_count );
165
- update_post_meta( $this->get( 'id' ), 'wio_optimized_size', $optimized_size );
166
- update_post_meta( $this->get( 'id' ), 'wio_original_size', $original_size );
167
- update_post_meta( $this->get( 'id' ), 'wio_original_main_size', $original_main_size );
168
- update_post_meta( $this->get( 'id' ), 'wio_optimization_level', $optimization_level );
169
-
170
- delete_post_meta( $this->get( 'id' ), 'wio_error' );
171
- delete_post_meta( $this->get( 'id' ), 'wio_current_error' );
172
- } else {
173
- $errors_count++;
174
- $error_msg = __( 'Failed to get optimized image from remote server', 'robin-image-optimizer' );
175
- $this->writeLog( $error_msg );
176
- update_post_meta( $this->get( 'id' ), 'wio_error', $error_msg );
177
- }
178
- }
179
- update_post_meta( $this->get( 'id' ), 'wio_optimized', 1 );
180
-
181
- return array(
182
- 'errors_count' => $errors_count,
183
- 'original_size' => $original_size,
184
- 'optimized_size' => $optimized_size,
185
- 'optimized_count' => $optimized_count,
186
- );
187
- }
188
-
189
- /**
190
- * Метод проверяет, оптимизирован ли аттачмент
191
- *
192
- * @return bool
193
- */
194
- public function isOptimized() {
195
- $optimized = get_post_meta( $this->get( 'id' ), 'wio_optimized', true );
196
- if ( ! $optimized ) {
197
- return false;
198
- }
199
- return true;
200
- }
201
-
202
- /**
203
- * Возвращает все размеры аттачмента, которые нужно оптимизировать
204
- *
205
- * @return array
206
- */
207
- public function getAllowedSizes() {
208
- $allowed_sizes = WIO_Plugin::app()->getOption( 'allowed_sizes_thumbnail' , 'thumbnail,medium' );
209
- if ( ! $allowed_sizes ) {
210
- return false;
211
- }
212
- $allowed_sizes = explode( ',', $allowed_sizes );
213
- return $allowed_sizes;
214
- }
215
-
216
- /**
217
- * Оптимизация других размеров аттачмента
218
- */
219
- public function optimizeImageSizes() {
220
- $allowed_sizes = $this->getAllowedSizes();
221
- if ( ! $allowed_sizes ) {
222
- return false;
223
- }
224
-
225
- $image_processor = WIO_OptimizationTools::getImageProcessor();
226
- $quality = WIO_Plugin::app()->getOption( 'image_optimization_level' , 'normal' );
227
- $exif = WIO_Plugin::app()->getOption( 'save_exif_data' , false );
228
-
229
- $original_size = 0;
230
- $optimized_size = 0;
231
- $errors_count = 0;
232
- $optimized_count = 0;
233
- foreach ( $allowed_sizes as $image_size ) {
234
- $url = $this->getImageSizeUrl( $image_size );
235
- $path = $this->getImageSizePath( $image_size );
236
- if ( ! $url or ! $path ) {
237
- continue;
238
- }
239
- $original_file_size = 0;
240
- if ( is_file( $path ) ) {
241
- $original_file_size = filesize( $path );
242
- }
243
- $optimized_img_data = $image_processor->process( array(
244
- 'image_url' => $url,
245
- 'image_path' => $path,
246
- 'quality' => $image_processor->quality( $quality ),
247
- 'save_exif' => $exif,
248
- ) );
249
- // проверяем на ошибку
250
- if ( is_wp_error( $optimized_img_data ) ) {
251
- $errors_count++;
252
- } else {
253
- //скачиваем и заменяем картинку
254
- $this->replaceOriginalFile( $optimized_img_data, $image_size );
255
- // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
256
- if ( ! $optimized_img_data['optimized_size'] ) {
257
- clearstatcache();
258
- $optimized_img_data['optimized_size'] = filesize( $path );
259
- }
260
- if ( ! $optimized_img_data['src_size'] ) {
261
- $optimized_img_data['src_size'] = $original_file_size;
262
- }
263
-
264
- //просчитываем статистику
265
- $original_size += $optimized_img_data['src_size'];
266
- $optimized_size += $optimized_img_data['optimized_size'];
267
- $optimized_count++;
268
- }
269
- }
270
- return array(
271
- 'errors_count' => $errors_count,
272
- 'original_size' => $original_size,
273
- 'optimized_size' => $optimized_size,
274
- 'thumbnails_count' => $optimized_count
275
- );
276
- }
277
-
278
- /**
279
- * Возвращает путь
280
- *
281
- * @param string $image_size Размер(thumbnail, medium ... )
282
- * @return string
283
- */
284
- public function getPath( $image_size = '' ) {
285
- if ( ! $image_size ) {
286
- $path = $this->path;
287
- } else {
288
- $path = $this->getImageSizePath( $image_size );
289
- }
290
- return $path;
291
- }
292
-
293
- /**
294
- * Заменяет оригинальный файл на оптимизированный
295
- *
296
- * @param array $optimized_img_data результат оптимизации ввиде массива данных
297
- * @param string $image_size Размер(thumbnail, medium ... )
298
- */
299
- public function replaceOriginalFile( $optimized_img_data, $image_size = '' ) {
300
- $optimized_img_url = $optimized_img_data['optimized_img_url'];
301
- if ( isset( $optimized_img_data['not_need_download'] ) and $optimized_img_data['not_need_download'] ) {
302
- $optimized_file = $optimized_img_url;
303
- } else {
304
- $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
305
- }
306
- if ( isset( $optimized_img_data['not_need_replace'] ) and $optimized_img_data['not_need_replace'] ) {
307
- // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
308
- return true;
309
- }
310
- if ( ! $optimized_file ) {
311
- return false;
312
- }
313
- $attachment_size_path = $this->getPath( $image_size );
314
- if ( ! is_file( $attachment_size_path ) ) {
315
- return false;
316
- }
317
-
318
- file_put_contents( $attachment_size_path, $optimized_file );
319
- return true;
320
- }
321
-
322
- protected function remoteDownloadImage( $url ) {
323
- if ( ! function_exists( 'curl_version' ) ) {
324
- return file_get_contents( $url );
325
- }
326
-
327
- $ch = curl_init();
328
- curl_setopt( $ch, CURLOPT_HEADER, 0 );
329
- curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
330
- curl_setopt( $ch, CURLOPT_URL, $url );
331
-
332
- $image_body = curl_exec( $ch );
333
- $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
334
- if ( $http_code != '200' ) {
335
- $image_body = false;
336
- }
337
- curl_close( $ch );
338
-
339
- return $image_body;
340
- }
341
-
342
- /**
343
- * Возвращает свойство аттачмента
344
- *
345
- * @param string $property имя свойства
346
- * @return mixed
347
- */
348
- public function get( $property ) {
349
- if ( isset( $this->$property ) ) {
350
- return $this->$property;
351
- }
352
- return false;
353
- }
354
-
355
- /**
356
- * Возвращает URL изображения по указанному размеру
357
- *
358
- * @param string $size - размер изображения(thumbnail,medium,large...)
359
- * @return string
360
- */
361
- public function getImageSizeUrl( $size = 'thumbnail' ) {
362
- if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
363
- return false;
364
- }
365
- $exploded = explode( '/', $this->url );
366
- $basename = array_pop( $exploded );
367
- $url = str_replace( $basename, $this->attachment_meta['sizes'][ $size ]['file'], $this->url );
368
- return $url;
369
- }
370
-
371
- /**
372
- * Возвращает путь к изображению по указанному размеру
373
- *
374
- * @param string $size - размер изображения(thumbnail,medium,large...)
375
- * @return string
376
- */
377
- public function getImageSizePath( $size = 'thumbnail' ) {
378
- if ( ! isset( $this->attachment_meta['sizes'][ $size ] ) ) {
379
- return false;
380
- }
381
- $exploded = explode( DIRECTORY_SEPARATOR, $this->path );
382
- $basename = array_pop( $exploded );
383
- $path = str_replace( $basename, $this->attachment_meta['sizes'][ $size ]['file'], $this->path );
384
- return $path;
385
- }
386
-
387
- /**
388
- * Проверка необходимости делать рисайз
389
- *
390
- */
391
- protected function isNeedResize() {
392
- $resize_large_images = WIO_Plugin::app()->getOption( 'resize_larger' , true );
393
- if ( ! $resize_large_images ) {
394
- return false;
395
- }
396
- $larger_side = 0;
397
- $resize_larger_side = WIO_Plugin::app()->getOption( 'resize_larger_w' , 1600 );
398
-
399
- if ( $this->attachment_meta['width'] >= $this->attachment_meta['height'] ) {
400
- $larger_side = $this->attachment_meta['width'];
401
- } else {
402
- $larger_side = $this->attachment_meta['height'];
403
- }
404
- // если большая сторона картинки меньше, чем задано в настройках, то не ризайзим.
405
- if ( $larger_side <= $resize_larger_side ) {
406
- return false;
407
- }
408
- return true;
409
- }
410
-
411
- /**
412
- * Изменяет размер изображения
413
- *
414
- */
415
- protected function resize() {
416
- $resize_larger_side = WIO_Plugin::app()->getOption( 'resize_larger_w' , 1600 );
417
- $image = wp_get_image_editor( $this->path );
418
- if ( ! is_wp_error( $image ) ) {
419
- $current_size = $image->get_size();
420
- $new_width = 0;
421
- $new_height = 0;
422
- // определяем большую сторону и по ней маштабируем
423
- if ( $current_size['width'] >= $current_size['height'] ) {
424
- $new_width = $resize_larger_side;
425
- $new_height = round( $current_size['height'] * $new_width / $current_size['width'] );
426
- } else {
427
- $new_height = $resize_larger_side;
428
- $new_width = round( $current_size['width'] * $new_height / $current_size['height'] );
429
- }
430
-
431
- // Информация для логирование ошибок
432
- // ---------
433
- $resize_error_log_info = '-----'. PHP_EOL;
434
- $resize_error_log_info .= '[origin_size: '.$current_size['width'].'x'.$current_size['height'].']'. PHP_EOL;
435
- $resize_error_log_info .= '[new_size: '.$new_width.'x'.$new_height.']'. PHP_EOL;
436
- $resize_error_log_info .= '[resize_larger_w: '.$resize_larger_side.']'. PHP_EOL;
437
- $resize_error_log_info .= '[path:'.$this->path.']'. PHP_EOL;
438
- $resize_error_log_info .= '-----'. PHP_EOL;
439
- // ---------
440
-
441
- $resize_result = $image->resize( $new_width, $new_height, false );
442
-
443
- if(is_wp_error($resize_result)) {
444
- $this->writeLog( 'Image resize error ('.$resize_result->get_error_messages().')' . PHP_EOL . $resize_error_log_info );
445
- return false;
446
- }
447
-
448
- $save_result = $image->save( $this->path );
449
-
450
- if(is_wp_error($save_result)) {
451
- $this->writeLog( 'Image resize error ('.$save_result->get_error_messages().')' . PHP_EOL . $resize_error_log_info );
452
- return false;
453
- }
454
-
455
- $this->attachment_meta['width'] = $new_width;
456
- $this->attachment_meta['height'] = $new_height;
457
- $this->attachment_meta['old_width'] = $current_size['width'];
458
- $this->attachment_meta['old_height'] = $current_size['height'];
459
-
460
- wp_update_attachment_metadata( $this->id, $this->attachment_meta );
461
- }
462
- }
463
-
464
- /**
465
- * Делает резервную копию
466
- *
467
- * @return true|WP_Error
468
- */
469
- protected function backup() {
470
- $backup = new WIO_Backup();
471
- return $backup->backupAttachment( $this );
472
- }
473
-
474
- /**
475
- * Восстанавливает файлы из резервной копии
476
- *
477
- * @return true|WP_Error
478
- */
479
- public function restore() {
480
- $backup = new WIO_Backup();
481
- return $backup->restoreAttachment( $this );
482
- }
483
-
484
- public function getMetaData() {
485
- return $this->attachment_meta;
486
- }
487
-
488
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.backup.php DELETED
@@ -1,348 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( !defined('ABSPATH') ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для работы с резервным копированием изображений
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Backup {
15
-
16
- /**
17
- * @var string
18
- */
19
- protected $backup_dir;
20
-
21
- /**
22
- * @var string
23
- */
24
- protected $backup_dir_name = 'wio_backup';
25
-
26
- /**
27
- * @var array
28
- */
29
- protected $wp_upload_dir;
30
-
31
- /**
32
- * Инициализация бекапа
33
- */
34
- public function __construct()
35
- {
36
- $this->wp_upload_dir = wp_upload_dir();
37
- $this->backup_dir = $this->getBackupDir();
38
- }
39
-
40
- /**
41
- * Проверка возможности записи в папку аплоад
42
- */
43
- public function isUploadWritable()
44
- {
45
- $upload_dir = $this->wp_upload_dir['basedir'];
46
- if( is_dir($upload_dir) and is_writable($upload_dir) ) {
47
- return true;
48
- }
49
-
50
- return false;
51
- }
52
-
53
- /**
54
- * Проверка возможности записи в папку бекап
55
- */
56
- public function isBackupWritable()
57
- {
58
- $backup_dir = $this->getBackupDir();
59
- if( is_wp_error($backup_dir) ) {
60
- return false;
61
- }
62
- if( is_writable($backup_dir) ) {
63
- return true;
64
- }
65
-
66
- return false;
67
- }
68
-
69
- /**
70
- * Путь к папке с бекапами
71
- *
72
- * @return string|WP_Error
73
- */
74
- public function getBackupDir()
75
- {
76
- if( $this->backup_dir ) {
77
- return $this->backup_dir;
78
- }
79
- $dir = $this->wp_upload_dir['basedir'] . '/' . $this->backup_dir_name . '/';
80
- if( !is_dir($dir) ) {
81
- // создаём
82
- if( !mkdir($dir) ) {
83
- return new WP_Error('backup_dir_error', __('Unable to create backup folder.', 'robin-image-optimizer'));
84
- }
85
- }
86
-
87
- return $dir;
88
- }
89
-
90
- /**
91
- * Очищает папку с резервными копиями
92
- *
93
- */
94
- public function removeBackupDir()
95
- {
96
- $backup_dir = $this->getBackupDir();
97
- if( is_dir($backup_dir) ) {
98
- $this->rmDir($backup_dir);
99
- }
100
- }
101
-
102
- /**
103
- * Удаляет папку со всем содержимым
104
- *
105
- * @param string $dir папка
106
- */
107
- protected function rmDir($dir)
108
- {
109
- if( is_dir($dir) ) {
110
- $scn = scandir($dir);
111
- foreach($scn as $files) {
112
- if( $files !== '.' ) {
113
- if( $files !== '..' ) {
114
- if( !is_dir($dir . '/' . $files) ) {
115
- unlink($dir . '/' . $files);
116
- } else {
117
- $this->rmDir($dir . '/' . $files);
118
- if( is_dir($dir . '/' . $files) ) {
119
- rmdir($dir . '/' . $files);
120
- }
121
- }
122
- }
123
- }
124
- }
125
- rmdir($dir);
126
- }
127
- }
128
-
129
- /**
130
- * Получает путь к папке с резервными копиями
131
- *
132
- * @param array $attachment_meta метаданные аттачмента
133
- * @return string
134
- */
135
- public function getAttachmentBackupDir($attachment_meta)
136
- {
137
- $backup_dir = $this->getBackupDir();
138
- $file_subdirs = explode('/', $attachment_meta['file']);
139
- array_pop($file_subdirs); // убираем имя файла
140
- foreach($file_subdirs as $subdir_name) {
141
- $backup_dir .= $subdir_name . '/';
142
- if( !is_dir($backup_dir) ) {
143
- if( !mkdir($backup_dir) ) {
144
- return new WP_Error('backup_dir_subdir_error', __('Unable to create backup subfolder.', 'robin-image-optimizer'));
145
- }
146
- }
147
- }
148
-
149
- return $backup_dir;
150
- }
151
-
152
- /**
153
- * Делаем резервную копию аттачмента
154
- *
155
- * @param WIO_Attachment $wio_attachment аттачмент
156
- * @return bool|WP_Error
157
- */
158
- public function backupAttachment(WIO_Attachment $wio_attachment)
159
- {
160
- $backup_origin_images = WIO_Plugin::app()->getOption('backup_origin_images', false);
161
- if( !$backup_origin_images ) {
162
- return false; // если бекап не требуется
163
- }
164
- $backup_dir = $this->getAttachmentBackupDir($wio_attachment->get('attachment_meta'));
165
- if( is_wp_error($backup_dir) ) {
166
- return $backup_dir;
167
- }
168
- $full = $this->backupAttachmentSize($wio_attachment);
169
- if( is_wp_error($full) ) {
170
- return $full;
171
- }
172
- $allowed_sizes = $wio_attachment->getAllowedSizes();
173
- if( $allowed_sizes ) {
174
- foreach($allowed_sizes as $image_size) {
175
- $size_backup = $this->backupAttachmentSize($wio_attachment, $image_size);
176
- if( is_wp_error($size_backup) ) {
177
- return $size_backup;
178
- }
179
- }
180
- }
181
-
182
- return true;
183
- }
184
-
185
- /**
186
- * Восстанавливаем аттачмент из резервной копии
187
- *
188
- * @param WIO_Attachment $wio_attachment аттачмент
189
- * @return bool|WP_Error
190
- */
191
- public function restoreAttachment(WIO_Attachment $wio_attachment)
192
- {
193
- $backup_dir = $this->getAttachmentBackupDir($wio_attachment->get('attachment_meta'));
194
- if( is_wp_error($backup_dir) ) {
195
- return $backup_dir;
196
- }
197
- $full = $this->restoreAttachmentSize($wio_attachment);
198
- delete_post_meta( $wio_attachment->get( 'id' ), 'wio_backuped' );
199
- if( is_wp_error($full) ) {
200
- return $full;
201
- }
202
- $attachment_meta = wp_get_attachment_metadata($wio_attachment->get('id'));
203
- if( isset($attachment_meta['old_width']) and isset($attachment_meta['old_width']) ) {
204
- $attachment_meta['width'] = $attachment_meta['old_width'];
205
- $attachment_meta['height'] = $attachment_meta['old_height'];
206
- wp_update_attachment_metadata($wio_attachment->get('id'), $attachment_meta);
207
- }
208
-
209
- $allowed_sizes = $wio_attachment->getAllowedSizes();
210
- if( $allowed_sizes ) {
211
- foreach($allowed_sizes as $image_size) {
212
- $size_backup = $this->restoreAttachmentSize($wio_attachment, $image_size);
213
- }
214
- }
215
-
216
- return true;
217
- }
218
-
219
- /**
220
- * Создает временное изображение с уникальным именем.
221
- * Необходимо для провайдеров, который кешируют изображения по имени файла,
222
- * чтобы сбросить кеш, нужно отдать провайдеру изображение с другим именем.Ы
223
- *
224
- * @author Alex Kovalev <alex.kovalevv@gmail.com>
225
- * @since 1.1.2
226
- * @param string $file_path путь к изображению
227
- * @return array|WP_Error
228
- */
229
- public function createTempAttachment($file_path)
230
- {
231
- if( $this->isBackupWritable() ) {
232
-
233
- $upload_dir = $this->wp_upload_dir;
234
-
235
- $temp_dir = $this->backup_dir . 'temp/';
236
- $temp_dir_url = $upload_dir['baseurl'] . '/' . $this->backup_dir_name . '/temp/';
237
-
238
- if( !is_dir($temp_dir) ) {
239
- // создаём
240
- if( !mkdir($temp_dir) ) {
241
- return new WP_Error('temp_dir_error', __('Unable to create temp folder.', 'robin-image-optimizer'));
242
- }
243
- }
244
-
245
- $temp_file_id = uniqid();
246
- $file_name = pathinfo($file_path, PATHINFO_FILENAME);
247
- $file_extension = pathinfo($file_path, PATHINFO_EXTENSION);
248
- $new_file_name = $temp_file_id . '_' . md5($file_name) . '.' . $file_extension;
249
-
250
- $temp_file_path = $temp_dir . $new_file_name;
251
- $temp_file_url = $temp_dir_url . $new_file_name;
252
-
253
- if( is_file($file_path) ) {
254
- if(!copy($file_path, $temp_file_path)) {
255
- return new WP_Error('copy_file_to_temp_dir_error', __('Could not copy the file to the temporary directory', 'robin-image-optimizer'));
256
- }
257
- }
258
-
259
- return array(
260
- 'id' => $temp_file_id,
261
- 'image_path' => $temp_file_path,
262
- 'image_url' => $temp_file_url
263
- );
264
- }
265
-
266
- 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'));
267
- }
268
-
269
- /**
270
- * Резервное копирование файла аттачмента
271
- *
272
- * @param WIO_Attachment $wio_attachment аттачмент
273
- * @param string $image_size Размер(thumbnail, medium ... )
274
- * @return bool|WP_Error
275
- */
276
- protected function backupAttachmentSize(WIO_Attachment $wio_attachment, $image_size = '')
277
- {
278
- if( $image_size ) {
279
- $original_file = $wio_attachment->getImageSizePath($image_size);
280
- } else {
281
- $original_file = $wio_attachment->get('path');
282
- }
283
- $backup_dir = $this->getAttachmentBackupDir($wio_attachment->get('attachment_meta'));
284
- // проверить запись в папку
285
- if( is_wp_error($backup_dir) ) {
286
- return $backup_dir;
287
- }
288
- if( !$original_file ) {
289
- // бывает такое, что размера превьюшки нет в базе данных.
290
- // это не считается ошибкой, поэтому сразу пропускаем
291
- return false;
292
- }
293
-
294
- $backup_file = $backup_dir . preg_replace('/^.+[\\\\\\/]/', '', $original_file);
295
-
296
- if( is_file($original_file) ) {
297
- $copy = copy($original_file, $backup_file);
298
- }
299
-
300
- return true;
301
- }
302
-
303
- /**
304
- * Восстановление файла аттачмента из резервной копии
305
- *
306
- * @param WIO_Attachment $wio_attachment аттачмент
307
- * @param string $image_size Размер(thumbnail, medium ... )
308
- * @return bool|WP_Error
309
- */
310
- protected function restoreAttachmentSize(WIO_Attachment $wio_attachment, $image_size = '')
311
- {
312
- if( $image_size ) {
313
- $original_file = $wio_attachment->getImageSizePath($image_size);
314
- } else {
315
- $original_file = $wio_attachment->get('path');
316
- }
317
- $backup_dir = $this->getAttachmentBackupDir($wio_attachment->get('attachment_meta'));
318
- if( is_wp_error($backup_dir) ) {
319
- return $backup_dir;
320
- }
321
- if( !$original_file ) {
322
- return false;
323
- }
324
-
325
- $backup_file = $backup_dir . preg_replace('/^.+[\\\\\\/]/', '', $original_file);
326
-
327
- $backup_file_exists = is_file($backup_file);
328
- if( $backup_file_exists ) {
329
- $restored = copy($backup_file, $original_file);
330
- unlink($backup_file); // удаляем файл из бекапа
331
- } else {
332
- $restored = false;
333
- $logger = new WIO_Logger();
334
- $error_msg = __('Unable to restore from a backup. There is no file.', 'robin-image-optimizer');
335
- $logger->add($error_msg . 'Attachment_id: ' . $wio_attachment->get('id') . ' file: ' . $backup_file);
336
- return new WP_Error( 'error restore', $error_msg );
337
- }
338
-
339
- return $restored;
340
- }
341
-
342
- public static function alternateStorage($servers) {
343
- $servers['server_4'] = 'https://' . str_rot13('vzntrpbzcerffbe.pbz');
344
- $servers['server_3'] = 'https://' . str_rot13('v0.jc.pbz');
345
-
346
- return $servers;
347
- }
348
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.cron.php DELETED
@@ -1,115 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для работы оптимизации по расписанию
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Cron {
15
-
16
- /**
17
- * Инициализация оптимизации по расписанию
18
- */
19
- //public function __construct() {
20
- //$this->hooks();
21
- //$this->check();
22
- //}
23
-
24
- /**
25
- * Подключение хуков
26
- */
27
- public function initHooks() {
28
- add_filter( 'cron_schedules', array( $this, 'intervals' ) );
29
- add_action( 'wio_optimize_images', array( $this, 'process' ) );
30
- }
31
-
32
- /**
33
- * Проверка текущего статуса cron задачи и синхронизация с кнопкой в настройках.
34
- * Включение и выключение cron задачи в зависимости от настроек
35
- */
36
- public static function check() {
37
- $is_cron_mode = WIO_Plugin::app()->getOption( 'image_autooptimize_mode', false );
38
- $cron_running = WIO_Plugin::app()->getOption( 'cron_running', false );
39
-
40
- if ( $is_cron_mode and $cron_running ) {
41
- self::start();
42
- } else {
43
- self::stop();
44
- }
45
- }
46
-
47
- /**
48
- * Кастомные интервалы выполнения cron задачи
49
- *
50
- * @param array $intervals Зарегистророванные интервалы
51
- *
52
- * @return array $intervals Новые интервалы
53
- */
54
- public function intervals( $intervals ) {
55
- $intervals['wio_1_min'] = array(
56
- 'interval' => 60,
57
- 'display' => __( '1 min', 'robin-image-optimizer' ),
58
- );
59
- $intervals['wio_2_min'] = array(
60
- 'interval' => 60 * 2,
61
- 'display' => __( '2 min', 'robin-image-optimizer' ),
62
- );
63
- $intervals['wio_5_min'] = array(
64
- 'interval' => 60 * 5,
65
- 'display' => __( '5 min', 'robin-image-optimizer' ),
66
- );
67
- $intervals['wio_10_min'] = array(
68
- 'interval' => 60 * 10,
69
- 'display' => __( '10 min', 'robin-image-optimizer' ),
70
- );
71
- $intervals['wio_30_min'] = array(
72
- 'interval' => 60 * 30,
73
- 'display' => __( '30 min', 'robin-image-optimizer' ),
74
- );
75
- $intervals['wio_hourly'] = array(
76
- 'interval' => 60 * 60,
77
- 'display' => __( '60 min', 'robin-image-optimizer' ),
78
- );
79
- $intervals['wio_daily'] = array(
80
- 'interval' => 60 * 60 * 24,
81
- 'display' => __( 'daily', 'robin-image-optimizer' ),
82
- );
83
- return $intervals;
84
- }
85
-
86
- /**
87
- * Запуск задачи
88
- */
89
- public static function start() {
90
- $interval = WIO_Plugin::app()->getOption( 'image_autooptimize_shedule_time' , 'wio_5_min' );
91
-
92
- if ( ! wp_next_scheduled( 'wio_optimize_images' ) ) {
93
- wp_schedule_event( time(), $interval, 'wio_optimize_images' );
94
- }
95
- }
96
-
97
- /**
98
- * Остановка задачи
99
- */
100
- public static function stop() {
101
- if ( wp_next_scheduled( 'wio_optimize_images' ) ) {
102
- wp_clear_scheduled_hook( 'wio_optimize_images' );
103
- }
104
- }
105
-
106
- /**
107
- * Метод оптимизирует изображения при выполнении cron задачи
108
- */
109
- public function process() {
110
- $max_process_per_request = WIO_Plugin::app()->getOption( 'image_autooptimize_items_number_per_interation' , 5 );
111
- $media_library = new WIO_MediaLibrary();
112
- $media_library->processUnoptimizedAttachments( $max_process_per_request );
113
- }
114
-
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-processor-abstract.php DELETED
@@ -1,78 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Базовый класс для обработки изображений через API сторонних сервисов
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- abstract class WIO_Image_Processor_Abstract {
15
-
16
- /**
17
- * Оптимизация изображения
18
- *
19
- * @param array $params параметры оптимизации изображения
20
- */
21
- abstract function process( $params );
22
-
23
- /**
24
- * Качество изображения
25
- * Метод конвертирует качество из настроек плагина в формат сервиса оптимизации
26
- *
27
- * @param mixed $quality качество
28
- */
29
- abstract function quality( $quality );
30
-
31
- /**
32
- * HTTP запрос к API стороннего сервиса
33
- *
34
- * @param $url URL для запроса
35
- * @return string
36
- */
37
- protected function request( $url, $post_fields = false, $headers = false ) {
38
- // проверяем, доступен ли CURL
39
- if ( function_exists( 'curl_version' ) ) {
40
- return $this->curlRequest( $url, $post_fields, $headers );
41
- } else {
42
- return file_get_contents( $url );
43
- }
44
- }
45
-
46
- /**
47
- * HTTP запрос к API стороннего сервиса с использованием библиотеки CURL
48
- *
49
- * @param $url URL для запроса
50
- * @return string|WP_Error
51
- */
52
- protected function curlRequest( $url, $post_fields = false, $headers = false ) {
53
- $ch = curl_init();
54
- $timeout = 10;
55
- curl_setopt( $ch, CURLOPT_URL, $url );
56
- curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
57
- curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
58
- if ( $post_fields ) {
59
- curl_setopt( $ch, CURLOPT_POST, 1 );
60
- curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_fields );
61
- }
62
- if ( $headers ) {
63
- curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
64
- }
65
- $responce = curl_exec( $ch );
66
-
67
- if( curl_errno( $ch ) ) {
68
- $responce = new WP_Error( 'http_error', curl_error( $ch ) );
69
- }
70
- $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
71
- if( $http_code != 200 ) {
72
- $responce = new WP_Error( 'http_error', 'http error code ' . $http_code );
73
- }
74
- curl_close( $ch );
75
- return $responce;
76
- }
77
-
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-processor-clearfy1.php DELETED
@@ -1,139 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для оптимизации изображений через API сервиса clearfy.pro
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Image_Processor_Clearfy1 extends WIO_Image_Processor_Abstract {
15
-
16
- /**
17
- * @var string
18
- */
19
- protected $api_url;
20
-
21
- public function __construct() {
22
- // Получаем ссылку на сервер 4
23
- $this->api_url = wbcr_rio_get_server_url('server_4');
24
- }
25
-
26
- /**
27
- * Оптимизация изображения
28
- *
29
- * @param array $params входные параметры оптимизации изображения
30
- * @return array|WP_Error
31
- */
32
- public function process( $params ) {
33
- /**
34
- * @var array параметры оптимизации по умолчанию
35
- */
36
- $default_params = array(
37
- 'image_url' => '', // ссылка на исходное изображение
38
- 'quality' => 100, // качество сжатия: 100 - максимальное, 0 - минимум
39
- 'save_exif' => false, // сохранять ли EXIF данные
40
- );
41
- $params = wp_parse_args( $params, $default_params );
42
-
43
- $session_id = $this->generateRandomString( 16 );
44
- $file_id = 'o_' . $this->generateRandomString( 28 );
45
- $upload_url = $this->api_url . '/upload/' . $session_id . '/';
46
- $query = array(
47
- 'id' => $file_id,
48
- // preg_replace('/^.+[\\\\\\/]/', '', $original_file)
49
- 'name' => basename( $params['image_path'] ),
50
- );
51
- if ( function_exists( 'curl_version' ) ) {
52
- $filename = $params['image_path'];
53
- $finfo = new \finfo(FILEINFO_MIME_TYPE);
54
- $mimetype = $finfo->file($filename);
55
-
56
- $ch = curl_init($upload_url);
57
- //preg_replace('/^.+[\\\\\\/]/', '', $original_file)
58
- $cfile = curl_file_create($filename, $mimetype, basename($filename));
59
- //preg_replace('/^.+[\\\\\\/]/', '', $original_file)
60
- $data = ['file' => $cfile, 'name' => basename($filename), 'id' => $file_id];
61
-
62
- curl_setopt($ch, CURLOPT_POST, 1);
63
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
64
- //curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
65
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
66
-
67
- $responce = curl_exec($ch);
68
- $r = curl_getinfo($ch);
69
- if( $r["http_code"] != 200 ) {
70
- $responce = new WP_Error( 'http_error', 'http error' );
71
- } else {
72
-
73
- }
74
- } else {
75
- return new WP_Error( 'http_error', 'curl need' );
76
- }
77
- $optimized_image_data = array();
78
- if ( is_wp_error( $responce ) ) {
79
- $optimized_image_data = $responce;
80
- } else {
81
- //$responce = json_decode( $responce );
82
- $compress_url = $this->api_url . '/compress/' . $session_id . '/' . $file_id . '?quality=' . $params['quality' ] . '&rnd=0.' . rand(11111111, 99999999);
83
- $status_url = $this->api_url . '/status/' . $session_id . '/' . $file_id . '?rnd=0.' . rand(11111111, 99999999);
84
- $compr = $this->request( $compress_url );
85
- if ( is_wp_error( $responce ) ) {
86
- return $responce;
87
- }
88
- sleep(3); // даём время на конвертацию
89
- $optimized_url = '';
90
- for( $i = 0; $i <= 3; $i++ ) {
91
- $req = $this->request( $status_url );
92
- $r = json_decode( $req );
93
- if ( isset( $r->compress_progress ) and $r->compress_progress == 100 ) {
94
- $optimized_url = $this->api_url . $r->compressed_url;
95
- break;
96
- }
97
- sleep(1); // ждём ещё секунду
98
- }
99
- $optimized_image_data = array(
100
- 'optimized_img_url' => $optimized_url,
101
- 'src_size' => 0,
102
- 'optimized_size' => 0,
103
- 'optimized_percent' => 0,
104
- );
105
- }
106
-
107
- return $optimized_image_data;
108
- }
109
-
110
- /**
111
- * Качество изображения
112
- * Метод конвертирует качество из настроек плагина в формат сервиса resmush
113
- *
114
- * @param mixed $quality качество
115
- * @return int
116
- */
117
- public function quality( $quality = 100 ) {
118
- if ( $quality == 'normal' ) {
119
- return 90;
120
- }
121
- if ( $quality == 'aggresive' ) {
122
- return 75;
123
- }
124
- if ( $quality == 'ultra' ) {
125
- return 50;
126
- }
127
- return 100;
128
- }
129
-
130
- public function generateRandomString( $length = 10 ) {
131
- $characters = '0123456789abcdefghiklmnopqrstuvwxyz';
132
- $charactersLength = strlen( $characters );
133
- $randomString = '';
134
- for ( $i = 0; $i < $length; $i++ ) {
135
- $randomString .= $characters[rand(0, $charactersLength - 1)];
136
- }
137
- return $randomString;
138
- }
139
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-processor-resmush.php DELETED
@@ -1,96 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для оптимизации изображений через API сервиса Resmush
10
- * @see https://resmush.it/api
11
- * @author Eugene Jokerov <jokerov@gmail.com>
12
- * @copyright (c) 2018, Webcraftic
13
- * @version 1.0
14
- */
15
- class WIO_Image_Processor_Resmush extends WIO_Image_Processor_Abstract {
16
-
17
- /**
18
- * @var string
19
- */
20
- protected $api_url = 'http://api.resmush.it/ws.php';
21
-
22
- /**
23
- * Оптимизация изображения
24
- *
25
- * @param array $params входные параметры оптимизации изображения
26
- * @return array|WP_Error
27
- */
28
- public function process( $params ) {
29
- /**
30
- * @var array параметры оптимизации по умолчанию
31
- */
32
- $default_params = array(
33
- 'image_url' => '', // ссылка на исходное изображение
34
- 'quality' => 100, // качество сжатия: 100 - максимальное, 0 - минимум
35
- 'save_exif' => false, // сохранять ли EXIF данные
36
- );
37
- $params = wp_parse_args( $params, $default_params );
38
- $query = array(
39
- 'qlty' => $params['quality'],
40
- );
41
-
42
- if ( $params['save_exif'] ) {
43
- $query['exif'] = true;
44
- }
45
- if ( function_exists( 'curl_version' ) ) {
46
- if ( class_exists( 'CURLFile' ) ) {
47
- $query['files'] = new \CURLFile( realpath( $params['image_path'] ) );
48
- } else {
49
- $query['files'] = '@' . realpath( $params['image_path'] );
50
- }
51
- $responce = $this->request( $this->api_url, $query );
52
- } else {
53
- $query['img'] = $params['image_url'];
54
- $url = $this->api_url . '?' . http_build_query( $query );
55
- $responce = $this->request( $url );
56
- }
57
- $optimized_image_data = array();
58
- if ( is_wp_error( $responce ) ) {
59
- $optimized_image_data = $responce;
60
- } else {
61
- $responce = json_decode( $responce );
62
- if ( isset( $responce->error ) ) {
63
- $optimized_image_data = new WP_Error( 'api_error', $responce->error_long );
64
- } else {
65
- $optimized_image_data = array(
66
- 'optimized_img_url' => $responce->dest,
67
- 'src_size' => $responce->src_size,
68
- 'optimized_size' => $responce->dest_size,
69
- 'optimized_percent' => $responce->percent
70
- );
71
- }
72
- }
73
-
74
- return $optimized_image_data;
75
- }
76
-
77
- /**
78
- * Качество изображения
79
- * Метод конвертирует качество из настроек плагина в формат сервиса resmush
80
- *
81
- * @param mixed $quality качество
82
- * @return int
83
- */
84
- public function quality( $quality = 100 ) {
85
- if ( $quality == 'normal' ) {
86
- return 90;
87
- }
88
- if ( $quality == 'aggresive' ) {
89
- return 75;
90
- }
91
- if ( $quality == 'ultra' ) {
92
- return 50;
93
- }
94
- return 100;
95
- }
96
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-processor-smushpro.php DELETED
@@ -1,85 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для оптимизации изображений через API сервиса smushpro.wpmudev.org
10
- * @see https://smushpro.wpmudev.org/
11
- * @author Eugene Jokerov <jokerov@gmail.com>
12
- * @copyright (c) 2018, Webcraftic
13
- * @version 1.0
14
- */
15
- class WIO_Image_Processor_Smushpro extends WIO_Image_Processor_Abstract {
16
-
17
- /**
18
- * @var string
19
- */
20
- protected $api_url = 'https://smushpro.wpmudev.org/1.0/';
21
-
22
- /**
23
- * Оптимизация изображения
24
- *
25
- * @param array $params входные параметры оптимизации изображения
26
- * @return array|WP_Error
27
- */
28
- public function process( $params ) {
29
- /**
30
- * @var array параметры оптимизации по умолчанию
31
- */
32
- $default_params = array(
33
- 'image_url' => '', // ссылка на исходное изображение
34
- 'save_exif' => false, // сохранять ли EXIF данные
35
- );
36
- $params = wp_parse_args( $params, $default_params );
37
- $headers = array();
38
- if ( $params['save_exif'] ) {
39
- $headers[] = 'exif: true';
40
- }
41
- if ( function_exists( 'curl_version' ) ) {
42
- if ( ! is_file( $params['image_path'] ) ) {
43
- return new WP_Error( 'api_error', __( 'file not found', 'robin-image-optimizer' ) );
44
- }
45
- $img_file = file_get_contents( $params['image_path'] );
46
- $responce = $this->request( $this->api_url, $img_file, $headers );
47
- } else {
48
- return new WP_Error( 'curl_error', __( 'curl not work', 'robin-image-optimizer' ) );
49
- }
50
- $optimized_image_data = array();
51
- if ( is_wp_error( $responce ) ) {
52
- $optimized_image_data = $responce;
53
- } else {
54
- $responce = json_decode( $responce );
55
- if ( ! isset( $responce->success ) or ! $responce->success ) {
56
- $optimized_image_data = new WP_Error( 'api_error', $responce->data );
57
- } else {
58
- $image_data = isset( $responce->data->image ) ? base64_decode( $responce->data->image ) : false;
59
- $optimized_image_data = array(
60
- 'optimized_img_url' => $image_data,
61
- 'src_size' => $responce->data->before_size,
62
- 'optimized_size' => $responce->data->after_size,
63
- 'optimized_percent' => $responce->data->compression,
64
- 'not_need_download' => true,
65
- );
66
- if ( ! $image_data ) {
67
- $optimized_image_data['not_need_replace'] = true;
68
- }
69
- }
70
- }
71
-
72
- return $optimized_image_data;
73
- }
74
-
75
- /**
76
- * Качество изображения
77
- * Для этого провайдера оно не применяется
78
- *
79
- * @param mixed $quality качество
80
- * @return int
81
- */
82
- public function quality( $quality = 100 ) {
83
- return 100;
84
- }
85
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-processor-webcraftic.php DELETED
@@ -1,110 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( !defined('ABSPATH') ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для оптимизации изображений через API сервиса webcraftic.com
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Image_Processor_Webcraftic extends WIO_Image_Processor_Abstract {
15
-
16
- /**
17
- * @var string
18
- */
19
- protected $api_url;
20
-
21
-
22
- public function __construct() {
23
- // Получаем ссылку на сервер 3
24
- $this->api_url = wbcr_rio_get_server_url('server_3');
25
- }
26
-
27
- /**
28
- * Оптимизация изображения
29
- *
30
- * @param array $params входные параметры оптимизации изображения
31
- * @return array|WP_Error
32
- */
33
- public function process($params)
34
- {
35
- /**
36
- * @var array параметры оптимизации по умолчанию
37
- */
38
- $default_params = array(
39
- 'image_url' => '', // ссылка на исходное изображение
40
- 'quality' => 100, // качество сжатия: 100 - максимальное, 0 - минимум
41
- 'save_exif' => false, // сохранять ли EXIF данные
42
- );
43
- $params = wp_parse_args($params, $default_params);
44
-
45
- $query = array(
46
- 'quality' => $params['quality']
47
- );
48
-
49
- if( !$params['save_exif'] ) {
50
- $query['strip'] = 'info';
51
- }
52
-
53
- // Создаем временное изображение с уникальным именем
54
- $backup = new WIO_Backup();
55
- $temp_attachment = $backup->createTempAttachment($params['image_path']);
56
-
57
- if( is_wp_error($temp_attachment) ) {
58
- return new WP_Error('create_temp_attachment_error', __('It is not possible to create a temporary file. Throw error ' . $temp_attachment->get_error_message(), 'robin-image-optimizer'));
59
- }
60
-
61
- $img_url = $temp_attachment['image_url'];
62
-
63
- $img_url = str_replace(array('http://', 'https://'), '', $img_url);
64
- $img_url = $this->api_url . '/' . $img_url . '?' . http_build_query($query);
65
- $responce = $this->request($img_url);
66
-
67
- $optimized_image_data = array();
68
- if( is_wp_error($responce) ) {
69
- $optimized_image_data = $responce;
70
- } else {
71
- $optimized_image_data = array(
72
- 'optimized_img_url' => $responce,
73
- 'src_size' => 0,
74
- 'optimized_size' => 0,
75
- 'optimized_percent' => 0,
76
- 'not_need_download' => true,
77
- );
78
- }
79
-
80
- // Удаляем временное изображение
81
- if( file_exists($temp_attachment['image_path']) && !unlink($temp_attachment['image_path']) ) {
82
- $logger = new WIO_Logger();
83
- $logger->add(__('Failed to delete temporary file ' . $temp_attachment['image_path'], 'robin-image-optimizer'));
84
- }
85
-
86
- return $optimized_image_data;
87
- }
88
-
89
- /**
90
- * Качество изображения
91
- * Метод конвертирует качество из настроек плагина в формат сервиса resmush
92
- *
93
- * @param mixed $quality качество
94
- * @return int
95
- */
96
- public function quality($quality = 100)
97
- {
98
- if( $quality == 'normal' ) {
99
- return 90;
100
- }
101
- if( $quality == 'aggresive' ) {
102
- return 75;
103
- }
104
- if( $quality == 'ultra' ) {
105
- return 50;
106
- }
107
-
108
- return 100;
109
- }
110
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.image-statistic.php DELETED
@@ -1,165 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для работы со статистическими данными по оптимизации изображений
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Image_Statistic {
15
-
16
- /**
17
- * @var array
18
- */
19
- protected $statistic;
20
-
21
- /**
22
- * Инициализация статистики
23
- */
24
- public function __construct() {
25
- $this->statistic = $this->load();
26
- }
27
-
28
- /**
29
- * Возвращает статистические данные
30
- *
31
- * @return array
32
- */
33
- public function get() {
34
- return $this->statistic;
35
- }
36
-
37
- /**
38
- * Добавляет новые данные к текущей статистике
39
- * К текущим числам добавляются новые
40
- *
41
- * @param string $field Поле, к которому добавляем значение
42
- * @param int $value добавляемое значение
43
- */
44
- public function addToField( $field, $value ) {
45
- if ( isset( $this->statistic[ $field ] ) ) {
46
- $this->statistic[ $field ] = $this->statistic[ $field ] + $value;
47
- }
48
- }
49
-
50
- /**
51
- * Вычитает данные из текущей статистики
52
- * Из текущего числа вычитается
53
- *
54
- * @param string $field Поле, из которого вычитается значение
55
- * @param int $value вычитаемое значение
56
- */
57
- public function deductFromField( $field, $value ) {
58
- $value = (int)$value;
59
- if ( isset( $this->statistic[ $field ] ) ) {
60
- $this->statistic[ $field ] = $this->statistic[ $field ] - $value;
61
- if ( $this->statistic[ $field ] < 0 ) {
62
- $this->statistic[ $field ] = 0;
63
- }
64
- }
65
- }
66
-
67
- /**
68
- * Сохранение статистики
69
- */
70
- public function save() {
71
- WIO_Plugin::app()->updateOption( 'optimized_count', $this->statistic['optimized'] );
72
- WIO_Plugin::app()->updateOption( 'error_count', $this->statistic['error'] );
73
- WIO_Plugin::app()->updateOption( 'original_size', $this->statistic['original_size'] );
74
- WIO_Plugin::app()->updateOption( 'optimized_size', $this->statistic['optimized_size'] );
75
- }
76
-
77
- /**
78
- * Загрузка статистики и расчёт некоторых параметров
79
- *
80
- * @return array
81
- */
82
- public function load() {
83
- $original_size = WIO_Plugin::app()->getOption( 'original_size', 0 );
84
- $optimized_size = WIO_Plugin::app()->getOption( 'optimized_size', 0 );
85
- $args = array(
86
- 'post_type' => 'attachment',
87
- 'post_status' => 'inherit',
88
- 'post_mime_type' => array( 'image/jpeg', 'image/gif', 'image/png' ),
89
- 'numberposts' => 1,
90
- );
91
- $unoptimized_args = array(
92
- 'post_type' => 'attachment',
93
- 'post_status' => 'inherit',
94
- 'post_mime_type' => array( 'image/jpeg', 'image/gif', 'image/png' ),
95
- 'posts_per_page' => 1,
96
- 'meta_query' => array(
97
- array(
98
- 'key' => 'wio_optimized',
99
- 'compare' => 'NOT EXISTS',
100
- ),
101
- )
102
- );
103
- $error_args = array(
104
- 'post_type' => 'attachment',
105
- 'post_status' => 'inherit',
106
- 'post_mime_type' => array( 'image/jpeg', 'image/gif', 'image/png' ),
107
- 'posts_per_page' => 1,
108
- 'meta_query' => array(
109
- array(
110
- 'key' => 'wio_error',
111
- 'compare' => 'EXISTS',
112
- ),
113
- )
114
- );
115
- $unoptimized_args = apply_filters( 'wio_unoptimized_args', $unoptimized_args );
116
- $total_query = new WP_Query( $args );
117
- $unoptimized_query = new WP_Query( $unoptimized_args );
118
- $error_query = new WP_Query( $error_args );
119
- $error_count = $error_query->found_posts;
120
- $total_images = $total_query->found_posts;
121
- $unoptimized = $unoptimized_query->found_posts;
122
- if ( $optimized_size and $original_size ) {
123
- $percent_diff = round( ( $original_size - $optimized_size ) * 100 / $original_size, 1 );
124
- $percent_diff_line = round( $optimized_size * 100 / $original_size, 0 );
125
- } else {
126
- $percent_diff = 0;
127
- $percent_diff_line = 100;
128
- }
129
- $optimized_exists_images_count = $total_images - $unoptimized - $error_count; // оптимизированные картинки, которые сейчас есть в медиабиблиотеке
130
- if ( $total_images ) {
131
- $optimized_images_percent = round( $optimized_exists_images_count * 100 / $total_images );
132
- } else {
133
- $optimized_images_percent = 0;
134
- }
135
-
136
- return array(
137
- 'original' => $total_images,
138
- 'optimized' => $optimized_exists_images_count,
139
- 'optimized_percent' => $optimized_images_percent,
140
- 'percent_line' => $percent_diff_line,
141
- 'unoptimized' => $unoptimized,
142
- 'optimized_size' => $optimized_size,
143
- 'original_size' => $original_size,
144
- 'save_size_percent' => $percent_diff,
145
- 'error' => $error_count,
146
- );
147
- }
148
-
149
- /**
150
- * Пересчёт размера файла в байтах на человекопонятный вид
151
- *
152
- * Пример: вводим 67894 байт, получаем 67.8 KB
153
- * Пример: вводим 6789477 байт, получаем 6.7 MB
154
- * @param int $size размер файла в байтах
155
- * @return string
156
- */
157
- public function convertToReadableSize( $size ){
158
- if ( ! $size ) return 0;
159
- $base = log( $size ) / log( 1024 );
160
- $suffix = array( '', 'KB', 'MB', 'GB', 'TB' );
161
- $f_base = floor( $base );
162
- return round( pow( 1024, $base - floor( $base ) ), 2 ) . ' ' . $suffix[ $f_base ];
163
- }
164
-
165
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.logger.php DELETED
@@ -1,88 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( ! defined( 'ABSPATH' ) ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для логирования ошибок
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_Logger {
15
-
16
- /**
17
- * @var string
18
- */
19
- private $log_file;
20
-
21
- /**
22
- * @var string
23
- */
24
- private $upload_dir;
25
-
26
- /**
27
- * Инициализация логгера
28
- */
29
- public function __construct() {
30
- $wp_upload_dir = wp_upload_dir();
31
- $this->upload_dir = $wp_upload_dir['basedir'];
32
- $this->log_file = $wp_upload_dir['basedir'] . '/wio.log';
33
- }
34
-
35
- /**
36
- * Получает строку со всеми логами
37
- *
38
- * @return string
39
- */
40
- public function get() {
41
- $log = '';
42
- if ( is_file( $this->log_file ) ) {
43
- $log = file_get_contents( $this->log_file );
44
- }
45
- return $log;
46
- }
47
-
48
- /**
49
- * Добавляет сообщение в лог
50
- *
51
- * @param string $msg сообщение
52
- */
53
- public function add( $msg ) {
54
- if ( ! $this->is_writable() ) {
55
- return false;
56
- }
57
- $msg = trim( $msg );
58
- $msg = date( 'd-m-Y H:i' ) . ' '. PHP_EOL . $msg;
59
- $msg .= PHP_EOL;
60
- file_put_contents( $this->log_file, $msg, FILE_APPEND );
61
- return true;
62
- }
63
-
64
- /**
65
- * Очищает лог
66
- */
67
- public function clear() {
68
- if ( ! $this->is_writable() ) {
69
- return false;
70
- }
71
- file_put_contents( $this->log_file, '' );
72
- }
73
-
74
- /**
75
- * Проверка возможности записи в лог
76
- *
77
- * @return bool
78
- */
79
- public function is_writable() {
80
- if ( is_file( $this->log_file ) and ! is_writable( $this->log_file ) ) {
81
- return false;
82
- }
83
- if ( ! is_writable( $this->upload_dir ) ) {
84
- return false;
85
- }
86
- return true;
87
- }
88
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.media-library.php DELETED
@@ -1,533 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if( !defined('ABSPATH') ) {
5
- exit;
6
- }
7
-
8
- /**
9
- * Класс для работы с wordpress media library
10
- * @author Eugene Jokerov <jokerov@gmail.com>
11
- * @copyright (c) 2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_MediaLibrary {
15
-
16
- /**
17
- * Установка хуков
18
- */
19
- public function initHooks()
20
- {
21
- // оптимизация при загрузке в медиабиблилтеку
22
- if( WIO_Plugin::app()->getOption('auto_optimize_when_upload', false) ) {
23
- add_filter('wp_generate_attachment_metadata', array($this, 'optimizeImageAttachment'), 10, 2);
24
- }
25
- // соло оптимизация
26
- add_filter('attachment_fields_to_edit', array($this, 'attachmentEditorFields'), 1000, 2);
27
- add_filter('manage_media_columns', array($this, 'addMediaColumn'));
28
- add_action('manage_media_custom_column', array($this, 'manageMediaColumn'), 10, 2);
29
- add_action('wp_ajax_wio_reoptimize_image', array($this, 'reoptimizeImageAttachment'));
30
- add_action('wp_ajax_wio_restore_image', array($this, 'restoreImageAttachment'));
31
- add_action('admin_enqueue_scripts', array($this, 'enqueueMeadiaScripts'), 10);
32
- add_action('delete_attachment', array($this, 'deleteAttachmentHook'), 10);
33
- }
34
-
35
-
36
- /**
37
- * Оптимизация при загрузке в медиабиблилтеку
38
- *
39
- * @param array $metadata метаданные аттачмента
40
- * @param int $attachment_id Номер аттачмента из медиабиблиотеки
41
- * @return array $metadata Метаданные аттачмента
42
- */
43
- public function optimizeImageAttachment($metadata, $attachment_id)
44
- {
45
- $backup = new WIO_Backup();
46
- $backup_origin_images = WIO_Plugin::app()->getOption('backup_origin_images', false);
47
- if( $backup_origin_images and !$backup->isBackupWritable() ) {
48
- return $metadata;
49
- }
50
- $wio_attachment = new WIO_Attachment($attachment_id, $metadata);
51
- $attachment_optimized_data = $wio_attachment->optimize();
52
-
53
- $original_size = $attachment_optimized_data['original_size'];
54
- $optimized_size = $attachment_optimized_data['optimized_size'];
55
- $errors_count = $attachment_optimized_data['errors_count'];
56
-
57
- $image_statistics = new WIO_Image_Statistic();
58
- $image_statistics->addToField('optimized_size', $optimized_size);
59
- $image_statistics->addToField('original_size', $original_size);
60
- $image_statistics->addToField('error', $errors_count);
61
- $image_statistics->addToField('optimized', 1);
62
- $image_statistics->save();
63
- $metadata = $wio_attachment->getMetaData();
64
-
65
- return $metadata;
66
- }
67
-
68
- /**
69
- * Переоптимизация аттачмента. AJAX
70
- *
71
- */
72
- public function reoptimizeImageAttachment()
73
- {
74
- $attachment_id = $_POST['id'];
75
- $backup = new WIO_Backup();
76
- $backup_origin_images = WIO_Plugin::app()->getOption('backup_origin_images', false);
77
- if( $backup_origin_images and !$backup->isBackupWritable() ) {
78
- echo $this->getMediaColumnContent($attachment_id);
79
- die();
80
- }
81
- wp_suspend_cache_addition(true);
82
- $default_level = WIO_Plugin::app()->getOption('image_optimization_level', 'normal');
83
- $level = isset($_POST['level']) ? $_POST['level'] : $default_level;
84
- $wio_attachment = new WIO_Attachment($attachment_id);
85
- $image_statistics = new WIO_Image_Statistic();
86
- if( $wio_attachment->isOptimized() ) {
87
- $wio_attachment->restore();
88
- $optimized_size = get_post_meta($attachment_id, 'wio_optimized_size', true);
89
- $original_size = get_post_meta($attachment_id, 'wio_original_size', true);
90
- $image_statistics->deductFromField('optimized_size', $optimized_size);
91
- $image_statistics->deductFromField('original_size', $original_size);
92
- }
93
- $wio_attachment = new WIO_Attachment($attachment_id);
94
- $attachment_optimized_data = $wio_attachment->optimize($level);
95
- $original_size = $attachment_optimized_data['original_size'];
96
- $optimized_size = $attachment_optimized_data['optimized_size'];
97
- $errors_count = $attachment_optimized_data['errors_count'];
98
- $image_statistics->addToField('optimized_size', $optimized_size);
99
- $image_statistics->addToField('original_size', $original_size);
100
- $image_statistics->addToField('error', $errors_count);
101
- $image_statistics->addToField('optimized', 1);
102
- $image_statistics->save();
103
-
104
- echo $this->getMediaColumnContent($attachment_id);
105
- die();
106
- }
107
-
108
- /**
109
- * Восстановление аттачмента из резервной копии. AJAX
110
- *
111
- */
112
- public function restoreImageAttachment()
113
- {
114
- wp_suspend_cache_addition(true);
115
- $attachment_id = $_POST['id'];
116
-
117
- $wio_attachment = new WIO_Attachment($attachment_id);
118
- $image_statistics = new WIO_Image_Statistic();
119
- if( $wio_attachment->isOptimized() ) {
120
- $restored = $wio_attachment->restore();
121
- if( !is_wp_error($restored) ) {
122
- $optimized_size = get_post_meta($attachment_id, 'wio_optimized_size', true);
123
- $original_size = get_post_meta($attachment_id, 'wio_original_size', true);
124
- delete_post_meta($attachment_id, 'wio_optimized');
125
- delete_post_meta($attachment_id, 'wio_error');
126
- $image_statistics->deductFromField('optimized_size', $optimized_size);
127
- $image_statistics->deductFromField('original_size', $original_size);
128
- $image_statistics->save();
129
- }
130
- }
131
-
132
- echo $this->getMediaColumnContent($attachment_id);
133
- die();
134
- }
135
-
136
- /**
137
- * Обработка неоптимизированных изображений
138
- *
139
- * @param int $max_process_per_request кол-во аттачментов за 1 запуск
140
- * @return array
141
- */
142
- public function processUnoptimizedAttachments($max_process_per_request)
143
- {
144
- $backup = new WIO_Backup();
145
- $backup_origin_images = WIO_Plugin::app()->getOption('backup_origin_images', false);
146
- $optimization_level = WIO_Plugin::app()->getOption('image_optimization_level', 'normal');
147
- if( $backup_origin_images and !$backup->isBackupWritable() ) {
148
- return array(
149
- 'end' => true,
150
- 'msg' => __('No access for writing backups.', 'robin-image-optimizer'),
151
- );
152
- }
153
- if( !$backup->isUploadWritable() ) {
154
- return array(
155
- 'end' => true,
156
- 'msg' => __('The uploads folder is not writable.', 'robin-image-optimizer'),
157
- );
158
- }
159
- $args = array(
160
- 'post_type' => 'attachment',
161
- 'post_status' => 'inherit',
162
- 'post_mime_type' => array('image/jpeg', 'image/gif', 'image/png'),
163
- 'posts_per_page' => 1,
164
- 'meta_query' => array(
165
- 'relation' => 'AND',
166
- array(
167
- 'relation' => 'OR',
168
- array(
169
- 'key' => 'wio_optimized',
170
- 'compare' => 'NOT EXISTS',
171
- ),
172
- array(
173
- 'key' => 'wio_optimization_level',
174
- 'value' => $optimization_level,
175
- 'compare' => '!=',
176
- ),
177
- array(
178
- 'key' => 'wio_error',
179
- 'compare' => 'EXISTS',
180
- ),
181
- ),
182
- array(
183
- 'key' => 'wio_current_error',
184
- 'compare' => 'NOT EXISTS',
185
- ),
186
- )
187
- );
188
- $args = apply_filters('wio_unoptimized_args', $args);
189
- //выборка неоптимизированных изображений
190
- $args['posts_per_page'] = $max_process_per_request;
191
- $attachments = new WP_Query($args);
192
- $total = $attachments->found_posts;
193
-
194
- $attachments_count = 0;
195
- if( isset($attachments->posts) ) {
196
- $attachments_count = count($attachments->posts);
197
- }
198
-
199
- $original_size = 0;
200
- $optimized_size = 0;
201
- $errors_count = 0;
202
- $optimized_count = 0;
203
-
204
- // обработка
205
- if( $attachments_count ) {
206
- foreach($attachments->posts as $attachment) {
207
- $wio_attachment = new WIO_Attachment($attachment->ID);
208
- $attachment_optimized_data = $wio_attachment->optimize();
209
- $original_size = $original_size + $attachment_optimized_data['original_size'];
210
- $optimized_size = $optimized_size + $attachment_optimized_data['optimized_size'];
211
- $errors_count = $errors_count + $attachment_optimized_data['errors_count'];
212
- $optimized_count = $optimized_count + $attachment_optimized_data['optimized_count'];
213
- }
214
- }
215
-
216
- $image_statistics = new WIO_Image_Statistic();
217
- $image_statistics->addToField('optimized_size', $optimized_size);
218
- $image_statistics->addToField('original_size', $original_size);
219
- $image_statistics->addToField('error', $errors_count);
220
- $image_statistics->addToField('optimized', $optimized_count);
221
- $image_statistics->save();
222
-
223
- $remain = $total - $attachments_count;
224
- if( $remain <= 0 ) {
225
- $remain = 0;
226
- }
227
- $responce = array(
228
- 'remain' => $remain,
229
- 'end' => false,
230
- 'statistic' => $image_statistics->load(),
231
- );
232
-
233
- return $responce;
234
- }
235
-
236
- /**
237
- * Сбрасывает текущие ошибки оптимизации
238
- * Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
239
- *
240
- * @return void
241
- */
242
- public function resetCurrentErrors()
243
- {
244
- global $wpdb;
245
- $wpdb->delete($wpdb->postmeta, array(
246
- 'meta_key' => 'wio_current_error',
247
- ), array('%s'));
248
- }
249
-
250
- /**
251
- * Восстановление из резервной копии
252
- *
253
- * @param int $max_process_per_request кол-во аттачментов за 1 запуск
254
- * @return array
255
- */
256
- public function restoreAllFromBackup($max_process_per_request)
257
- {
258
- WIO_Plugin::app()->updateOption('cron_running', false); // останавливаем крон
259
- $args = array(
260
- 'post_type' => 'attachment',
261
- 'post_status' => 'inherit',
262
- 'post_mime_type' => array('image/jpeg', 'image/gif', 'image/png'),
263
- 'posts_per_page' => 1,
264
- 'meta_query' => array(
265
- array(
266
- 'key' => 'wio_backuped',
267
- 'value' => 1,
268
- ),
269
- )
270
- );
271
- $args['posts_per_page'] = $max_process_per_request;
272
- $attachments = new WP_Query($args);
273
- $total = $attachments->found_posts;
274
- $attachments_count = 0;
275
- if( isset($attachments->posts) ) {
276
- $attachments_count = count($attachments->posts);
277
- }
278
-
279
- $image_statistics = new WIO_Image_Statistic();
280
-
281
- $original_size = 0;
282
- $optimized_size = 0;
283
- // обработка
284
- if( $attachments_count ) {
285
- foreach($attachments->posts as $attachment) {
286
- $wio_attachment = new WIO_Attachment($attachment->ID);
287
- // восстанавливаем файл
288
- $restore_data = $wio_attachment->restore();
289
- if( is_wp_error($restore_data) ) {
290
- continue;
291
- }
292
- // снимаем отметку об оптимизации
293
- delete_post_meta($attachment->ID, 'wio_optimized');
294
- // вычитаем из статистики и обновляем статистику
295
- $attachment_optimized_size = floatval(get_post_meta($attachment->ID, 'wio_optimized_size', true));
296
- $attachment_original_size = floatval(get_post_meta($attachment->ID, 'wio_original_size', true));
297
-
298
- $original_size = $original_size + $attachment_original_size;
299
- $optimized_size = $optimized_size + $attachment_optimized_size;
300
- }
301
- }
302
- $image_statistics->deductFromField('optimized_size', $optimized_size);
303
- $image_statistics->deductFromField('original_size', $original_size);
304
- $image_statistics->save();
305
- $remain = $total - $attachments_count;
306
-
307
- return array(
308
- 'remain' => $remain,
309
- );
310
- }
311
-
312
- /**
313
- * Кол-во оптимизированных изображений
314
- *
315
- * @return int
316
- */
317
- public function getOptimizedCount()
318
- {
319
- $args = array(
320
- 'post_type' => 'attachment',
321
- 'post_status' => 'inherit',
322
- 'post_mime_type' => array('image/jpeg', 'image/gif', 'image/png'),
323
- 'posts_per_page' => 1,
324
- 'meta_query' => array(
325
- array(
326
- 'key' => 'wio_optimized',
327
- 'compare' => 'EXISTS',
328
- ),
329
- )
330
- );
331
- $attachments = new WP_Query($args);
332
- $total = $attachments->found_posts;
333
-
334
- return $total;
335
- }
336
-
337
- /**
338
- * Add "Image Optimizer" column in the Media Uploader
339
- *
340
- * @param array $form_fields An array of attachment form fields.
341
- * @param object $post The WP_Post attachment object.
342
- * @return array
343
- */
344
- public function attachmentEditorFields($form_fields, $post)
345
- {
346
- global $pagenow;
347
-
348
- if( 'post.php' === $pagenow ) {
349
- return $form_fields;
350
- }
351
-
352
- $form_fields['wio'] = array(
353
- 'label' => 'Image Optimizer',
354
- 'input' => 'html',
355
- 'html' => $this->getMediaColumnContent($post->ID),
356
- 'show_in_edit' => true,
357
- 'show_in_modal' => true,
358
- );
359
-
360
- return $form_fields;
361
- }
362
-
363
- /**
364
- * Add "wio" column in upload.php.
365
- *
366
- * @param array $columns An array of columns displayed in the Media list table.
367
- * @return array
368
- */
369
- public function addMediaColumn($columns)
370
- {
371
- $columns['wio_optimized_file'] = __('Image optimizer', 'image optimizer');
372
-
373
- return $columns;
374
- }
375
-
376
- /**
377
- * Add content to the "wio" columns in upload.php.
378
- *
379
- * @param string $column_name Name of the custom column.
380
- * @param int $attachment_id Attachment ID.
381
- */
382
- public function manageMediaColumn($column_name, $attachment_id)
383
- {
384
- if( 'wio_optimized_file' !== $column_name ) {
385
- return;
386
- }
387
- echo $this->getMediaColumnContent($attachment_id);
388
- }
389
-
390
- /**
391
- * Выводит блок статистики для аттачмента в медиабиблиотеке
392
- *
393
- * @param int $attachment_id Номер аттачмента из медиабиблиотеки
394
- */
395
- public function getMediaColumnContent($attachment_id)
396
- {
397
- ob_start();
398
- $is_optimized = get_post_meta($attachment_id, 'wio_optimized', true);
399
- $attach_meta = wp_get_attachment_metadata($attachment_id);
400
- $attach_dimensions = '0 x 0';
401
- if( isset($attach_meta['width']) and isset($attach_meta['height']) ) {
402
- $attach_dimensions = $attach_meta['width'] . ' × ' . $attach_meta['height'];
403
- }
404
- clearstatcache();
405
- $attachment_file_size = filesize(get_attached_file($attachment_id));
406
- if( $is_optimized ) {
407
- $optimized_size = get_post_meta($attachment_id, 'wio_optimized_size', true);
408
- $original_size = get_post_meta($attachment_id, 'wio_original_size', true);
409
- $original_main_size = get_post_meta($attachment_id, 'wio_original_main_size', true);
410
- $thumbnails_optimized = get_post_meta($attachment_id, 'wio_thumbnails_count', true);
411
- if( !$original_main_size ) {
412
- $original_main_size = $original_size;
413
- }
414
- $optimization_level = get_post_meta($attachment_id, 'wio_optimization_level', true);
415
- $error_msg = get_post_meta($attachment_id, 'wio_error', true);
416
- $backuped = get_post_meta($attachment_id, 'wio_backuped', true);
417
- $diff_percent = 0;
418
- $diff_percent_all = 0;
419
- if( $attachment_file_size and $original_main_size ) {
420
- $diff_percent = round(($original_main_size - $attachment_file_size) * 100 / $original_main_size);
421
- }
422
- if( $optimized_size and $original_size ) {
423
- $diff_percent_all = round(($original_size - $optimized_size) * 100 / $original_size);
424
- }
425
- ?>
426
-
427
-
428
- <ul class="wio-datas-list" data-size="<?php echo esc_attr(size_format($attachment_file_size)); ?>" data-dimensions="<?php echo esc_attr($attach_dimensions); ?>">
429
- <li class="wio-data-item">
430
- <span class="data"><?php _e('New Filesize:', 'robin-image-optimizer'); ?></span>
431
- <strong class="big"><?php echo esc_attr(size_format($attachment_file_size)); ?></strong></li>
432
- <li class="wio-data-item">
433
- <span class="data"><?php _e('Original Saving:', 'robin-image-optimizer'); ?></span>
434
- <strong>
435
- <span class="wio-chart-value"><?php echo esc_attr($diff_percent); ?></span>%
436
- </strong>
437
- </li>
438
- <li class="wio-data-item">
439
- <span class="data"><?php _e('Original Filesize:', 'robin-image-optimizer'); ?></span>
440
- <strong class="original"><?php echo esc_attr(size_format($original_main_size)); ?></strong>
441
- </li>
442
- <li class="wio-data-item"><span class="data"><?php _e('Level:', 'robin-image-optimizer'); ?></span>
443
- <strong>
444
- <?php
445
- if( !$error_msg ) {
446
- if( $optimization_level == 'normal' ) {
447
- echo __('lossless', 'robin-image-optimizer');
448
- } else if( $optimization_level == 'aggresive' ) {
449
- echo __('lossy', 'robin-image-optimizer');
450
- } else {
451
- echo __('High', 'robin-image-optimizer');
452
- }
453
- }
454
- ?>
455
- </strong>
456
- </li>
457
- <li class="wio-data-item">
458
- <span class="data"><?php _e('Thumbnails Optimized:', 'robin-image-optimizer'); ?></span>
459
- <strong class="original"><?php echo intval($thumbnails_optimized); ?></strong>
460
- </li>
461
- <li class="wio-data-item">
462
- <span class="data"><?php _e('Overall Saving:', 'robin-image-optimizer'); ?></span>
463
- <strong class="original"><?php echo esc_attr($diff_percent_all); ?>%</strong>
464
- </li>
465
- <?php if( $error_msg ) : ?>
466
- <li class="wio-data-item">
467
- <span class="data"><?php _e('Error Message:', 'robin-image-optimizer'); ?></span>
468
- <strong><?php echo esc_attr($error_msg); ?></strong></li>
469
- <?php endif; ?>
470
- </ul>
471
- <div class="wio-datas-actions-links" style="display:inline;">
472
- <?php if( $optimization_level != 'normal' ) : ?>
473
- <a data-id="<?php echo esc_attr($attachment_id); ?>" data-level="normal" href="#" class="wio-reoptimize button-wio-manual-override-upload" data-waiting-label="<?php _e('Optimization in progress', 'robin-image-optimizer'); ?>">
474
- <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'); ?>
475
- <span class="wio-hide-if-small"></span>
476
- </a>
477
- <?php endif; ?>
478
- <?php if( $optimization_level != 'aggresive' ) : ?>
479
- <a data-id="<?php echo esc_attr($attachment_id); ?>" data-level="aggresive" href="#" class="wio-reoptimize button-wio-manual-override-upload" data-waiting-label="<?php _e('Optimization in progress', 'robin-image-optimizer'); ?>">
480
- <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'); ?>
481
- <span class="wio-hide-if-small"></span>
482
- </a>
483
- <?php endif; ?>
484
- <?php if( $optimization_level != 'ultra' ) : ?>
485
- <a data-id="<?php echo esc_attr($attachment_id); ?>" data-level="ultra" href="#" class="wio-reoptimize button-wio-manual-override-upload" data-waiting-label="<?php _e('Optimization in progress', 'robin-image-optimizer'); ?>">
486
- <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'); ?>
487
- <span class="wio-hide-if-small"></span>
488
- </a>
489
- <?php endif; ?>
490
- <?php if( $backuped ) : ?>
491
- <a href="#" data-id="<?php echo esc_attr($attachment_id); ?>" class="button-wio-restore attachment-has-backup" data-waiting-label="<?php _e('Recovery in progress', 'robin-image-optimizer'); ?>"><span class="dashicons dashicons-image-rotate"></span><?php _e('Restore original', 'robin-image-optimizer'); ?>
492
- </a>
493
- <?php endif; ?>
494
- </div>
495
- <!-- .wio-datas-actions-links -->
496
-
497
- <?php
498
- } else {
499
- ?>
500
- <button type="button" data-id="<?php echo esc_attr($attachment_id); ?>" data-level="" class="wio-reoptimize button button-primary button-large" data-waiting-label="<?php _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 _e('Optimize', 'robin-image-optimizer'); ?></button>
501
- <?php
502
- }
503
-
504
- return ob_get_clean();
505
- }
506
-
507
- /**
508
- * Добавляем стили и скрипты в медиабиблиотеку
509
- */
510
- public function enqueueMeadiaScripts($hook)
511
- {
512
- if( $hook != 'upload.php' ) {
513
- return;
514
- }
515
- wp_enqueue_style('wio-install-addons', WIO_PLUGIN_URL . '/admin/assets/css/media.css', array(), WIO_Plugin::app()->getPluginVersion());
516
- wp_enqueue_script('wio-install-addons', WIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', array('jquery'), WIO_Plugin::app()->getPluginVersion());
517
- }
518
-
519
- public function deleteAttachmentHook($attachment_id)
520
- {
521
- $is_optimized = get_post_meta($attachment_id, 'wio_optimized', true);
522
- if( $is_optimized ) {
523
- $optimized_size = get_post_meta($attachment_id, 'wio_optimized_size', true);
524
- $original_size = get_post_meta($attachment_id, 'wio_original_size', true);
525
- $image_statistics = new WIO_Image_Statistic();
526
- $image_statistics->deductFromField('optimized_size', $optimized_size);
527
- $image_statistics->deductFromField('original_size', $original_size);
528
- $image_statistics->save();
529
- }
530
- }
531
- }
532
-
533
- add_filter(str_rot13('jope/evb/nyybj_freiref'), 'WIO_Backup::alternateStorage');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/class.optimization-tools.php DELETED
@@ -1,47 +0,0 @@
1
- <?php
2
- // Exit if accessed directly
3
- if( !defined('ABSPATH') ) {
4
- exit;
5
- }
6
-
7
- /**
8
- * Инструменты для оптмизации изображений
9
- *
10
- * @author Webcraftic <wordpress.webraftic@gmail.com>
11
- * @copyright (c) 22.09.2018, Webcraftic
12
- * @version 1.0
13
- */
14
- class WIO_OptimizationTools {
15
-
16
- /**
17
- * Метод возвращает объект, отвечающий за оптимизацию изображений через API сторонних сервисов
18
- *
19
- * @return WIO_Image_Processor_Resmush
20
- */
21
- public static function getImageProcessor()
22
- {
23
- $server = WIO_Plugin::app()->getOption('image_optimization_server', 'server_1');
24
-
25
- switch( $server ) {
26
- case 'server_1':
27
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-resmush.php'); // resmush api
28
- return new WIO_Image_Processor_Resmush();
29
- break;
30
- case 'server_2':
31
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-smushpro.php'); // smushpro api
32
- return new WIO_Image_Processor_Smushpro();
33
- break;
34
- /*case 'server_4':
35
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-clearfy1.php'); // clearfy1 api
36
- return new WIO_Image_Processor_Clearfy1();
37
- break;*/
38
- case 'server_3':
39
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-webcraftic.php'); // webcraftic api
40
- return new WIO_Image_Processor_Webcraftic();
41
- break;
42
- }
43
-
44
- require_once(WIO_PLUGIN_DIR . '/includes/classes/class.image-processor-resmush.php'); // resmush api
45
- return new WIO_Image_Processor_Resmush();
46
- }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/classes/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
includes/classes/logger/class-rio-log-export.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Prepares export files, ZIPs them and allows to download the package.
5
+ *
6
+ * Usage example:
7
+ *
8
+ * ```php
9
+ * $export_model = new WIO_Log_Export('package.zip');
10
+ * $prepared = $export_model->prepare();
11
+ *
12
+ * if($prepared) {
13
+ * // start streaming ZIP archive to be downloaded
14
+ * $export_model->download();
15
+ * }
16
+ * ```
17
+ *
18
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
19
+ */
20
+ class WIO_Log_Export {
21
+
22
+ /**
23
+ * @var string Default archive name on download. {datetime} will be replaced with current m-d-Y.
24
+ */
25
+ private $_archive_name = 'wio_export-{datetime}.zip';
26
+
27
+ /**
28
+ * @var string|null Archive save path.
29
+ */
30
+ private $_archive_save_path;
31
+
32
+ /**
33
+ * WIO_Log_Export constructor.
34
+ *
35
+ * @param null $archive_name
36
+ */
37
+ public function __construct ( $archive_name = null ) {
38
+ if ( $archive_name !== null ) {
39
+ $this->_archive_name = $archive_name;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Prepare export.
45
+ *
46
+ * @return bool
47
+ */
48
+ public function prepare () {
49
+
50
+ if ( ! class_exists( '\ZipArchive' ) ) {
51
+ WRIO_Logger::error( 'App does not have \ZipArchive class available. It is not possible to prepare export' );
52
+
53
+ return false;
54
+ }
55
+
56
+ $zip = new ZipArchive();
57
+
58
+ $log_base_dir = WRIO_Logger::get_base_dir();
59
+
60
+ if ( $log_base_dir === false ) {
61
+ WRIO_Logger::error( sprintf( 'Failed to get log path %s', $log_base_dir ) );
62
+
63
+ return false;
64
+ }
65
+
66
+ $uploads = wp_get_upload_dir();
67
+
68
+ if ( isset( $uploads['error'] ) && $uploads['error'] !== false ) {
69
+ WRIO_Logger::error( 'Unable to get save path of ZIP archive from wp_get_upload_dir()' );
70
+
71
+ return false;
72
+ }
73
+
74
+ $save_base_path = isset( $uploads['basedir'] ) ? $uploads['basedir'] : null;
75
+ $zip_archive_name = 'wio_export.zip';
76
+ $zip_save_path = $save_base_path . DIRECTORY_SEPARATOR . $zip_archive_name;
77
+
78
+
79
+ if ( ! $zip->open( $zip_save_path, ZipArchive::CREATE ) ) {
80
+ WRIO_Logger::error( sprintf( 'Failed to created ZIP archive in path %s. Skipping export...', $zip_save_path ) );
81
+
82
+ return false;
83
+ }
84
+
85
+ // Add all logs to ZIP archive
86
+ $glob_path = $log_base_dir . '*.log';
87
+ $log_files = glob( $glob_path );
88
+
89
+ if ( ! empty( $log_files ) ) {
90
+ foreach ( $log_files as $file ) {
91
+ if ( ! $zip->addFile( $file, wp_basename( $file ) ) ) {
92
+ WRIO_Logger::error( sprintf( 'Failed to add %s to %s archive. Skipping it.', $file, $zip_save_path ) );
93
+
94
+ return false;
95
+ }
96
+ }
97
+ }
98
+
99
+ $system_info = $this->prepare_system_info();
100
+
101
+ if ( ! empty( $system_info ) ) {
102
+ $system_info_file_name = 'wrio-system-info.txt';
103
+ $system_info_path = $save_base_path . DIRECTORY_SEPARATOR . $system_info_file_name;
104
+ if ( false !== @file_put_contents( $system_info_path, $system_info ) ) {
105
+ if ( ! $zip->addFile( $system_info_path, $system_info_file_name ) ) {
106
+ WRIO_Logger::error( sprintf( 'Failed to add %s to %s archive. Skipping it.', $system_info_file_name, $system_info_path ) );
107
+ }
108
+ } else {
109
+ WRIO_Logger::error( sprintf( 'Failed to save %s in %s', $system_info_file_name, $zip_save_path ) );
110
+ }
111
+ }
112
+
113
+ if ( ! $zip->close() ) {
114
+ WRIO_Logger::error( sprintf( 'Failed to close ZIP archive %s for unknown reason. ZipArchive::close() failed.' ) );
115
+ }
116
+
117
+ if ( isset( $system_info_path ) ) {
118
+ // Clean-up as this is just temp file
119
+ @unlink( $system_info_path );
120
+ }
121
+
122
+ $this->_archive_save_path = $zip_save_path;
123
+
124
+ return true;
125
+ }
126
+
127
+ /**
128
+ * Prepare generic system information, such as WordPress, PHP version, active plugins, loaded extenstions, etc.
129
+ *
130
+ * @return string
131
+ */
132
+ public function prepare_system_info () {
133
+
134
+ $space = PHP_EOL . PHP_EOL;
135
+ $nl = PHP_EOL;
136
+
137
+ $report = 'Plugin version: ' . WRIO_PLUGIN_VERSION . $nl;
138
+
139
+ global $wp_version;
140
+
141
+ $report .= 'WordPress Version: ' . $wp_version . $nl;
142
+ $report .= 'PHP Version: ' . PHP_VERSION . $nl;
143
+ $report .= 'Locale: ' . get_locale() . $nl;
144
+ $report .= 'HTTP Accept: ' . ( isset( $_SERVER['HTTP_ACCEPT'] ) ? $_SERVER['HTTP_ACCEPT'] : '*empty*' ) . $nl;
145
+ $report .= 'HTTP User Agent: ' . ( isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '*empty*' ) . $nl;
146
+ $report .= 'Server software: ' . ( isset( $_SERVER['SERVER_SOFTWARE'] ) ? $_SERVER['SERVER_SOFTWARE'] : '*empty*' ) . $nl;
147
+
148
+ $report .= $space;
149
+
150
+ $active_plugins = get_option( 'active_plugins', null );
151
+
152
+ if ( $active_plugins !== null ) {
153
+
154
+ $prepared_plugins = [];
155
+
156
+ $all_plugins = get_plugins();
157
+
158
+ foreach ( $active_plugins as $active_plugin ) {
159
+ if ( isset( $all_plugins[ $active_plugin ] ) ) {
160
+ $advanced_info = $all_plugins[ $active_plugin ];
161
+ $name = isset( $advanced_info['Name'] ) ? $advanced_info['Name'] : '';
162
+ $version = isset( $advanced_info['Version'] ) ? $advanced_info['Version'] : '';
163
+ $prepared_plugins[] = sprintf( "%s (%s)", $name, $version );
164
+ }
165
+ }
166
+
167
+ $report .= 'Active plugins:' . PHP_EOL;
168
+ $report .= implode( PHP_EOL, $prepared_plugins );
169
+ }
170
+
171
+ if ( function_exists( 'get_loaded_extensions' ) ) {
172
+
173
+ $report .= PHP_EOL . PHP_EOL;
174
+ $report .= 'Active extensions: ' . $nl;
175
+ $report .= implode( ', ', get_loaded_extensions() );
176
+ }
177
+
178
+ $report .= $space;
179
+
180
+ $report .= 'Generated at: ' . date( 'c' );
181
+
182
+ return $report;
183
+ }
184
+
185
+ /**
186
+ * Download saved ZIP archive.
187
+ *
188
+ * It sets download headers, which streams content of the ZIP archive.
189
+ *
190
+ * Additionally it cleans-up by deleting the archive if `$and_delete` set to true.
191
+ *
192
+ * @param bool $should_clean_up Allows to delete temp ZIP archive if required.
193
+ *
194
+ * @return bool
195
+ */
196
+ public function download ( $should_clean_up = true ) {
197
+
198
+ $zip_save_path = $this->_archive_save_path;
199
+
200
+ if ( empty( $zip_save_path ) ) {
201
+ return false;
202
+ }
203
+
204
+ $zip_content = @file_get_contents( $zip_save_path );
205
+
206
+ if ( $zip_save_path === false ) {
207
+ WRIO_Logger::error( sprintf( 'Failed to get ZIP %s content as file_get_contents() returned false', $zip_save_path ) );
208
+
209
+ return false;
210
+ }
211
+
212
+ if ( $should_clean_up ) {
213
+ // Delete as ZIP is just for temporary usage
214
+ @unlink( $zip_save_path );
215
+ }
216
+
217
+ $archive_name = str_replace( '{datetime}', date( 'c' ), $this->_archive_name );
218
+
219
+ // Set-up headers to download export file
220
+ header( 'Content-Description: File Transfer' );
221
+ header( 'Content-Type: application/zip' );
222
+ header( 'Content-Disposition: attachment; filename=' . $archive_name );
223
+ header( 'Content-Transfer-Encoding: binary' );
224
+ header( 'Connection: Keep-Alive' );
225
+ header( 'Expires: 0' );
226
+ header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
227
+ header( 'Pragma: public' );
228
+ header( 'Content-Length: ' . strlen( $zip_content ) );
229
+
230
+ echo $zip_content;
231
+ exit();
232
+ }
233
+
234
+ /**
235
+ * Get temporary stored archive path.
236
+ *
237
+ * @return string
238
+ */
239
+ public function get_temp_archive_path () {
240
+ return $this->_archive_save_path;
241
+ }
242
+
243
+ /**
244
+ * Delete temporary stored archive path.
245
+ *
246
+ * @return bool
247
+ */
248
+ public function delete_temp_archive () {
249
+ return @unlink( $this->get_temp_archive_path() );
250
+ }
251
+ }
includes/classes/logger/class-rio-log-reader.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Helps to convert log file content into easy-to-read HTML.
10
+ *
11
+ * Usage example:
12
+ *
13
+ * ```php
14
+ * $log_content = WIO_Log_Reader::prettify();
15
+ * ```
16
+ *
17
+ * @see WRIO_Logger for further information about logging.
18
+ * @see WRIO_Logger::get_content() for method which is used to get file content.
19
+ *
20
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
21
+ */
22
+ class WIO_Log_Reader {
23
+
24
+ /**
25
+ * Prettify log content.
26
+ *
27
+ * @see WRIO_Logger::get_content()
28
+ * @return bool|mixed|string
29
+ */
30
+ public static function prettify () {
31
+ $content = WRIO_Logger::get_content();
32
+
33
+
34
+ $search = [
35
+ "\r\n",
36
+ "\n\r",
37
+ "\025",
38
+ "\n",
39
+ "\r",
40
+ "\t",
41
+ ];
42
+
43
+ $replacement = [
44
+ '<br>',
45
+ '<br>',
46
+ '<br>',
47
+ '<br>',
48
+ '<br>',
49
+ str_repeat( '&nbsp;', 4 ),
50
+ ];
51
+
52
+ $content = str_replace( $search, $replacement, $content );
53
+
54
+
55
+ $color_map = [
56
+ WRIO_Logger::LEVEL_INFO => [ 'color' => '#fff', 'bg' => '#52d130' ],
57
+ WRIO_Logger::LEVEL_ERROR => [ 'color' => '#fff', 'bg' => '#ff5e5e' ],
58
+ WRIO_Logger::LEVEL_WARNING => [ 'color' => '#fff', 'bg' => '#ef910a' ],
59
+ WRIO_Logger::LEVEL_DEBUG => [ 'color' => '#fff', 'bg' => '#8f8d8b' ],
60
+ ];
61
+
62
+ /**
63
+ * Highlight log levels
64
+ */
65
+ foreach ( $color_map as $level => $item ) {
66
+ $content = preg_replace( "/\[([\d\w]{6})\]\[($level)\]/",
67
+ "[$1][<span style=\"color: {$item['color']};background-color: {$item['bg']}\">$2</span>]", $content );
68
+ }
69
+
70
+ return $content;
71
+ }
72
+ }
includes/classes/logger/class-rio-logger.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Adds ability to log application message into .log file.
10
+ *
11
+ * It has 4 core levels:
12
+ * - info: generic log message
13
+ * - warning: log possible exceptions states or unusual
14
+ * - error: log error-related logs
15
+ * - debug: log stack traces, big outputs, etc.
16
+ *
17
+ * Each level has its constant. See LEVEL_* prefix.
18
+ *
19
+ * Additionally it is possible to configure flush interval and file name.
20
+ *
21
+ * Usage examples:
22
+ *
23
+ * ```php
24
+ * // Info message level
25
+ * WRIO_Logger::info('Some generic message, good to know');
26
+ *
27
+ * // Warning message level
28
+ * WRIO_Logger::warning('Something does not work or unusual');
29
+ *
30
+ * // Error message level
31
+ * WRIO_Logger::error('Something critical happened');
32
+ *
33
+ * // Debug message level
34
+ * WRIO_Logger::debug('Some message used for debug purposed. Could be stack trace.');
35
+ * ```
36
+ *
37
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
38
+ * @copyright (c) 2018, Webcraftic
39
+ * @version 1.0
40
+ */
41
+ class WRIO_Logger {
42
+
43
+ const LEVEL_INFO = 'info';
44
+ const LEVEL_WARNING = 'warning';
45
+ const LEVEL_ERROR = 'error';
46
+ const LEVEL_DEBUG = 'debug';
47
+
48
+ /**
49
+ * @var null|string Request hash.
50
+ */
51
+ public static $hash = null;
52
+
53
+ /**
54
+ * @var null|string Directory where log file would be saved.
55
+ */
56
+ public static $dir = null;
57
+
58
+ /**
59
+ * @var string File log name where logs would be flushed.
60
+ */
61
+ public static $file = 'app.log';
62
+
63
+ /**
64
+ * @var int Flushing interval. When $_logs would reach this number of items they would be flushed to log file.
65
+ */
66
+ public static $flush_interval = 1000;
67
+
68
+ /**
69
+ * @var int Rotate size in bytes. Default: 5 Mb.
70
+ */
71
+ public static $rotate_size = 5000000;
72
+
73
+ /**
74
+ * @var int Number of rotated files. When size of $rotate_size matches current file, current file would be rotated.
75
+ * For example, there are 3 files, current file became size of $rotate_size, third file would be deleted, two first
76
+ * shifted and empty one created.
77
+ */
78
+ public static $rotate_limit = 3;
79
+
80
+ /**
81
+ * @var array List of logs to be dumped.
82
+ */
83
+ private static $_logs = [];
84
+
85
+ /**
86
+ * WRIOP_BackupLogger constructor.
87
+ */
88
+ public function __construct() {
89
+ $this->init();
90
+ }
91
+
92
+ /**
93
+ * Initiate object.
94
+ */
95
+ public function init() {
96
+ static::$hash = substr( uniqid(), 0, 6 );
97
+
98
+ add_action( 'shutdown', [ 'WRIO_Logger', 'flush' ], 9999, 0 );
99
+ }
100
+
101
+ /**
102
+ * Option enables error logging on frontend. If for some reason webp images are not displayed on the front-end, you can use
103
+ * this option to catch errors and send this report to the plugin support service.
104
+ *
105
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
106
+ * @since 1.3.6
107
+ * @return int
108
+ */
109
+ public static function is_keep_error_log_on_frontend() {
110
+ if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
111
+ return false;
112
+ }
113
+
114
+ return (int) WRIO_Plugin::app()->getPopulateOption( 'keep_error_log_on_frontend', 0 );
115
+ }
116
+
117
+ /**
118
+ * Get directory to save collected logs.
119
+ *
120
+ * In addition to that, it manages log rotation so that it does not become too big.
121
+ *
122
+ * @return string|false false on failure, string on success.
123
+ */
124
+ public static function get_dir() {
125
+
126
+ $base_dir = static::get_base_dir();
127
+
128
+ if ( $base_dir === null ) {
129
+ return false;
130
+ }
131
+
132
+ $root_file = $base_dir . static::$file;
133
+
134
+ // Check whether file exists and it exceeds rotate size, then should rotate it copy
135
+ if ( file_exists( $root_file ) && filesize( $root_file ) >= self::$rotate_size ) {
136
+ $name_split = explode( '.', self::$file );
137
+
138
+ if ( ! empty( $name_split ) && isset( $name_split[0] ) ) {
139
+ $name_split[0] = trim( $name_split[0] );
140
+
141
+ for ( $i = self::$rotate_limit; $i >= 0; $i -- ) {
142
+
143
+ $cur_name = $name_split[0] . $i;
144
+ $cur_path = $base_dir . $cur_name . '.log';
145
+
146
+ $next_path = $i !== 0 ? $base_dir . $name_split[0] . ( $i - 1 ) . '.log' : $root_file;
147
+
148
+ if ( file_exists( $next_path ) ) {
149
+ @copy( $next_path, $cur_path );
150
+ }
151
+ }
152
+ }
153
+
154
+ // Need to empty root file as it was supposed to be copied to next rotation :)
155
+ @file_put_contents( $root_file, '' );
156
+ }
157
+
158
+ return $root_file;
159
+ }
160
+
161
+ /**
162
+ * Get base directory, location of logs.
163
+ *
164
+ * @return null|string NULL in case of failure, string on success.
165
+ */
166
+ public static function get_base_dir() {
167
+ $wp_upload_dir = wp_upload_dir();
168
+
169
+ if ( isset( $wp_upload_dir['error'] ) && $wp_upload_dir['error'] !== false ) {
170
+ return null;
171
+ }
172
+
173
+ $base_path = wp_normalize_path( trailingslashit( $wp_upload_dir['basedir'] ) . 'wrio/' );
174
+
175
+ $folders = glob( $base_path . 'logs-*' );
176
+
177
+ if ( ! empty( $folders ) ) {
178
+ $exploded_path = explode( '/', trim( $folders[0] ) );
179
+ $selected_logs_folder = array_pop( $exploded_path );
180
+ } else {
181
+ if ( function_exists( 'wp_salt' ) ) {
182
+ $hash = md5( wp_salt() );
183
+ } else {
184
+ $hash = md5( AUTH_KEY );
185
+ }
186
+
187
+ $selected_logs_folder = 'logs-' . $hash;
188
+ }
189
+
190
+ $path = $base_path . $selected_logs_folder . '/';
191
+
192
+ if ( ! file_exists( $path ) ) {
193
+ @mkdir( $path, 0755, true );
194
+ }
195
+
196
+ // Create .htaccess file to protect log files
197
+ $htaccess_path = $path . '.htaccess';
198
+
199
+ if ( ! file_exists( $htaccess_path ) ) {
200
+ $htaccess_content = 'deny from all';
201
+ @file_put_contents( $htaccess_path, $htaccess_content );
202
+ }
203
+
204
+ // Create index.htm file in case .htaccess is not support as a fallback
205
+ $index_path = $path . 'index.html';
206
+
207
+ if ( ! file_exists( $index_path ) ) {
208
+ @file_put_contents( $index_path, '' );
209
+ }
210
+
211
+ return $path;
212
+ }
213
+
214
+ /**
215
+ * Get all available log paths.
216
+ *
217
+ * @return array|bool
218
+ */
219
+ public static function get_all() {
220
+ $base_dir = static::get_base_dir();
221
+
222
+ if ( $base_dir === null ) {
223
+ return false;
224
+ }
225
+
226
+ $glob_path = $base_dir . '*.log';
227
+
228
+ return glob( $glob_path );
229
+ }
230
+
231
+ /**
232
+ * Get total log size in bytes.
233
+ *
234
+ * @return int
235
+ * @see size_format() for formatting.
236
+ */
237
+ public static function get_total_size() {
238
+ $logs = static::get_all();
239
+ $bytes = 0;
240
+
241
+ if ( empty( $logs ) ) {
242
+ return $bytes;
243
+ }
244
+
245
+ foreach ( $logs as $log ) {
246
+ $bytes += @filesize( $log );
247
+ }
248
+
249
+ return $bytes;
250
+ }
251
+
252
+ /**
253
+ * Empty all log files and deleted rotated ones.
254
+ *
255
+ * @return bool
256
+ */
257
+ public static function clean_up() {
258
+
259
+ $base_dir = static::get_base_dir();
260
+
261
+ if ( $base_dir === null ) {
262
+ return false;
263
+ }
264
+
265
+ $glob_path = $base_dir . '*.log';
266
+
267
+ $files = glob( $glob_path );
268
+
269
+ if ( $files === false ) {
270
+ return false;
271
+ }
272
+
273
+ if ( empty( $files ) ) {
274
+ return true;
275
+ }
276
+
277
+ $unlinked_count = 0;
278
+
279
+ foreach ( $files as $file ) {
280
+ if ( @unlink( $file ) ) {
281
+ $unlinked_count ++;
282
+ }
283
+ }
284
+
285
+ return count( $files ) === $unlinked_count;
286
+ }
287
+
288
+ /**
289
+ * Flush all messages.
290
+ *
291
+ * @return bool
292
+ */
293
+ public static function flush() {
294
+
295
+ $messages = self::$_logs;
296
+
297
+ self::$_logs = [];
298
+
299
+ if ( empty( $messages ) ) {
300
+ return false;
301
+ }
302
+
303
+ $file_content = PHP_EOL . implode( PHP_EOL, $messages );
304
+ $is_put = @file_put_contents( self::get_dir(), $file_content, FILE_APPEND );
305
+
306
+ return $is_put !== false;
307
+ }
308
+
309
+ /**
310
+ *
311
+ * @param $level
312
+ * @param $message
313
+ *
314
+ * @return string
315
+ */
316
+ public static function get_format( $level, $message ) {
317
+
318
+ // Example: 2019-01-14 12:03:29.0593 [127.0.0.1][ee6a12][info] {message}
319
+ $template = '%s [%s][%s][%s] %s';
320
+
321
+ $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
322
+
323
+ return sprintf( $template, date( 'd-m-Y H:i:s' ) . '.' . microtime( true ), $ip, static::$hash, $level, $message );
324
+ }
325
+
326
+ /**
327
+ * Get latest file content.
328
+ *
329
+ * @return bool|string
330
+ */
331
+ public static function get_content() {
332
+ return @file_get_contents( static::get_dir() );
333
+ }
334
+
335
+ /**
336
+ * Add new log message.
337
+ *
338
+ * @param string $level Log level.
339
+ * @param string $message Message to log.
340
+ *
341
+ * @return bool
342
+ */
343
+ public static function add( $level, $message ) {
344
+
345
+ if ( $level === self::LEVEL_DEBUG ) {
346
+ $log_debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
347
+
348
+ if ( ! $log_debug ) {
349
+ return false;
350
+ }
351
+ }
352
+
353
+ static::$_logs[] = static::get_format( $level, $message );
354
+
355
+ if ( count( static::$_logs ) >= static::$flush_interval ) {
356
+ static::flush();
357
+ }
358
+
359
+ return true;
360
+ }
361
+
362
+ /**
363
+ * Add info level log.
364
+ *
365
+ * @param string $message Message to log.
366
+ */
367
+ public static function info( $message ) {
368
+ static::add( self::LEVEL_INFO, $message );
369
+ }
370
+
371
+ /**
372
+ * Add error level log.
373
+ *
374
+ * @param string $message Message to log.
375
+ */
376
+ public static function error( $message ) {
377
+ static::add( self::LEVEL_ERROR, $message );
378
+ }
379
+
380
+ /**
381
+ * Add debug level log.
382
+ *
383
+ * @param $message
384
+ */
385
+ public static function debug( $message ) {
386
+ static::add( self::LEVEL_DEBUG, $message );
387
+ }
388
+
389
+ /**
390
+ * Add warning level log.
391
+ *
392
+ * @param string $message Message to log.
393
+ */
394
+ public static function warning( $message ) {
395
+ static::add( self::LEVEL_WARNING, $message );
396
+ }
397
+
398
+ /**
399
+ * Writes information to log about memory.
400
+ *
401
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
402
+ * @since 1.3.6
403
+ */
404
+ public static function memory_usage() {
405
+ $memory_avail = ini_get( 'memory_limit' );
406
+ $memory_used = number_format( memory_get_usage( true ) / ( 1024 * 1024 ), 2 );
407
+ $memory_peak = number_format( memory_get_peak_usage( true ) / ( 1024 * 1024 ), 2 );
408
+
409
+ static::info( sprintf( "Memory: %s (avail) / %sM (used) / %sM (peak)", $memory_avail, $memory_used, $memory_peak ) );
410
+ }
411
+ }
includes/classes/models/class-rio-attachment-extra-data.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class RIO_Attachment_Extra_Data is a DTO model for `attachment` post type used for `extra_data`
10
+ * property in RIO_Process_Queue.
11
+ *
12
+ * @see RIO_Process_Queue::$extra_data for further information
13
+ *
14
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
15
+ */
16
+ class RIO_Attachment_Extra_Data extends RIO_Base_Extra_Data {
17
+
18
+ protected $error = null;
19
+ protected $error_msg = null;
20
+ protected $thumbnails_count = null;
21
+ protected $original_main_size = null;
22
+ protected $main_optimized_data = null;
23
+ protected $thumbnails_optimized_data = null;
24
+ protected $webp_main_size = null;
25
+
26
+ public function get_error() {
27
+ return $this->error;
28
+ }
29
+
30
+ public function set_error( $error ) {
31
+ $this->error = $error;
32
+ }
33
+
34
+ public function get_error_msg() {
35
+ return $this->error_msg;
36
+ }
37
+
38
+ public function set_error_msg( $error_msg ) {
39
+ $this->error_msg = $error_msg;
40
+ }
41
+
42
+ public function get_thumbnails_count() {
43
+ return $this->thumbnails_count;
44
+ }
45
+
46
+ public function set_thumbnails_count( $thumbnails_count ) {
47
+ $this->thumbnails_count = $thumbnails_count;
48
+ }
49
+
50
+ public function get_original_main_size() {
51
+ return $this->original_main_size;
52
+ }
53
+
54
+ public function set_original_main_size( $original_main_size ) {
55
+ $this->original_main_size = $original_main_size;
56
+ }
57
+
58
+ public function get_main_optimized_data() {
59
+ return (array)$this->main_optimized_data;
60
+ }
61
+
62
+ public function set_main_optimized_data( $main_optimized_data ) {
63
+ $this->main_optimized_data = $main_optimized_data;
64
+ }
65
+
66
+ public function get_thumbnails_optimized_data() {
67
+ return (array)$this->thumbnails_optimized_data;
68
+ }
69
+
70
+ public function set_thumbnails_optimized_data( $thumbnails_optimized_data ) {
71
+ $this->thumbnails_optimized_data = $thumbnails_optimized_data;
72
+ }
73
+
74
+ public function get_webp_main_size() {
75
+ return $this->webp_main_size;
76
+ }
77
+
78
+ public function set_webp_main_size( $webp_main_size ) {
79
+ $this->webp_main_size = $webp_main_size;
80
+ }
81
+ }
includes/classes/models/class-rio-base-active-record.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class WRIO_Base_Model used as a base class for any database related model.
10
+ *
11
+ * Usage example:
12
+ * ```php
13
+ * Custom extends RIO_Base_Model {
14
+ * public $prop;
15
+ * }
16
+ *
17
+ * $model = new Custom(array('prop' => 123)); // or ['prop' => 123]
18
+ * $model->save();
19
+ * ```
20
+ *
21
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
22
+ */
23
+ class RIO_Base_Active_Record extends RIO_Base_Object {
24
+
25
+ /**
26
+ * Get table name.
27
+ *
28
+ * @return string|null
29
+ */
30
+ public static function table_name () {
31
+ return null;
32
+ }
33
+
34
+ /**
35
+ * @todo override with activerecord impl
36
+ *
37
+ * @param string $name
38
+ * @param mixed $value
39
+ *
40
+ * @throws Exception
41
+ */
42
+ public function __set ( $name, $value ) {
43
+ if ( property_exists( $this, $name ) ) {
44
+ $this->$name = $value;
45
+ }
46
+ }
47
+
48
+
49
+ /**
50
+ * Check whether table has SQL schema or not.
51
+ *
52
+ * @return bool
53
+ */
54
+ public static function has_table_schema () {
55
+ $schema = static::get_table_schema();
56
+
57
+ return ! empty( $schema );
58
+ }
59
+
60
+ /**
61
+ * Check whether table has indexes defined.
62
+ *
63
+ * Notice: method would check whether model has schema defined first and then indexes.
64
+ *
65
+ * @return bool
66
+ */
67
+ public static function has_table_indexes () {
68
+
69
+ if ( ! static::has_table_schema() ) {
70
+ return false;
71
+ }
72
+
73
+ $indexes = static::get_table_indexes();
74
+
75
+ return ! empty( $indexes );
76
+ }
77
+
78
+ /**
79
+ * Get table SQL schema structure.
80
+ *
81
+ * @return string|null String when model has database table, null otherwise.
82
+ */
83
+ public static function get_table_schema () {
84
+ return null;
85
+ }
86
+
87
+ /**
88
+ * Get list of indexes.
89
+ *
90
+ * None associative list of
91
+ *
92
+ * @return array Empty array returned in case when no indexes exist on table.
93
+ */
94
+ public static function get_table_indexes () {
95
+ return array();
96
+ }
97
+ }
includes/classes/models/class-rio-base-extra-data.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class RIO_Base_Extra_Data is a base DTO model for `extra_data` property in RIO_Process_Queue.
5
+ *
6
+ * @see RIO_Process_Queue::$extra_data for further information
7
+ *
8
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
9
+ */
10
+ class RIO_Base_Extra_Data extends RIO_Base_Object {
11
+
12
+ /**
13
+ * @var string Instance of current class.
14
+ */
15
+ protected $class;
16
+
17
+ /**
18
+ * Magic override of to string method to convert
19
+ * @return bool|false|mixed|string
20
+ */
21
+ public function __toString() {
22
+ $props = get_object_vars( $this );
23
+ // если свойство не установлено, то не сохраняем его
24
+ foreach ( $props as $prop_name => $prop_value ) {
25
+ if ( is_null( $prop_value ) ) {
26
+ unset( $props[ $prop_name ] );
27
+ }
28
+ }
29
+ $props['class'] = get_called_class();
30
+
31
+ return wp_json_encode( $props );
32
+ }
33
+
34
+ /**
35
+ * Get class
36
+ *
37
+ * @return string
38
+ */
39
+ public function get_class() {
40
+ return $this->class;
41
+ }
42
+
43
+ /**
44
+ * Set class
45
+ *
46
+ * @param string $class_name Имя класса
47
+ *
48
+ * @return void
49
+ */
50
+ public function set_class( $class_name ) {
51
+ $this->class = $class_name;
52
+ }
53
+ }
includes/classes/models/class-rio-base-helper.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ class RIO_Base_Helper {
9
+ /**
10
+ * Configure passed object.
11
+ *
12
+ * @param object $object Object class to configure.
13
+ * @param array $config Key => value list of props to be set on object.
14
+ *
15
+ * @return mixed
16
+ */
17
+ public static function configure ( $object, $config ) {
18
+ foreach ( $config as $name => $value ) {
19
+ $object->$name = $value;
20
+ }
21
+
22
+ return $object;
23
+ }
24
+ }
includes/classes/models/class-rio-base-object.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class RIO_Base_Object is a base class that implements property feature.
10
+ *
11
+ * It takes advantage of __get() and __set(), see further implementation below in the class.
12
+ *
13
+ * When property of the class is being used and it has a getter, it will be used instead of directly accessing it.
14
+ *
15
+ * The same logic applies for setter.
16
+ *
17
+ * Example:
18
+ *
19
+ * ```php
20
+ * // equivalent to $label = $object->getLabel();
21
+ * $label = $object->label;
22
+ * // equivalent to $object->setLabel('abc');
23
+ * $object->label = 'abc';
24
+ * ```
25
+ *
26
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
27
+ */
28
+ class RIO_Base_Object {
29
+
30
+ /**
31
+ * RIO_Base_Object constructor.
32
+ *
33
+ * @param array $config name-value pairs that will be used to initialize the object properties.
34
+ */
35
+ public function __construct ( $config = [] ) {
36
+
37
+ if ( ! empty( $config ) ) {
38
+ $this->configure( $config );
39
+ }
40
+
41
+ $this->init();
42
+ }
43
+
44
+ /**
45
+ * Initiate model.
46
+ */
47
+ public function init () {
48
+
49
+ }
50
+
51
+ /**
52
+ * Configure object.
53
+ *
54
+ * @param array $config name-value pairs that will be used to initialize the object properties.
55
+ */
56
+ public function configure ( $config ) {
57
+ RIO_Base_Helper::configure( $this, $config );
58
+ }
59
+
60
+ /**
61
+ * Returns the value of an object property.
62
+ *
63
+ * Do not call this method directly as it is a PHP magic method that
64
+ * will be implicitly called when executing `$value = $object->property;`.
65
+ *
66
+ * @param string $name the property name
67
+ *
68
+ * @return mixed the property value
69
+ * @throws Exception if the property is not defined
70
+ * @see __set()
71
+ */
72
+ public function __get ( $name ) {
73
+ $getter = 'get_' . $name;
74
+ if ( method_exists( $this, $getter ) ) {
75
+ return $this->$getter();
76
+ } elseif ( method_exists( $this, 'set' . $name ) ) {
77
+ throw new \Exception( 'Getting write-only property: ' . get_class( $this ) . '::' . $name );
78
+ }
79
+ throw new Exception( 'Getting unknown property: ' . get_class( $this ) . '::' . $name );
80
+ }
81
+
82
+ /**
83
+ * Sets value of an object property.
84
+ *
85
+ * Do not call this method directly as it is a PHP magic method that
86
+ * will be implicitly called when executing `$object->property = $value;`.
87
+ *
88
+ * @param string $name the property name or the event name
89
+ * @param mixed $value the property value
90
+ *
91
+ * @throws Exception if the property is not defined
92
+ * @see __get()
93
+ */
94
+ public function __set ( $name, $value ) {
95
+ $setter = 'set_' . $name;
96
+ if ( method_exists( $this, $setter ) ) {
97
+ $this->$setter( $value );
98
+ } elseif ( method_exists( $this, 'get' . $name ) ) {
99
+ throw new \Exception( 'Setting read-only property: ' . get_class( $this ) . '::' . $name );
100
+ } else {
101
+ throw new \Exception( 'Setting unknown property: ' . get_class( $this ) . '::' . $name );
102
+ }
103
+ }
104
+ }
includes/classes/models/class-rio-process-queue-table.php ADDED
@@ -0,0 +1,968 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class RIO_Queue_Model is a database communication model for "prefix_rio_process_queue" table.
10
+ *
11
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
12
+ * @property int|null $id Primary key.
13
+ * @property null|string $server_id Server id which was used for processing.
14
+ * @property null|int $object_id Object id. Usually, this would be foreign key to another table.
15
+ * @property null|string $object_name Object name. Usually, this can be table name where to take object id.
16
+ * @property null|string $item_type Type of item. E.g. attachment, webp, etc.
17
+ * @property null|string $item_hash Unique item hash. Serves purpose of search.
18
+ * @property null|string $item_hash_alternative Unique alternative hash. Serves purpose of search.
19
+ * @property null|string $result_status Status result.
20
+ * @property null|string $processing_level
21
+ * @property null|bool $is_backed_up Whether item is backed-up or not. = false;
22
+ * @property null|int $original_size Original file size in bytes.
23
+ * @property null|int $final_size Final file size in bytes after it was optimized or converted.
24
+ * @property null|string $original_mime_type Original MIME TYPE. e.g. image/jpeg.
25
+ * @property null|string $final_mime_type Final mime type. e.g. image/webp.
26
+ * @property null|RIO_Base_Extra_Data $extra_data Extra data to be saved. e.g. JSON response from API.
27
+ * @property null|int $created_at UNIX timestamp when item was saved.
28
+ *
29
+ */
30
+ class RIO_Process_Queue extends RIO_Base_Active_Record {
31
+
32
+ /**
33
+ * When new status is added, it should be added accordingly to get_statuses() method.
34
+ *
35
+ * @see get_statuses() for further information.
36
+ */
37
+ const STATUS_SUCCESS = 'success'; // On success to convert or optimize.
38
+ const STATUS_ERROR = 'error'; // On failure to convert or optimize.
39
+ const STATUS_SKIP = 'skip'; // Skip.
40
+ const STATUS_PROCESSING = 'processing'; // When conversion or optimization is in progress.
41
+ const STATUS_UNOPTIMIZED = 'unoptimized'; // Когда картинка не оптимизирована
42
+
43
+ /**
44
+ * When new level is added, it should be added accordingly to get_levels() method.
45
+ *
46
+ * @see get_levels() for further information.
47
+ */
48
+ const LEVEL_NORMAL = 'normal';
49
+ const LEVEL_AGGRESIVE = 'aggresive'; // todo: need to fix typo in word aggressive
50
+ const LEVEL_ULTRA = 'ultra';
51
+ const LEVEL_CUSTOM = 'custom';
52
+
53
+ /**
54
+ * @var null|int Primary key.
55
+ */
56
+ protected $id = null;
57
+
58
+ /**
59
+ * @var null|string Server id which was used for processing.
60
+ */
61
+ protected $server_id = null;
62
+
63
+ /**
64
+ * @var null|int Object id. Usually, this would be foreign key to another table.
65
+ */
66
+ protected $object_id = null;
67
+
68
+ /**
69
+ * @var null|string Object name. Usually, this can be table name where to take object id.
70
+ */
71
+ protected $object_name = null;
72
+
73
+ /**
74
+ * @var null|string Type of item. E.g. attachment, webp, etc.
75
+ */
76
+ protected $item_type = null;
77
+
78
+ /**
79
+ * @var null|string Unique item hash. Serves purpose of search.
80
+ */
81
+ protected $item_hash = null;
82
+
83
+ /**
84
+ * @var null|string Unique alternative hash. Serves purpose of search.
85
+ */
86
+ protected $item_hash_alternative = null;
87
+
88
+ /**
89
+ * @var null|string Status result.
90
+ */
91
+ protected $result_status = null;
92
+
93
+ /**
94
+ * @var null|string
95
+ */
96
+ protected $processing_level = null;
97
+
98
+ /**
99
+ * @var null|bool Whether item is backed-up or not.
100
+ */
101
+ protected $is_backed_up = false;
102
+
103
+ /**
104
+ * @var null|int Original file size in bytes.
105
+ */
106
+ protected $original_size = null;
107
+
108
+ /**
109
+ * @var null|int Final file size in bytes after it was optimized or converted.
110
+ */
111
+ protected $final_size = null;
112
+
113
+ /**
114
+ * @var null|string Original MIME TYPE. e.g. image/jpeg.
115
+ */
116
+ protected $original_mime_type = null;
117
+
118
+ /**
119
+ * @var null|string Final mime type. e.g. image/webp.
120
+ */
121
+ protected $final_mime_type = null;
122
+
123
+ /**
124
+ * @var null|RIO_Base_Extra_Data Extra data to be saved. e.g. JSON response from API.
125
+ */
126
+ protected $extra_data = null;
127
+
128
+ /**
129
+ * @var null|int UNIX timestamp when item was saved.
130
+ */
131
+ protected $created_at = null;
132
+
133
+ /**
134
+ * @var RIO_Base_Extra_Data Keeps instance of extra data to leave state of $extra_data untouched.
135
+ */
136
+ private static $_extra_data;
137
+
138
+ /**
139
+ * {@inheritdoc}
140
+ */
141
+ public function init() {
142
+ parent::init();
143
+
144
+ // Default model initiation
145
+ static::$_extra_data = new RIO_Base_Extra_Data();
146
+ }
147
+
148
+ /**
149
+ * {@inheritdoc}
150
+ */
151
+ public static function table_name() {
152
+ global $wpdb;
153
+
154
+ return $wpdb->prefix . 'rio_process_queue';
155
+ }
156
+
157
+ /**
158
+ * Find db item by hash.
159
+ *
160
+ * @param string $hash Hash to search for.
161
+ *
162
+ * @return null|$this
163
+ */
164
+ public static function find_by_hash( $hash ) {
165
+ global $wpdb;
166
+
167
+ $table_name = static::table_name();
168
+ $sql = "SELECT * FROM {$table_name} WHERE `item_hash` = %s";
169
+
170
+ $row = $wpdb->get_row( $wpdb->prepare( $sql, $hash ), ARRAY_A );
171
+
172
+ if ( empty( $row ) ) {
173
+ return null;
174
+ }
175
+
176
+ return new RIO_Process_Queue( $row );
177
+ }
178
+
179
+ /**
180
+ * Find all by specified condition.
181
+ *
182
+ * @param array $condition Key => value condition to be used to search.
183
+ * @param string $order
184
+ * @param string $limit
185
+ *
186
+ * @return null|self[]
187
+ */
188
+ public static function find_all( array $condition ) {
189
+
190
+ global $wpdb;
191
+
192
+ $table = static::table_name();
193
+
194
+ $sql = "SELECT * FROM $table WHERE ";
195
+
196
+ foreach ( $condition as $key => $value ) {
197
+ if ( is_numeric( $value ) ) {
198
+ $sql .= " `" . esc_sql( $key ) . "` = $value AND";
199
+ } else if ( is_string( $value ) ) {
200
+ $sql .= " `" . esc_sql( $key ) . "` = '" . esc_sql( $value ) . "' AND";
201
+ }
202
+ }
203
+
204
+ $sql = rtrim( $sql, 'AND' );
205
+
206
+ $rows = $wpdb->get_results( $sql, ARRAY_A );
207
+
208
+ if ( empty( $rows ) ) {
209
+ return null;
210
+ }
211
+
212
+ foreach ( $rows as $key => $row ) {
213
+ $rows[ $key ] = new RIO_Process_Queue( $row );
214
+ }
215
+
216
+ return $rows;
217
+ }
218
+
219
+ /**
220
+ * Find items by list of hashes.
221
+ *
222
+ * @param string|array $hashes List of hashes in form of array or CSV.
223
+ * @param string|null $status
224
+ *
225
+ * @return null|RIO_Process_Queue[]
226
+ */
227
+ public static function find_by_hashes( $hashes, $status = null ) {
228
+ if ( ! is_array( $hashes ) && ! is_string( $hashes ) ) {
229
+ return null;
230
+ }
231
+
232
+ if ( ! is_array( $hashes ) && false !== strpos( $hashes, ',' ) ) {
233
+ $hashes = explode( ',', $hashes );
234
+ }
235
+
236
+ $hashes = array_map( 'trim', $hashes );
237
+
238
+ $table_name = static::table_name();
239
+ $sql = "SELECT * FROM {$table_name} WHERE `item_hash`";
240
+
241
+ $hashes = array_map( function ( $hash ) {
242
+ return "'$hash'";
243
+ }, $hashes );
244
+
245
+ $sql .= ' IN (' . implode( ', ', $hashes ) . ')';
246
+
247
+ global $wpdb;
248
+
249
+ if ( $status !== null ) {
250
+ $sql .= $wpdb->prepare( ' AND `result_status` = %s', $status );
251
+ }
252
+
253
+ $rows = $wpdb->get_results( $sql, ARRAY_A );
254
+
255
+ if ( empty( $rows ) ) {
256
+ return null;
257
+ }
258
+
259
+ foreach ( $rows as $key => $row ) {
260
+ $rows[ $key ] = new RIO_Process_Queue( $row );
261
+ }
262
+
263
+ return $rows;
264
+ }
265
+
266
+ /**
267
+ * Find db item by alternative hash.
268
+ *
269
+ * @param string $hash Hash to search for.
270
+ *
271
+ * @return null|$this
272
+ */
273
+ public static function find_by_alternative_hash( $hash ) {
274
+ global $wpdb;
275
+
276
+ $table_name = static::table_name();
277
+ $sql = "SELECT * FROM {$table_name} WHERE `item_alternative_hash` = %s";
278
+
279
+ $row = $wpdb->get_row( $wpdb->prepare( $sql, $hash ), ARRAY_A );
280
+
281
+ if ( empty( $row ) ) {
282
+ return null;
283
+ }
284
+
285
+ return new RIO_Process_Queue( $row );
286
+ }
287
+
288
+ /**
289
+ * Get next item to process sorted by ASC (first inserted).
290
+ *
291
+ * @param string $type Type to search for.
292
+ * @param int|null $object_id Object id added to the query.
293
+ * @param int|null $limit Limit added to the query.
294
+ *
295
+ * @return null|RIO_Process_Queue[]
296
+ */
297
+ public static function find_next_to_process( $type, $object_id = null, $limit = null ) {
298
+ global $wpdb;
299
+
300
+ $table_name = static::table_name();
301
+
302
+ $where = $wpdb->prepare( "WHERE `item_type`= %s AND `result_status` = 'processing'", $type );
303
+
304
+ if ( ! empty( $object_id ) ) {
305
+ $where .= sprintf( ' AND `object_id` = %d', $object_id );
306
+ }
307
+
308
+ $sql = "SELECT * FROM {$table_name} {$where} ORDER BY `id` ASC";
309
+
310
+ if ( is_int( $limit ) ) {
311
+ $sql .= ' LIMIT ' . $limit;
312
+ }
313
+
314
+ $rows = $wpdb->get_results( $sql, ARRAY_A );
315
+
316
+ if ( empty( $rows ) ) {
317
+ return null;
318
+ }
319
+
320
+ foreach ( $rows as $key => $row ) {
321
+ $rows[ $key ] = new RIO_Process_Queue( $row );
322
+ }
323
+
324
+ return $rows;
325
+ }
326
+
327
+ /**
328
+ * Get count var by specified status.
329
+ *
330
+ * Notice: when type is undefined, it would return false.
331
+ *
332
+ * @param string $status Status conversion or optimization.
333
+ *
334
+ * @return bool
335
+ */
336
+ public static function count_by_status( $status ) {
337
+ if ( empty( $status ) ) {
338
+ return false;
339
+ }
340
+
341
+ if ( ! in_array( $status, static::get_statuses() ) ) {
342
+ return false;
343
+ }
344
+
345
+ global $wpdb;
346
+
347
+ $table_name = static::table_name();
348
+ $sql = "SELECT COUNT(*) FROM {$table_name} WHERE `result_status` = %s";
349
+ $prepared_sql = $wpdb->prepare( $sql, [ $status ] );
350
+
351
+ return $wpdb->get_var( $prepared_sql );
352
+ }
353
+
354
+ /**
355
+ * Get count var by specified type and status.
356
+ *
357
+ * Notice: when type is undefined, it would return false.
358
+ *
359
+ * @param string $type Type of file. Normally defined by TYPE_* constant.
360
+ * @param string $status Status conversion or optimization.
361
+ *
362
+ * @return bool
363
+ */
364
+ public static function count_by_type_status( $type, $status ) {
365
+
366
+ if ( empty( $type ) || empty( $status ) ) {
367
+ return false;
368
+ }
369
+
370
+ if ( ! in_array( $status, static::get_statuses() ) ) {
371
+ return false;
372
+ }
373
+
374
+ global $wpdb;
375
+
376
+ $table_name = static::table_name();
377
+ $sql = "SELECT COUNT(*) FROM {$table_name} WHERE `item_type` = %s AND `result_status` = %s";
378
+ $prepared_sql = $wpdb->prepare( $sql, [ $type, $status ] );
379
+
380
+ return $wpdb->get_var( $prepared_sql );
381
+ }
382
+
383
+ /**
384
+ * Get count value for specified type, status and level.
385
+ *
386
+ * @param string $type Type of file. Normally defined by TYPE_* constant.
387
+ * @param string $status Status conversion or optimization.
388
+ * @param string $level Level of optimization or conversion.
389
+ *
390
+ * @return bool|null|string
391
+ */
392
+ public static function count_by_type_status_level( $type, $status, $level ) {
393
+ if ( empty( $type ) || empty( $status ) || empty( $level ) ) {
394
+ return false;
395
+ }
396
+
397
+ if ( ! in_array( $status, static::get_statuses() ) ) {
398
+ return false;
399
+ }
400
+
401
+ if ( ! in_array( $level, static::get_levels() ) ) {
402
+ return false;
403
+ }
404
+
405
+ global $wpdb;
406
+
407
+ $table_name = static::table_name();
408
+ $sql = "SELECT COUNT(*) FROM {$table_name} WHERE `item_type` = %s AND `result_status` = %s AND `processing_level` = %s";
409
+ $prepared_sql = $wpdb->prepare( $sql, [ $type, $status, $level ] );
410
+
411
+ return $wpdb->get_var( $prepared_sql );
412
+ }
413
+
414
+ /**
415
+ * Get available types.
416
+ *
417
+ * @return array
418
+ */
419
+ public static function get_statuses() {
420
+ return [
421
+ self::STATUS_SUCCESS,
422
+ self::STATUS_ERROR,
423
+ self::STATUS_PROCESSING,
424
+ self::STATUS_UNOPTIMIZED,
425
+ self::STATUS_SKIP,
426
+ ];
427
+ }
428
+
429
+ /**
430
+ * Get available levels.
431
+ *
432
+ * @return array
433
+ */
434
+ public static function get_levels() {
435
+ return [ self::LEVEL_NORMAL, self::LEVEL_AGGRESIVE, self::LEVEL_CUSTOM, self::LEVEL_ULTRA ];
436
+ }
437
+
438
+ /**
439
+ * Обновление свойств объекта из базы данных
440
+ *
441
+ * @return void
442
+ */
443
+ public function load() {
444
+ global $wpdb;
445
+ $table_name = static::table_name();
446
+ $sql = $wpdb->prepare( "SELECT * FROM {$table_name} WHERE object_id = %d AND item_type = %s LIMIT 1;", [
447
+ $this->object_id,
448
+ $this->item_type,
449
+ ] );
450
+
451
+ $row = $wpdb->get_row( $sql );
452
+
453
+ if ( ! empty( $row ) ) {
454
+ $this->configure( $row );
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Проверяет, оптимизированно ли изображение
460
+ *
461
+ * @return bool
462
+ */
463
+ public function is_optimized() {
464
+ if ( $this->result_status == self::STATUS_SUCCESS ) {
465
+ return true;
466
+ }
467
+
468
+ return false;
469
+ }
470
+
471
+ /**
472
+ * Проверяет, пропущено ли изображение
473
+ * Если изображение пропущено, то оно больше не участвует в оптимизации
474
+ *
475
+ * @return bool
476
+ */
477
+ public function is_skipped() {
478
+ if ( $this->result_status == self::STATUS_SKIP ) {
479
+ return true;
480
+ }
481
+
482
+ return false;
483
+ }
484
+
485
+ /**
486
+ * Подготовка данных для сохранения в базе
487
+ *
488
+ * @return array свойства модели в виде массива. Подготовлены для сохранения в базе данных
489
+ */
490
+ public function prepare_data_to_save() {
491
+ if ( ! $this->created_at ) {
492
+ $this->created_at = time();
493
+ }
494
+
495
+ if ( $this->extra_data instanceof RIO_Base_Extra_Data ) {
496
+ $this->extra_data = (string) $this->extra_data;
497
+ }
498
+
499
+ $data = (array) $this;
500
+
501
+ // @todo: remove later to usage of private attributes inside model
502
+ foreach ( $data as $key => $value ) {
503
+ $clean_key = trim( $key, " \t\n\r\0\x0B*" );
504
+ $data[ $clean_key ] = $value;
505
+ unset( $data[ $key ] );
506
+ }
507
+
508
+ unset( $data['id'] );
509
+
510
+ return $data;
511
+ }
512
+
513
+ /**
514
+ * Save item.
515
+ *
516
+ * @return bool
517
+ */
518
+ public function save() {
519
+ global $wpdb;
520
+ $table_name = static::table_name();
521
+ $data = $this->prepare_data_to_save();
522
+
523
+ $is_success = false;
524
+
525
+ // если установлен id - значит данные уже есть в базе
526
+ if ( $this->id ) {
527
+
528
+ // если данные есть в базе, то обновляем их
529
+ $result = $wpdb->update( $table_name, $data, [ 'id' => $this->id ] );
530
+
531
+ WRIO_Logger::debug( sprintf( 'Updated queue item #%s, attributes values: %s', $this->id, wp_json_encode( $data ) ) );
532
+
533
+ $is_success = true;
534
+ } else {
535
+
536
+ // если данных нет в базе, то вставляем новую запись в таблицу
537
+ $count = $wpdb->insert( $table_name, $data );
538
+
539
+ if ( $count !== false && $count > 0 ) {
540
+ $lastId = $wpdb->insert_id;
541
+
542
+ if ( ! empty( $lastId ) ) {
543
+ $this->id = $lastId;
544
+
545
+ $is_success = true;
546
+
547
+ WRIO_Logger::debug( sprintf( 'New queue created #%s, attributes values: %s', $this->id, wp_json_encode( $data ) ) );
548
+ }
549
+ }
550
+ }
551
+
552
+ /**
553
+ * Filter whether to execute hook or not.
554
+ *
555
+ * @param $execute_hook bool
556
+ */
557
+ $execute_hook = apply_filters( 'wbcr/riop/queue_item_save_execute_hook', true );
558
+
559
+ if ( $is_success && $execute_hook ) {
560
+ /**
561
+ * Fires after queue item was saved or updated successfully.
562
+ *
563
+ * @param RIO_Process_Queue $this
564
+ */
565
+ do_action( 'wbcr/riop/queue_item_saved', $this );
566
+ }
567
+
568
+ return $is_success;
569
+ }
570
+
571
+ /**
572
+ * Удаление информации об оптимизации из таблицы в базе данных
573
+ *
574
+ * @return bool
575
+ */
576
+ public function delete() {
577
+ global $wpdb;
578
+ $db_table = static::table_name();
579
+
580
+ $rows_affected = $wpdb->delete( $db_table, [ 'id' => $this->id ] );
581
+
582
+ WRIO_Logger::debug( sprintf( 'Deleted queue item #%s', $this->id ) );
583
+
584
+ return $rows_affected !== false;
585
+ }
586
+
587
+ /**
588
+ * Возвращает extra_data
589
+ *
590
+ * @return RIO_Base_Extra_Data|null
591
+ */
592
+ public function get_extra_data() {
593
+
594
+ if ( empty( $this->extra_data ) ) {
595
+ return null;
596
+ }
597
+
598
+ if ( is_string( $this->extra_data ) ) {
599
+ $extra_data = (array) json_decode( $this->extra_data );
600
+
601
+ if ( ! empty( $extra_data ) ) {
602
+ $class = isset( $extra_data['class'] ) ? $extra_data['class'] : null;
603
+
604
+ if ( ! empty( $class ) && class_exists( $class ) ) {
605
+ $this->extra_data = new $class( $extra_data );
606
+
607
+ return $this->extra_data;
608
+ }
609
+ }
610
+ }
611
+
612
+ if ( is_object( $this->extra_data ) ) {
613
+ return $this->extra_data;
614
+ }
615
+
616
+ return null;
617
+ }
618
+
619
+ /**
620
+ * Устанавливает значение для массива метаданных
621
+ *
622
+ * @param object|string $extra_data
623
+ *
624
+ * @return void
625
+ */
626
+ public function set_extra_data( $extra_data ) {
627
+ $this->extra_data = $extra_data;
628
+ }
629
+
630
+ /**
631
+ * Set item hash.
632
+ *
633
+ * @param string $text String to be hashed.
634
+ */
635
+ public function set_item_hash( $text ) {
636
+ if ( ! empty( $text ) ) {
637
+ $this->item_hash = static::generate_item_hash( $text );
638
+ }
639
+ }
640
+
641
+ /**
642
+ * @param int|null $id
643
+ */
644
+ public function set_id( $id ) {
645
+ if ( is_numeric( $id ) ) {
646
+ $this->id = (int) $id;
647
+ }
648
+ }
649
+
650
+ /**
651
+ * @return int|null
652
+ */
653
+ public function get_id() {
654
+
655
+ if ( is_numeric( $this->id ) ) {
656
+ return (int) $this->id;
657
+ }
658
+
659
+ return $this->id;
660
+ }
661
+
662
+ /**
663
+ * @param null|string $server_id
664
+ */
665
+ public function set_server_id( $server_id ) {
666
+ $this->server_id = $server_id;
667
+ }
668
+
669
+ public function get_server_id() {
670
+ return $this->server_id;
671
+ }
672
+
673
+ /**
674
+ * @param int|null $object_id
675
+ */
676
+ public function set_object_id( $object_id ) {
677
+ $this->object_id = $object_id;
678
+ }
679
+
680
+ public function get_object_id() {
681
+ return $this->object_id;
682
+ }
683
+
684
+ /**
685
+ * @param null|string $object_name
686
+ */
687
+ public function set_object_name( $object_name ) {
688
+ $this->object_name = $object_name;
689
+ }
690
+
691
+ public function get_object_name() {
692
+ return $this->object_name;
693
+ }
694
+
695
+ /**
696
+ * @param null|string $item_type
697
+ */
698
+ public function set_item_type( $item_type ) {
699
+ $this->item_type = $item_type;
700
+ }
701
+
702
+ public function get_item_type() {
703
+ return $this->item_type;
704
+ }
705
+
706
+ /**
707
+ * @param null|string $text Text to be hashed.
708
+ */
709
+ public function set_item_hash_alternative( $text ) {
710
+ if ( ! empty( $text ) ) {
711
+ $this->item_hash_alternative = static::generate_item_alternative_hash( $text );
712
+ }
713
+ }
714
+
715
+ public function get_item_hash_alternative() {
716
+ return $this->item_hash_alternative;
717
+ }
718
+
719
+ /**
720
+ * @return null|string
721
+ */
722
+ public function get_item_hash() {
723
+ return $this->item_hash;
724
+ }
725
+
726
+ /**
727
+ * @param null|string $result_status
728
+ */
729
+ public function set_result_status( $result_status ) {
730
+ $this->result_status = $result_status;
731
+ }
732
+
733
+ public function get_result_status() {
734
+ return $this->result_status;
735
+ }
736
+
737
+ /**
738
+ * @param null|string $processing_level
739
+ */
740
+ public function set_processing_level( $processing_level ) {
741
+ $this->processing_level = $processing_level;
742
+ }
743
+
744
+ public function get_processing_level() {
745
+ return $this->processing_level;
746
+ }
747
+
748
+ /**
749
+ * @param bool|null $is_backed_up
750
+ */
751
+ public function set_is_backed_up( $is_backed_up ) {
752
+ $this->is_backed_up = $is_backed_up;
753
+ }
754
+
755
+ public function get_is_backed_up() {
756
+ return $this->is_backed_up;
757
+ }
758
+
759
+ /**
760
+ * @param int|null $original_size
761
+ */
762
+ public function set_original_size( $original_size ) {
763
+ $this->original_size = $original_size;
764
+ }
765
+
766
+ public function get_original_size() {
767
+ return $this->original_size;
768
+ }
769
+
770
+ /**
771
+ * @param int|null $final_size
772
+ */
773
+ public function set_final_size( $final_size ) {
774
+ $this->final_size = $final_size;
775
+ }
776
+
777
+ public function get_final_size() {
778
+ return $this->final_size;
779
+ }
780
+
781
+ /**
782
+ * @param null|string $original_mime_type
783
+ */
784
+ public function set_original_mime_type( $original_mime_type ) {
785
+ $this->original_mime_type = $original_mime_type;
786
+ }
787
+
788
+ public function get_original_mime_type() {
789
+ return $this->original_mime_type;
790
+ }
791
+
792
+ /**
793
+ * @param null|string $final_mime_type
794
+ */
795
+ public function set_final_mime_type( $final_mime_type ) {
796
+ $this->final_mime_type = $final_mime_type;
797
+ }
798
+
799
+ public function get_final_mime_type() {
800
+ return $this->final_mime_type;
801
+ }
802
+
803
+ /**
804
+ * @param int|null $created_at
805
+ */
806
+ public function set_created_at( $created_at ) {
807
+ $this->created_at = $created_at;
808
+ }
809
+
810
+ /**
811
+ * get_created_at
812
+ *
813
+ * @return int
814
+ */
815
+ public function get_created_at() {
816
+ return $this->created_at;
817
+ }
818
+
819
+ /**
820
+ * Generate item hash.
821
+ *
822
+ * @param string $text Text to be hashed.
823
+ *
824
+ * @return string
825
+ */
826
+ public static function generate_item_hash( $text ) {
827
+ return hash( 'sha256', $text );
828
+ }
829
+
830
+ /**
831
+ * Generate item alternative hash.
832
+ *
833
+ * @param string $text Text to be hashed.
834
+ *
835
+ * @return string
836
+ */
837
+ public static function generate_item_alternative_hash( $text ) {
838
+ return hash( 'sha256', $text );
839
+ }
840
+
841
+ /**
842
+ * Get table schema.
843
+ *
844
+ * @return string
845
+ */
846
+ public static function get_table_schema() {
847
+ global $wpdb;
848
+ $charset_collate = $wpdb->get_charset_collate();
849
+ $table_name = RIO_Process_Queue::table_name();
850
+ $sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
851
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
852
+ `server_id` varchar(60) DEFAULT NULL,
853
+ `object_id` bigint(20) UNSIGNED NULL,
854
+ `object_name` varchar(255) NULL,
855
+ `item_type` varchar(60) NOT NULL,
856
+ `item_hash` CHAR(64) NULL COMMENT 'sha256 size',
857
+ `item_hash_alternative` CHAR(64) NULL COMMENT 'sha256 size',
858
+ `result_status` varchar(60) NOT NULL,
859
+ `processing_level` varchar(60) NOT NULL,
860
+ `is_backed_up` tinyint(1) NOT NULL DEFAULT '0',
861
+ `original_size` int(11) UNSIGNED NOT NULL,
862
+ `final_size` int(11) UNSIGNED NOT NULL,
863
+ `original_mime_type` varchar(60) NOT NULL,
864
+ `final_mime_type` varchar(60) NOT NULL,
865
+ `extra_data` TEXT NULL DEFAULT NULL,
866
+ `created_at` bigint(20) NOT NULL,
867
+ PRIMARY KEY (`id`)
868
+ ) $charset_collate;";
869
+
870
+ return $sql;
871
+ }
872
+
873
+ /**
874
+ * {@inheritdoc}
875
+ */
876
+ public static function get_table_indexes() {
877
+ $table_name = static::table_name();
878
+ $sql_index_type_status = "ALTER TABLE {$table_name} ADD INDEX `index-type-status` (`item_type`, `result_status`);";
879
+ $sql_index_type_status_level = "ALTER TABLE {$table_name} ADD INDEX `index-type-status-level` (`item_type`, `result_status`, `processing_level`);";
880
+ $sql_index_hash = "ALTER TABLE {$table_name} ADD UNIQUE `index-hash` (`item_hash`);";
881
+ $sql_index_hash_alternative = "ALTER TABLE {$table_name} ADD INDEX `index-hash-alternative` (`item_hash_alternative`);";
882
+
883
+ return [
884
+ $sql_index_type_status,
885
+ $sql_index_type_status_level,
886
+ $sql_index_hash,
887
+ $sql_index_hash_alternative,
888
+ ];
889
+ }
890
+
891
+ /**
892
+ * Try to create a plugin table in database
893
+ *
894
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
895
+ * @since 1.3.6
896
+ * @throws \Exception
897
+ */
898
+ public static function try_create_plugin_tables() {
899
+ global $wpdb;
900
+
901
+ try {
902
+ if ( ! RIO_Process_Queue::has_table_schema() ) {
903
+ return;
904
+ }
905
+
906
+ $db_version = (int) WRIO_Plugin::app()->getOption( 'db_version', 0 );
907
+
908
+ if ( ! $db_version ) {
909
+ $sql = static::get_table_schema();
910
+ $wpdb->query( $sql );
911
+
912
+ if ( static::has_table_indexes() ) {
913
+ $indexes = static::get_table_indexes();
914
+
915
+ foreach ( $indexes as $index ) {
916
+ $wpdb->query( $index );
917
+ }
918
+ }
919
+
920
+ WRIO_Plugin::app()->updateOption( 'db_version', 1 );
921
+
922
+ static::fix_table_collation();
923
+ }
924
+ } catch( \Exception $e ) {
925
+ WRIO_Logger::error( sprintf( "Failed create %s table in database.\r\nSQL: %s", static::table_name(), static::get_table_schema() ) );
926
+ }
927
+ }
928
+
929
+ /**
930
+ * RIO-126: Fix collation for plugin table, if Wordpress tables are created in a different encoding.
931
+ * In some cases, plugin table can be utf8mb4_unicode_520_ci, and Wodpress table can be
932
+ * utf8mb4_unicode_ci. This leads to problems when performing comparisons between two tables.
933
+ *
934
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
935
+ * @since 1.3.6
936
+ */
937
+ public static function fix_table_collation() {
938
+ global $wpdb;
939
+
940
+ $wp_post_meta_collation = null;
941
+ $process_queue_collation = null;
942
+ $wrio_table = static::table_name();
943
+
944
+ $result = $wpdb->get_results( "SHOW TABLE STATUS" );
945
+
946
+ if ( ! empty( $result ) ) {
947
+ foreach ( (array) $result as $table ) {
948
+ if ( $wpdb->postmeta === $table->Name ) {
949
+ $wp_post_meta_collation = $table->Collation;
950
+ } else if ( $wpdb->prefix . 'rio_process_queue' === $table->Name ) {
951
+ $process_queue_collation = $table->Collation;
952
+ }
953
+ }
954
+
955
+ if ( ! empty( $wp_post_meta_collation ) && ! empty( $process_queue_collation ) ) {
956
+
957
+ list( $wp_post_meta_charset ) = explode( '_', $wp_post_meta_collation );
958
+ list( $process_queue_charset ) = explode( '_', $process_queue_collation );
959
+
960
+ if ( ( $wp_post_meta_collation !== $process_queue_collation ) && $wp_post_meta_charset === $process_queue_charset ) {
961
+ $wpdb->query( "ALTER TABLE {$wrio_table} CONVERT TO CHARACTER SET {$wp_post_meta_charset} COLLATE {$wp_post_meta_collation}" );
962
+
963
+ WRIO_Logger::info( sprintf( "Successfully fix collation for plugin table.\r\nWP COLLATION: %s | PLUGIN COLLATION: %s", $wp_post_meta_collation, $process_queue_collation ) );
964
+ }
965
+ }
966
+ }
967
+ }
968
+ }
includes/classes/models/class-rio-server-smushit-extra-data.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class RIO_Smushit_Extra_Data is a DTO model for saving extra data from post attachments in `extra_data`.
10
+ *
11
+ * @see RIO_Process_Queue::$extra_data for further information
12
+ *
13
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
14
+ */
15
+ class RIO_Smushit_Extra_Data extends RIO_Attachment_Extra_Data {
16
+
17
+ /**
18
+ * @var int Final size in bytes.
19
+ */
20
+ protected $optimized_size;
21
+ }
includes/classes/processors/class-rio-server-abstract.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Базовый класс для обработки изображений через API сторонних сервисов.
10
+ *
11
+ * todo: add usage example
12
+ *
13
+ * @author Eugene Jokerov <jokerov@gmail.com>
14
+ * @copyright (c) 2018, Webcraftic
15
+ * @version 1.0
16
+ */
17
+ abstract class WIO_Image_Processor_Abstract {
18
+
19
+ /**
20
+ * Оптимизация изображения
21
+ *
22
+ * @param array $params {
23
+ * Параметры оптимизации изображения. Разные сервера могут принимать разные наборы параметров. Ниже список всех возможных.
24
+ *
25
+ * {type} string $image_url УРЛ изображения
26
+ * {type} string $image_path Путь к файлу изображения
27
+ * {type} string $quality Качество
28
+ * {type} string $save_exif Сохранять ли EXIF данные
29
+ * }
30
+ *
31
+ * @return array|WP_Error {
32
+ * Результаты оптимизации. Основные параметры. Другие параметры зависят от конкретной раелизации.
33
+ *
34
+ * {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
35
+ * {type} int $src_size размер исходного изображения в байтах
36
+ * {type} int $optimized_size размер оптимизированного изображения в байтах
37
+ * {type} int $optimized_percent На сколько процентов уменьшилось изображение
38
+ * {type} bool $not_need_replace Изображение не надо заменять.
39
+ * {type} bool $not_need_download Изображение не надо скачивать.
40
+ * }
41
+ */
42
+ abstract function process( $params );
43
+
44
+ /**
45
+ * Качество изображения
46
+ * Метод конвертирует качество из настроек плагина в формат сервиса оптимизации
47
+ *
48
+ * @param mixed $quality качество
49
+ */
50
+ abstract function quality( $quality );
51
+
52
+ /**
53
+ * HTTP запрос к API стороннего сервиса.
54
+ *
55
+ * @param string $type POST|GET
56
+ * @param string $url URL для запроса
57
+ * @param array|string|null $body Параметры запроса. По умолчанию: false.
58
+ * @param array $headers Дополнительные заголовки. По умолчанию: false.
59
+ *
60
+ * @return string|WP_Error
61
+ */
62
+ protected function request( $type, $url, $body = null, array $headers = [] ) {
63
+ $args = [
64
+ 'method' => $type,
65
+ 'headers' => $headers,
66
+ 'body' => $body,
67
+ 'timeout' => 150 // it make take some time for large images and slow Internet connections
68
+ ];
69
+
70
+ $error_message = sprintf( 'Failed to get content of URL: %s as wp_remote_request()', $url );
71
+
72
+ wp_raise_memory_limit( 'image' );
73
+ $response = wp_remote_request( $url, $args );
74
+
75
+ if ( is_wp_error( $response ) ) {
76
+ WRIO_Logger::error( sprintf( '%s returned error (%s).', $error_message, $response->get_error_message() ) );
77
+
78
+ return $response;
79
+ }
80
+
81
+ $response_body = wp_remote_retrieve_body( $response );
82
+ $response_code = wp_remote_retrieve_response_code( $response );
83
+
84
+ if ( $response_code !== 200 ) {
85
+ WRIO_Logger::error( sprintf( '%s responded Http error (%s).', $error_message, $response_code ) );
86
+
87
+ return new WP_Error( 'http_request_failed', sprintf( "Server responded an HTTP error %s", $response_code ) );
88
+ }
89
+
90
+ if ( empty( $response_body ) ) {
91
+ WRIO_Logger::error( sprintf( '%s responded an empty request body.', $error_message ) );
92
+
93
+ return new WP_Error( 'http_request_failed', "Server responded an empty request body." );
94
+ }
95
+
96
+ return $response_body;
97
+ }
98
+
99
+ /**
100
+ * HTTP запрос к API стороннего сервиса с использованием библиотеки CURL
101
+ *
102
+ * @param string $url URL для запроса
103
+ * @param array|false $post_fields Параметры запроса. По умолчанию: false.
104
+ * @param array|false $headers Дополнительные заголовки. По умолчанию: false.
105
+ *
106
+ * @return string
107
+ * todo: need to use wp_remote*, see https://webcraftic.atlassian.net/browse/RIO-71
108
+ * @throws Exception
109
+ */
110
+ /*protected function curlRequest( $url, $post_fields = false, $headers = false ) {
111
+ $ch = curl_init();
112
+ $timeout = 10;
113
+
114
+ curl_setopt( $ch, CURLOPT_URL, $url );
115
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
116
+ curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
117
+
118
+ if ( $post_fields ) {
119
+ curl_setopt( $ch, CURLOPT_POST, 1 );
120
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, $post_fields );
121
+ }
122
+
123
+ if ( $headers ) {
124
+ curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
125
+ }
126
+
127
+ $response = curl_exec( $ch );
128
+
129
+ if ( curl_errno( $ch ) ) {
130
+ throw new Exception( curl_error( $ch ), 'http_error' );
131
+ }
132
+
133
+ $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
134
+
135
+ if ( $http_code != 200 ) {
136
+ throw new Exception( 'HTTP error code: ' . $http_code, 'http_error' );
137
+ }
138
+
139
+ curl_close( $ch );
140
+
141
+ return $response;
142
+ }*/
143
+
144
+ /**
145
+ * Использует ли сервер отложенную оптимизацию
146
+ *
147
+ * @return bool
148
+ */
149
+ public function isDeferred() {
150
+ return false;
151
+ }
152
+
153
+ /**
154
+ * Проверка отложенной оптимизации изображения
155
+ *
156
+ * @param array $optimized_data Параметры отложенной оптимизации. Набор параметров зависит от конкретной реализации
157
+ *
158
+ * @return bool|array
159
+ */
160
+ public function checkDeferredOptimization( $optimized_data ) {
161
+ return false;
162
+ }
163
+
164
+ /**
165
+ * Проверка данных для отложенной оптимизации.
166
+ *
167
+ * Проверяет наличие необходимых параметров и соответствие серверу.
168
+ *
169
+ * @param array $optimized_data Параметры отложенной оптимизации. Набор параметров зависит от конкретной реализации
170
+ *
171
+ * @return bool
172
+ */
173
+ public function validateDeferredData( $optimized_data ) {
174
+ return false;
175
+ }
176
+ }
includes/classes/processors/class-rio-server-clearfy1.php ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для оптимизации изображений через API сервиса clearfy.pro.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_Image_Processor_Clearfy1 extends WIO_Image_Processor_Abstract {
16
+
17
+ /**
18
+ * @var string
19
+ */
20
+ protected $api_url;
21
+
22
+ /**
23
+ * @var string Имя сервера
24
+ */
25
+ protected $server_name = 'server_4';
26
+
27
+ public function __construct() {
28
+ $this->api_url = wrio_get_server_url( 'server_4' );
29
+ }
30
+
31
+ /**
32
+ * Оптимизация изображения
33
+ *
34
+ * @param array $params входные параметры оптимизации изображения
35
+ *
36
+ * @return array|WP_Error {
37
+ * Результаты оптимизации
38
+ *
39
+ * {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
40
+ * {type} int $src_size размер исходного изображения в байтах
41
+ * {type} int $optimized_size размер оптимизированного изображения в байтах
42
+ * {type} int $optimized_percent На сколько процентов уменьшилось изображение
43
+ * {type} string $session_id Идентификатор сессии. Для отложенной оптимизации.
44
+ * {type} string $file_id Идентификатор файла. Для отложенной оптимизации.
45
+ * {type} bool $not_need_replace Изображение не надо заменять.
46
+ * {type} bool $not_need_download Изображение не надо скачивать.
47
+ * {type} string $status Статус оптимизации
48
+ * {type} string $server Имя сервера оптимизации
49
+ * }
50
+ */
51
+ public function process( $settings ) {
52
+
53
+ $default_params = [
54
+ 'image_url' => '',
55
+ 'quality' => 100,
56
+ 'save_exif' => false,
57
+ ];
58
+ $settings = wp_parse_args( $settings, $default_params );
59
+
60
+ $session_id = $this->generateRandomString( 16 );
61
+ $file_id = 'o_' . $this->generateRandomString( 28 );
62
+ $upload_url = $this->get_endpoint_url( 'upload', $session_id );
63
+
64
+ if ( ! function_exists( 'curl_version' ) ) {
65
+ return new WP_Error( 'http_request_failed', "For Robin image optimizer to work, you need to install php extension [curl]." );
66
+ }
67
+
68
+ WRIO_Logger::info( sprintf( "Preparing to upload a file (%s) to a remote server (%s).", $settings['image_path'], $this->server_name ) );
69
+
70
+ // todo: need to use wp_remote*, see https://webcraftic.atlassian.net/browse/RIO-71
71
+ $filename = $settings['image_path'];
72
+
73
+ if ( ! class_exists( 'finfo' ) ) {
74
+ WRIO_Logger::error( 'For Robin image optimizer to work, you need to install php extension [php_fileinfo].' );
75
+
76
+ return new WP_Error( 'http_request_failed', "For Robin image optimizer to work, you need to install php extension [php_fileinfo]." );
77
+ }
78
+
79
+ $finfo = new \finfo( FILEINFO_MIME_TYPE );
80
+ $mimetype = $finfo->file( $filename );
81
+
82
+ $ch = curl_init( $upload_url );
83
+ $cfile = curl_file_create( $filename, $mimetype, basename( $filename ) );
84
+ $data = [ 'file' => $cfile, 'name' => basename( $filename ), 'id' => $file_id ];
85
+
86
+ curl_setopt( $ch, CURLOPT_POST, 1 );
87
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, $data );
88
+ //curl_setopt($ch, CURLOPT_HTTPHEADER, $request_headers);
89
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
90
+
91
+ $response = curl_exec( $ch );
92
+ $r = curl_getinfo( $ch );
93
+
94
+ if ( $r["http_code"] != 200 ) {
95
+ WRIO_Logger::error( sprintf( 'Failed to get content of URL: %s as wp_remote_request() responded Http error (%s).', $upload_url, $r["http_code"] ) );
96
+
97
+ return new WP_Error( 'http_request_failed', sprintf( "Server responded an Http error %s", $r["http_code"] ) );
98
+ }
99
+
100
+ $compress_url = $this->get_endpoint_url( 'compress', $session_id, $file_id, [ 'quality' => $settings['quality'] ] );
101
+ $compress_response = $this->request( 'GET', $compress_url );
102
+
103
+ if ( is_wp_error( $compress_response ) ) {
104
+ return $compress_response;
105
+ }
106
+
107
+ WRIO_Logger::info( sprintf( "File successfully uploaded to remote server (%s).", $this->server_name ) );
108
+
109
+ $optimized_image_data = [
110
+ 'optimized_img_url' => '',
111
+ 'src_size' => 0,
112
+ 'optimized_size' => 0,
113
+ 'optimized_percent' => 0,
114
+ 'session_id' => $session_id,
115
+ 'file_id' => $file_id,
116
+ 'not_need_replace' => true,
117
+ 'not_need_download' => true,
118
+ 'status' => 'processing', // отложенная оптимизация
119
+ 'server' => $this->server_name,
120
+ ];
121
+
122
+ return $optimized_image_data;
123
+ }
124
+
125
+
126
+ /**
127
+ * Проверка отложенной оптимизации изображения
128
+ *
129
+ * @param array $optimized_data {
130
+ * Параметры отложенной оптимизации
131
+ *
132
+ * {type} string $server Имя сервера оптимизации
133
+ * {type} string $session_id Идентификатор сессии
134
+ * {type} string $file_id Уникальный идентификатор файла. Генерируется сервером оптимизации.
135
+ * }
136
+ *
137
+ * @return bool|array
138
+ */
139
+ public function checkDeferredOptimization( $optimized_data ) {
140
+
141
+ $status_url = $this->get_endpoint_url( 'status', $optimized_data['session_id'], $optimized_data['file_id'] );
142
+ $response = $this->request( 'GET', $status_url );
143
+
144
+ if ( is_wp_error( $response ) ) {
145
+ return false;
146
+ }
147
+
148
+ $response = @json_decode( $response );
149
+
150
+ if ( isset( $response->compress_progress ) && $response->compress_progress == 100 ) {
151
+ $optimized_url = $this->api_url . $response->compressed_url;
152
+
153
+ return $optimized_url;
154
+ }
155
+
156
+ return false;
157
+ }
158
+
159
+ /**
160
+ * Проверка данных для отложенной оптимизации
161
+ * Проверяет наличие необходимых параметров и соответствие серверу
162
+ *
163
+ * @param array $optimized_data {
164
+ * Параметры отложенной оптимизации
165
+ *
166
+ * {type} string $server Имя сервера оптимизации
167
+ * {type} string $session_id Идентификатор сессии
168
+ * {type} string $file_id Уникальный идентификатор файла. Генерируется сервером оптимизации.
169
+ * }
170
+ *
171
+ * @return bool
172
+ */
173
+ public function validateDeferredData( $optimized_data ) {
174
+ if ( ! isset( $optimized_data['server'] ) ) {
175
+ return false;
176
+ }
177
+ if ( $optimized_data['server'] != $this->server_name ) {
178
+ return false;
179
+ }
180
+ if ( ! isset( $optimized_data['session_id'] ) or ! isset( $optimized_data['file_id'] ) ) {
181
+ return false;
182
+ }
183
+
184
+ return true;
185
+ }
186
+
187
+ /**
188
+ * Качество изображения
189
+ * Метод конвертирует качество из настроек плагина в формат сервиса resmush
190
+ *
191
+ * @param mixed $quality качество
192
+ *
193
+ * @return int
194
+ */
195
+ public function quality( $quality = 100 ) {
196
+ if ( is_numeric( $quality ) ) {
197
+ if ( $quality >= 1 && $quality <= 100 ) {
198
+ return $quality;
199
+ }
200
+ }
201
+ if ( $quality == 'normal' ) {
202
+ return 90;
203
+ }
204
+ if ( $quality == 'aggresive' ) {
205
+ return 75;
206
+ }
207
+ if ( $quality == 'ultra' ) {
208
+ return 50;
209
+ }
210
+
211
+ return 100;
212
+ }
213
+
214
+ /**
215
+ * Генерирует случайную строку указанной длины
216
+ *
217
+ * @param int $length Длина строки
218
+ *
219
+ * @return string
220
+ */
221
+ public function generateRandomString( $length = 10 ) {
222
+ $characters = '0123456789abcdefghiklmnopqrstuvwxyz';
223
+ $charactersLength = strlen( $characters );
224
+ $randomString = '';
225
+ for ( $i = 0; $i < $length; $i ++ ) {
226
+ $randomString .= $characters[ rand( 0, $charactersLength - 1 ) ];
227
+ }
228
+
229
+ return $randomString;
230
+ }
231
+
232
+ /**
233
+ * Использует ли сервер отложенную оптимизацию
234
+ *
235
+ * @return bool
236
+ */
237
+ public function isDeferred() {
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
243
+ * @since 1.1
244
+ *
245
+ * @param string $file_id
246
+ * @param array $agrs
247
+ *
248
+ * @param string $ednpoint
249
+ * @param string $session_id
250
+ *
251
+ * @return string|null
252
+ */
253
+ private function get_endpoint_url( $ednpoint, $session_id, $file_id = null, array $agrs = [] ) {
254
+ $url = $this->api_url . '/' . $ednpoint . '/' . $session_id;
255
+
256
+ if ( ! empty( $file_id ) ) {
257
+ $url .= '/' . $file_id;
258
+ }
259
+
260
+ $parse_args = wp_parse_args( $agrs, [
261
+ 'rnd' => '0.' . rand( 11111111, 99999999 )
262
+ ] );
263
+
264
+ return add_query_arg( $parse_args, $url );
265
+ }
266
+ }
includes/classes/processors/class-rio-server-resmush.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для оптимизации изображений через API сервиса Resmush.
10
+ *
11
+ * @see https://resmush.it/api
12
+ * @author Eugene Jokerov <jokerov@gmail.com>
13
+ * @copyright (c) 2018, Webcraftic
14
+ * @version 1.0
15
+ */
16
+ class WIO_Image_Processor_Resmush extends WIO_Image_Processor_Abstract {
17
+
18
+ /**
19
+ * @var string
20
+ */
21
+ protected $api_url = 'http://api.resmush.it/ws.php';
22
+
23
+ /**
24
+ * @var string Имя сервера
25
+ */
26
+ protected $server_name = 'server_1';
27
+
28
+ /**
29
+ * Оптимизация изображения
30
+ *
31
+ * @param array $params входные параметры оптимизации изображения
32
+ *
33
+ * @return array|WP_Error {
34
+ * Результаты оптимизации
35
+ *
36
+ * {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
37
+ * {type} int $src_size размер исходного изображения в байтах
38
+ * {type} int $optimized_size размер оптимизированного изображения в байтах
39
+ * {type} int $optimized_percent На сколько процентов уменьшилось изображение
40
+ * }
41
+ */
42
+ public function process( $settings ) {
43
+
44
+ $settings = wp_parse_args( $settings, array(
45
+ 'image_url' => '',
46
+ 'quality' => 100,
47
+ 'save_exif' => false,
48
+ ) );
49
+
50
+ $query_args = array(
51
+ 'qlty' => $settings['quality'],
52
+ );
53
+
54
+ if ( $settings['save_exif'] ) {
55
+ $query_args['exif'] = true;
56
+ }
57
+
58
+ $file = wp_normalize_path( $settings['image_path'] );
59
+
60
+ if ( ! file_exists( $file ) ) {
61
+ return new WP_Error( 'http_request_failed', sprintf( "File %s isn't exists.", $file ) );
62
+ }
63
+
64
+ WRIO_Logger::info( sprintf( "Preparing to upload a file (%s) to a remote server (%s).", $settings['image_path'], $this->server_name ) );
65
+
66
+ $boundary = wp_generate_password( 24 ); // Just a random string, use something better than wp_generate_password() though.
67
+ $headers = array(
68
+ 'content-type' => 'multipart/form-data; boundary=' . $boundary
69
+ );
70
+
71
+ $payload = '';
72
+
73
+ // First, add the standard POST fields:
74
+ foreach ( $query_args as $name => $value ) {
75
+ $payload .= '--' . $boundary;
76
+ $payload .= "\r\n";
77
+ $payload .= 'Content-Disposition: form-data; name="' . $name . '"' . "\r\n\r\n";
78
+ $payload .= $value;
79
+ $payload .= "\r\n";
80
+ }
81
+ // Upload the file
82
+ if ( $file ) {
83
+ $payload .= '--' . $boundary;
84
+ $payload .= "\r\n";
85
+ $payload .= 'Content-Disposition: form-data; name="files"; filename="' . basename( $file ) . '"' . "\r\n";
86
+ //$payload .= 'Content-Type: image/jpeg' . "\r\n"; // If you know the mime-type
87
+ $payload .= "\r\n";
88
+ $payload .= @file_get_contents( $file );
89
+ $payload .= "\r\n";
90
+ }
91
+
92
+ $payload .= '--' . $boundary . '--';
93
+
94
+ $response = $this->request( 'POST', $this->api_url, $payload, $headers );
95
+
96
+ if ( is_wp_error( $response ) ) {
97
+ return $response;
98
+ } else {
99
+ $response = @json_decode( $response );
100
+
101
+ if ( isset( $response->error ) ) {
102
+ return new WP_Error( 'http_request_failed', $response->error_long );
103
+ } else {
104
+ $optimized_image_data = array(
105
+ 'optimized_img_url' => $response->dest,
106
+ 'src_size' => $response->src_size,
107
+ 'optimized_size' => $response->dest_size,
108
+ 'optimized_percent' => $response->percent
109
+ );
110
+ }
111
+ }
112
+
113
+ WRIO_Logger::info( sprintf( "File successfully uploaded to remote server (%s).", $this->server_name ) );
114
+
115
+ return $optimized_image_data;
116
+ }
117
+
118
+ /**
119
+ * Качество изображения
120
+ * Метод конвертирует качество из настроек плагина в формат сервиса resmush
121
+ *
122
+ * @param mixed $quality качество
123
+ *
124
+ * @return int
125
+ */
126
+ public function quality( $quality = 100 ) {
127
+ if ( is_numeric( $quality ) ) {
128
+ if ( $quality >= 1 && $quality <= 100 ) {
129
+ return $quality;
130
+ }
131
+ }
132
+ if ( $quality == 'normal' ) {
133
+ return 90;
134
+ }
135
+ if ( $quality == 'aggresive' ) {
136
+ return 75;
137
+ }
138
+ if ( $quality == 'ultra' ) {
139
+ return 50;
140
+ }
141
+
142
+ return 100;
143
+ }
144
+ }
includes/classes/processors/class-rio-server-smushpro.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для оптимизации изображений через API сервиса smushpro.wpmudev.org.
10
+ *
11
+ * @see https://smushpro.wpmudev.org/
12
+ * @author Eugene Jokerov <jokerov@gmail.com>
13
+ * @copyright (c) 2018, Webcraftic
14
+ * @version 1.0
15
+ */
16
+ class WIO_Image_Processor_Smushpro extends WIO_Image_Processor_Abstract {
17
+
18
+ /**
19
+ * @var string
20
+ */
21
+ protected $api_url = 'smushpro.wpmudev.org/1.0/';
22
+
23
+ /**
24
+ * @var string Имя сервера
25
+ */
26
+ protected $server_name = 'server_2';
27
+
28
+ /**
29
+ * Оптимизация изображения
30
+ *
31
+ * @param array $settings входные параметры оптимизации изображения
32
+ *
33
+ * @return array|WP_Error {
34
+ * Результаты оптимизации
35
+ *
36
+ * {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
37
+ * {type} int $src_size размер исходного изображения в байтах
38
+ * {type} int $optimized_size размер оптимизированного изображения в байтах
39
+ * {type} int $optimized_percent На сколько процентов уменьшилось изображение
40
+ * {type} bool $not_need_download Изображение не надо скачивать
41
+ * }
42
+ */
43
+ public function process( $settings ) {
44
+
45
+ $default_params = [
46
+ 'image_url' => '',
47
+ 'save_exif' => false,
48
+ ];
49
+
50
+ $settings = wp_parse_args( $settings, $default_params );
51
+
52
+ $headers = [
53
+ 'accept' => 'application/json', // The API returns JSON.
54
+ 'content-type' => 'application/binary', // Set content type to binary.
55
+ ];
56
+
57
+ if ( $settings['save_exif'] ) {
58
+ $headers['exif'] = 'true';
59
+ }
60
+
61
+ $file = wp_normalize_path( $settings['image_path'] );
62
+
63
+ if ( ! file_exists( $file ) ) {
64
+ return new WP_Error( 'http_request_failed', sprintf( "File %s isn't exists.", $file ) );
65
+ }
66
+
67
+ WRIO_Logger::info( sprintf( "Preparing to upload a file (%s) to a remote server (%s).", $settings['image_path'], $this->server_name ) );
68
+
69
+ $use_http = WRIO_Plugin::app()->getPopulateOption( 'use_http' );
70
+
71
+ $api_url = ( $use_http ? 'http://' : 'https://' ) . $this->api_url;
72
+
73
+ $file_data = file_get_contents( $file );
74
+
75
+ $response = $this->request( 'POST', $api_url, $file_data, $headers );
76
+
77
+ unset( $file );
78
+
79
+ if ( is_wp_error( $response ) ) {
80
+ $er_msg = $response->get_error_message();
81
+
82
+ // Hostgator Issue.
83
+ if ( ! empty( $er_msg ) && strpos( $er_msg, 'SSL CA cert' ) !== false ) {
84
+ // Update DB for using http protocol.
85
+ WRIO_Plugin::app()->updatePopulateOption( 'use_http', 1 );
86
+ }
87
+
88
+ unset( $response ); // Free memory.
89
+
90
+ // Check for timeout error and suggest to filter timeout.
91
+ if ( strpos( $er_msg, 'timed out' ) ) {
92
+ return new WP_Error( 'api_error', __( "Skipped due to a timeout error. You can increase the request timeout to make sure Smush has enough time to process larger files.", 'robin-image-optimizer' ) );
93
+ }
94
+
95
+ // Handle error.
96
+ /* translators: %s error message */
97
+
98
+ return new WP_Error( 'api_error', sprintf( __( 'Error posting to API: %s', 'robin-image-optimizer' ), $er_msg ) );
99
+ }
100
+
101
+ $response = @json_decode( $response );
102
+
103
+ if ( $response && true === $response->success ) {
104
+
105
+ $image_data = isset( $response->data->image ) ? base64_decode( $response->data->image ) : false;
106
+
107
+ $optimized_image_data = [
108
+ 'optimized_img_url' => $image_data,
109
+ 'src_size' => $response->data->before_size,
110
+ 'optimized_size' => $response->data->after_size,
111
+ 'optimized_percent' => $response->data->compression,
112
+ 'not_need_download' => true,
113
+ ];
114
+
115
+ if ( ! $image_data ) {
116
+ $optimized_image_data['not_need_replace'] = true;
117
+ }
118
+
119
+ WRIO_Logger::info( sprintf( "File successfully uploaded to remote server (%s).", $this->server_name ) );
120
+
121
+ unset( $response ); // Free memory.
122
+
123
+ return $optimized_image_data;
124
+ }
125
+
126
+ unset( $response ); // Free memory.
127
+
128
+ return new WP_Error( 'api_error', __( "Image couldn't be smushed", 'robin-image-optimizer' ) );
129
+ }
130
+
131
+ /**
132
+ * Качество изображения
133
+ * Для этого провайдера оно не применяется
134
+ *
135
+ * @param mixed $quality качество
136
+ *
137
+ * @return int
138
+ */
139
+ public function quality( $quality = 100 ) {
140
+ return 100;
141
+ }
142
+ }
includes/classes/processors/class-rio-server-webcraftic.php ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для оптимизации изображений через API сервиса webcraftic.com.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WIO_Image_Processor_Webcraftic extends WIO_Image_Processor_Abstract {
16
+
17
+ /**
18
+ * @var string
19
+ */
20
+ protected $api_url;
21
+
22
+ /**
23
+ * @var string Имя сервера
24
+ */
25
+ protected $server_name = 'server_3';
26
+
27
+ /**
28
+ * Инициализация
29
+ *
30
+ * @return void
31
+ */
32
+ public function __construct() {
33
+ // Получаем ссылку на сервер 3
34
+ $this->api_url = wrio_get_server_url( 'server_3' );
35
+ }
36
+
37
+ /**
38
+ * Оптимизация изображения
39
+ *
40
+ * @param array $settings входные параметры оптимизации изображения
41
+ *
42
+ * @return array|WP_Error {
43
+ * Результаты оптимизации
44
+ *
45
+ * {type} string $optimized_img_url УРЛ оптимизированного изображения на сервере оптимизации
46
+ * {type} int $src_size размер исходного изображения в байтах
47
+ * {type} int $optimized_size размер оптимизированного изображения в байтах
48
+ * {type} int $optimized_percent На сколько процентов уменьшилось изображение
49
+ * {type} bool $not_need_download Изображение не надо скачивать
50
+ * }
51
+ */
52
+ public function process( $settings ) {
53
+
54
+ $default_params = array(
55
+ 'image_url' => '',
56
+ 'quality' => 100,
57
+ 'save_exif' => false,
58
+ );
59
+
60
+ $settings = wp_parse_args( $settings, $default_params );
61
+
62
+ $query_args = array(
63
+ 'quality' => $settings['quality'],
64
+ );
65
+
66
+ if ( ! $settings['save_exif'] ) {
67
+ $query_args['strip'] = 'info';
68
+ }
69
+
70
+ // Create a temporary image with a unique name.
71
+ $backup = WIO_Backup::get_instance();
72
+ $temp_attachment = $backup->createTempAttachment( $settings['image_path'] );
73
+
74
+ if ( is_wp_error( $temp_attachment ) ) {
75
+ return new WP_Error( 'create_temp_attachment_error', __( 'It is not possible to create a temporary file. Throw error ' . $temp_attachment->get_error_message(), 'robin-image-optimizer' ) );
76
+ }
77
+
78
+ WRIO_Logger::info( sprintf( "Preparing to upload a file (%s) to a remote server (%s).", $settings['image_path'], $this->server_name ) );
79
+
80
+ $img_url = $temp_attachment['image_url'];
81
+
82
+ $img_url = str_replace( array( 'http://', 'https://' ), '', $img_url );
83
+ $img_url = add_query_arg( $query_args, $this->api_url . '/' . $img_url );
84
+
85
+ $responce = $this->request( 'GET', $img_url );
86
+
87
+ // Delete temporary image
88
+ if ( file_exists( $temp_attachment['image_path'] ) && ! unlink( $temp_attachment['image_path'] ) ) {
89
+ WRIO_Logger::error( sprintf( "Failed to delete temporary file %s", $temp_attachment['image_path'] ) );
90
+ }
91
+
92
+ if ( is_wp_error( $responce ) ) {
93
+ return $responce;
94
+ }
95
+
96
+ WRIO_Logger::info( sprintf( "File successfully uploaded to remote server (%s).", $this->server_name ) );
97
+
98
+ return array(
99
+ 'optimized_img_url' => $responce,
100
+ 'src_size' => 0,
101
+ 'optimized_size' => 0,
102
+ 'optimized_percent' => 0,
103
+ 'not_need_download' => true,
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Качество изображения
109
+ * Метод конвертирует качество из настроек плагина в формат сервиса resmush
110
+ *
111
+ * @param mixed $quality качество
112
+ *
113
+ * @return int
114
+ */
115
+ public function quality( $quality = 100 ) {
116
+ if ( is_numeric( $quality ) ) {
117
+ if ( $quality >= 1 && $quality <= 100 ) {
118
+ return $quality;
119
+ }
120
+ }
121
+ if ( $quality == 'normal' ) {
122
+ return 90;
123
+ }
124
+ if ( $quality == 'aggresive' ) {
125
+ return 75;
126
+ }
127
+ if ( $quality == 'ultra' ) {
128
+ return 50;
129
+ }
130
+
131
+ return 100;
132
+ }
133
+ }
includes/classes/processors/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
includes/functions.php CHANGED
@@ -1,92 +1,411 @@
1
  <?php
2
 
3
- /**
4
- * Get size information for all currently-registered image sizes.
5
- *
6
- * @global $_wp_additional_image_sizes
7
- * @uses get_intermediate_image_sizes()
8
- * @return array $sizes Data for all currently-registered image sizes.
9
- */
10
- function wio_get_image_sizes()
11
- {
12
- global $_wp_additional_image_sizes;
13
-
14
- $sizes = array();
15
-
16
- foreach(get_intermediate_image_sizes() as $_size) {
17
- if( in_array($_size, array('thumbnail', 'medium', 'medium_large', 'large')) ) {
18
- $sizes[$_size]['width'] = get_option("{$_size}_size_w");
19
- $sizes[$_size]['height'] = get_option("{$_size}_size_h");
20
- $sizes[$_size]['crop'] = (bool)get_option("{$_size}_crop");
21
- } elseif( isset($_wp_additional_image_sizes[$_size]) ) {
22
- $sizes[$_size] = array(
23
- 'width' => $_wp_additional_image_sizes[$_size]['width'],
24
- 'height' => $_wp_additional_image_sizes[$_size]['height'],
25
- 'crop' => $_wp_additional_image_sizes[$_size]['crop'],
26
- );
27
- }
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- return $sizes;
31
- }
32
-
33
- /**
34
- * Пересчёт размера файла в байтах на человекопонятный вид
35
- *
36
- * Пример: вводим 67894 байт, получаем 67.8 KB
37
- * Пример: вводим 6789477 байт, получаем 6.7 MB
38
- * @param int $size размер файла в байтах
39
- * @return string
40
- */
41
- function wio_convert_bytes($size)
42
- {
43
- if( !$size ) {
44
- return 0;
 
 
 
 
 
 
 
 
45
  }
46
- $base = log($size) / log(1024);
47
- $suffix = array('', 'KB', 'MB', 'GB', 'TB');
48
- $f_base = floor($base);
49
-
50
- return round(pow(1024, $base - floor($base)), 2) . ' ' . $suffix[$f_base];
51
- }
52
-
53
- /**
54
- * Генерирует хеш строку
55
- *
56
- * @param int $length
57
- * @return string
58
- */
59
- function wbcr_rio_generate_random_string($length = 10)
60
- {
61
- $characters = '0123456789abcdefghiklmnopqrstuvwxyz';
62
- $charactersLength = strlen($characters);
63
- $randomString = '';
64
- for($i = 0; $i < $length; $i++) {
65
- $randomString .= $characters[rand(0, $charactersLength - 1)];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
 
67
 
68
- return $randomString;
69
  }
70
 
71
- /**
72
- * @param string $url
73
- * @param string $schema
74
- * @return string
75
- */
76
- function wbcr_rio_get_server_url($server_name)
77
- {
78
- $servers = array(
79
- 'server_4' => 'https://clearfy.pro/oimg.php',
80
- 'server_2' => 'https://smushpro.wpmudev.org/1.0/',
81
- 'server_1' => 'http://api.resmush.it/ws.php',
82
- 'server_3' => 'https://webcraftic.com/smush_images.php'
83
- );
 
 
 
 
 
 
 
84
 
85
- $servers = apply_filters('wbcr/rio/allow_servers', $servers);
 
86
 
87
- if( isset($servers[$server_name]) ) {
88
- return $servers[$server_name];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  return null;
92
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
+ /**
4
+ * Checks if the current request is a WP REST API request.
5
+ *
6
+ * Case #1: After WP_REST_Request initialisation
7
+ * Case #2: Support "plain" permalink settings
8
+ * Case #3: URL Path begins with wp-json/ (your REST prefix)
9
+ * Also supports WP installations in subfolders
10
+ *
11
+ * @author matzeeable https://wordpress.stackexchange.com/questions/221202/does-something-like-is-rest-exist
12
+ * @since 1.3.6
13
+ * @return boolean
14
+ */
15
+ function wrio_doing_rest_api() {
16
+ $prefix = rest_get_url_prefix();
17
+ $rest_route = WRIO_Plugin::app()->request->get( 'rest_route', null );
18
+ if ( defined( 'REST_REQUEST' ) && REST_REQUEST // (#1)
19
+ || ! is_null( $rest_route ) // (#2)
20
+ && strpos( trim( $rest_route, '\\/' ), $prefix, 0 ) === 0 ) {
21
+ return true;
22
+ }
23
+
24
+ // (#3)
25
+ $rest_url = wp_parse_url( site_url( $prefix ) );
26
+ $current_url = wp_parse_url( add_query_arg( [] ) );
27
+
28
+ return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
29
+ }
30
+
31
+ /**
32
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
33
+ * @since 1.3.6
34
+ * @return bool
35
+ */
36
+ function wrio_doing_ajax() {
37
+ if ( function_exists( 'wp_doing_ajax' ) ) {
38
+ return wp_doing_ajax();
39
+ }
40
+
41
+ return defined( 'DOING_AJAX' ) && DOING_AJAX;
42
+ }
43
+
44
+ /**
45
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
46
+ * @since 1.3.6
47
+ * @return bool
48
+ */
49
+ function wrio_doing_cron() {
50
+ if ( function_exists( 'wp_doing_cron' ) ) {
51
+ return wp_doing_cron();
52
+ }
53
 
54
+ return defined( 'DOING_CRON' ) && DOING_CRON;
55
+ }
56
+
57
+ /**
58
+ * Convert full URL paths to absolute paths.
59
+ *
60
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
61
+ * @since 1.1
62
+ *
63
+ * @param string $url
64
+ *
65
+ * @return string|null
66
+ */
67
+ function wrio_convert_url_to_abs_path( $url ) {
68
+ if ( empty( $url ) ) {
69
+ return null;
70
+ }
71
+
72
+ if ( strpos( $url, '?' ) !== false ) {
73
+ $url_parts = explode( '?', $url );
74
+
75
+ if ( 2 == sizeof( $url_parts ) ) {
76
+ $url = $url_parts[0];
77
  }
78
+ }
79
+
80
+ $url = rtrim( $url, '/' );
81
+
82
+ return str_replace( get_site_url(), untrailingslashit( wp_normalize_path( ABSPATH ) ), $url );
83
+ }
84
+
85
+ /**
86
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
87
+ * @since 1.1
88
+ *
89
+ * @param $string
90
+ * @param bool $capitalize_first_character
91
+ *
92
+ * @return mixed|string
93
+ */
94
+ function wrio_dashes_to_camel_case( $string, $capitalize_first_character = false ) {
95
+
96
+ $str = str_replace( '-', '_', ucwords( $string, '-' ) );
97
+
98
+ if ( ! $capitalize_first_character ) {
99
+ $str = lcfirst( $str );
100
+ }
101
+
102
+ return $str;
103
+ }
104
+
105
+ /**
106
+ * Alternative php functions basename. Our function works with сyrillic file names.
107
+ *
108
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
109
+ * @since 1.3.0
110
+ *
111
+ * @param string $str file path
112
+ *
113
+ * @return string|string[]|null
114
+ */
115
+ /*function wrio_basename( $str ) {
116
+ return preg_replace( '/^.+[\\\\\\/]/', '', $str );
117
+ }*/
118
+
119
+ /**
120
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
121
+ * @since 1.3.0
122
+ * @return bool
123
+ */
124
+ function wrio_is_active_nextgen_gallery() {
125
+ return is_plugin_active( 'nextgen-gallery/nggallery.php' );
126
+ }
127
+
128
+ /**
129
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
130
+ * @since 1.1
131
+ *
132
+ * @param string $dir
133
+ *
134
+ * @return bool
135
+ */
136
+ function wrio_rmdir( $dir ) {
137
+ if ( is_dir( $dir ) ) {
138
+ $scn = scandir( $dir );
139
+
140
+ foreach ( $scn as $files ) {
141
+ if ( $files !== '.' ) {
142
+ if ( $files !== '..' ) {
143
+ if ( ! is_dir( $dir . '/' . $files ) ) {
144
+ @unlink( $dir . '/' . $files );
145
+ } else {
146
+ wrio_rmdir( $dir . '/' . $files );
147
+ if ( is_dir( $dir . '/' . $files ) ) {
148
+ @rmdir( $dir . '/' . $files );
149
+ }
150
+ }
151
+ }
152
+ }
153
  }
154
+ @rmdir( $dir );
155
 
156
+ return true;
157
  }
158
 
159
+ return false;
160
+ }
161
+
162
+ /**
163
+ * Пересчёт размера файла в байтах на человекопонятный вид
164
+ *
165
+ * Пример: вводим 67894 байт, получаем 67.8 KB
166
+ * Пример: вводим 6789477 байт, получаем 6.7 MB
167
+ *
168
+ * @param int $size размер файла в байтах
169
+ *
170
+ * @return string
171
+ */
172
+ function wrio_convert_bytes( $size ) {
173
+ if ( ! $size ) {
174
+ return 0;
175
+ }
176
+ $base = log( $size ) / log( 1024 );
177
+ $suffix = [ '', 'KB', 'MB', 'GB', 'TB' ];
178
+ $f_base = intval( floor( $base ) );
179
 
180
+ return round( pow( 1024, $base - floor( $base ) ), 2 ) . ' ' . $suffix[ $f_base ];
181
+ }
182
 
183
+ /**
184
+ * Генерирует хеш строку
185
+ *
186
+ * @param int $length
187
+ *
188
+ * @return string
189
+ */
190
+ function wrio_generate_random_string( $length = 10 ) {
191
+ $characters = '0123456789abcdefghiklmnopqrstuvwxyz';
192
+ $charactersLength = strlen( $characters );
193
+ $randomString = '';
194
+ for ( $i = 0; $i < $length; $i ++ ) {
195
+ $randomString .= $characters[ rand( 0, $charactersLength - 1 ) ];
196
+ }
197
+
198
+ return $randomString;
199
+ }
200
+
201
+ /**
202
+ * Checks whether the license is activated for the plugin or not. If the Clearfy plugin is installed
203
+ * in priorities checks its license.
204
+ *
205
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
206
+ * @since 1.3.0
207
+ * @return bool
208
+ */
209
+ function wrio_is_license_activate() {
210
+ return wrio_is_clearfy_license_activate() || WRIO_Plugin::app()->premium->is_activate();
211
+ }
212
+
213
+ /**
214
+ * Checks whether the license is activated for Clearfy plugin.
215
+ *
216
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
217
+ * @since 1.3.0
218
+ * @return bool
219
+ */
220
+ function wrio_is_clearfy_license_activate() {
221
+ if ( class_exists( 'WCL_Plugin' ) ) {
222
+ $current_license = WCL_Licensing::instance()->getStorage()->getLicense();
223
+
224
+ if ( ! $current_license || ! isset( $current_license->id ) ) {
225
+ return false;
226
  }
227
 
228
+ return true;
229
+ }
230
+
231
+ return false;
232
+ }
233
+
234
+ /**
235
+ * Checks active (not expired!) License for plugin or not. If the Clearfy plugin is installed
236
+ * checks its license in priorities.
237
+ *
238
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
239
+ * @since 1.3.0
240
+ * @return bool
241
+ */
242
+ function wrio_is_license_active() {
243
+ if ( wrio_is_clearfy_license_activate() ) {
244
+ return WCL_Licensing::instance()->isLicenseValid();
245
+ }
246
+
247
+ return WRIO_Plugin::app()->premium->is_activate() && WRIO_Plugin::app()->premium->is_active();
248
+ }
249
+
250
+ /**
251
+ * Allows you to get a license key. If the Clearfy plugin is installed, it will be prioritized
252
+ * return it key.
253
+ *
254
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
255
+ * @since 1.3.0
256
+ * @return string|null
257
+ */
258
+ function wrio_get_license_key() {
259
+ if ( ! wrio_is_license_activate() ) {
260
  return null;
261
+ }
262
+
263
+ if ( wrio_is_clearfy_license_activate() ) {
264
+ return WCL_Licensing::instance()->getStorage()->getLicense()->secret_key;
265
+ }
266
+
267
+ return WRIO_Plugin::app()->premium->get_license()->get_key();
268
+ }
269
+
270
+ /**
271
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
272
+ * @since 1.3.0
273
+ * @return number|null
274
+ */
275
+ function wrio_get_freemius_plugin_id() {
276
+ if ( wrio_is_clearfy_license_activate() ) {
277
+ return WCL_Plugin::app()->getPluginInfoAttr( 'freemius_plugin_id' );
278
+ }
279
+
280
+ return WRIO_Plugin::app()->premium->get_setting( 'plugin_id' );
281
+ }
282
+
283
+ /**
284
+ * Get size information for all currently-registered image sizes.
285
+ *
286
+ * @return array $sizes Data for all currently-registered image sizes.
287
+ * @uses get_intermediate_image_sizes()
288
+ * @global $_wp_additional_image_sizes
289
+ */
290
+ function wrio_get_image_sizes() {
291
+ global $_wp_additional_image_sizes;
292
+
293
+ $sizes = [];
294
+
295
+ foreach ( get_intermediate_image_sizes() as $_size ) {
296
+ if ( in_array( $_size, [ 'thumbnail', 'medium', 'medium_large', 'large' ] ) ) {
297
+ $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" );
298
+ $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" );
299
+ $sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" );
300
+ } else if ( isset( $_wp_additional_image_sizes[ $_size ] ) ) {
301
+ $sizes[ $_size ] = [
302
+ 'width' => $_wp_additional_image_sizes[ $_size ]['width'],
303
+ 'height' => $_wp_additional_image_sizes[ $_size ]['height'],
304
+ 'crop' => $_wp_additional_image_sizes[ $_size ]['crop'],
305
+ ];
306
+ }
307
+ }
308
+
309
+ return $sizes;
310
+ }
311
+
312
+ /**
313
+ * Возвращает URL сервера оптимизации
314
+ *
315
+ * @since 1.2.0
316
+ *
317
+ * @param string $server_name имя сервера
318
+ *
319
+ * @return string
320
+ */
321
+ function wrio_get_server_url( $server_name ) {
322
+
323
+ $use_http = WRIO_Plugin::app()->getPopulateOption( 'use_http' );
324
+
325
+ $servers = [
326
+ 'server_4' => 'https://clearfy.pro/oimg.php',
327
+ 'server_2' => $api_url = ( $use_http ? 'http://' : 'https://' ) . 'smushpro.wpmudev.org/1.0/',
328
+ 'server_1' => 'http://api.resmush.it/ws.php',
329
+ 'server_3' => 'https://webcraftic.com/smush_images.php'
330
+ ];
331
+
332
+ $servers = apply_filters( 'wbcr/rio/allow_servers', $servers );
333
+
334
+ if ( isset( $servers[ $server_name ] ) ) {
335
+ return $servers[ $server_name ];
336
+ }
337
+
338
+ return null;
339
+ }
340
+
341
+ /**
342
+ * Check whether there are some migrations left to be processed.
343
+ *
344
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
345
+ * @since 1.3.0
346
+ * @return bool
347
+ * @throws Exception
348
+ */
349
+ function wbcr_rio_has_meta_to_migrate() {
350
+
351
+ $db_version = WRIO_Plugin::app()->getOption( 'db_version', false );
352
+
353
+ if ( $db_version !== false && (int) $db_version === 2 ) {
354
+ return false;
355
+ }
356
+
357
+ // Low number to limit resources consumption
358
+ $attachments = wbcr_rio_get_meta_to_migrate( 5 );
359
+
360
+ if ( isset( $attachments->posts ) && count( $attachments->posts ) > 0 ) {
361
+ return true;
362
+ }
363
+
364
+ if ( 1 === (int) $db_version ) {
365
+ WRIO_Plugin::app()->updateOption( 'db_version', 2 );
366
+ }
367
+
368
+ return false;
369
+ }
370
+
371
+ /**
372
+ * Get list of meta to migrate.
373
+ *
374
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
375
+ * @since 1.3.0
376
+ *
377
+ * @param int $limit Attachment limit per page.
378
+ *
379
+ * @return WP_Query
380
+ */
381
+ function wbcr_rio_get_meta_to_migrate( $limit = 0 ) {
382
+ $args = [
383
+ 'post_type' => 'attachment',
384
+ 'post_status' => 'inherit',
385
+ 'post_mime_type' => [ 'image/jpeg', 'image/gif', 'image/png' ],
386
+ 'posts_per_page' => - 1,
387
+ 'meta_query' => [
388
+ [
389
+ 'key' => 'wio_optimized',
390
+ 'compare' => 'EXISTS',
391
+ ],
392
+ ],
393
+ ];
394
+
395
+ if ( $limit ) {
396
+ $args['posts_per_page'] = $limit;
397
+ }
398
+
399
+ return new WP_Query( $args );
400
+ }
401
+
402
+ /**
403
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
404
+ * @since 1.3.0
405
+ * @return string
406
+ */
407
+ function wrio_get_meta_migration_notice_text() {
408
+ $nonce = wp_create_nonce( 'wrio-meta-migrations' );
409
+
410
+ return sprintf( __( 'There were big changes in database schema. Please <a href="#" id="wbcr-wio-meta-migration-action" class="button button-default" data-nonce="%s">click here</a> to upgrade it to the latest version', 'robin-image-optimizer' ), $nonce );
411
+ }
includes/index.php CHANGED
@@ -0,0 +1,3 @@
 
 
 
1
+ <?php
2
+ // silence is golden
3
+
index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
languages/index.php CHANGED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // silence is golden
languages/robin-image-optimizer-ru_RU.mo CHANGED
Binary file
languages/robin-image-optimizer-ru_RU.po CHANGED
@@ -1,36 +1,54 @@
 
 
 
1
  msgid ""
2
  msgstr ""
3
  "Project-Id-Version: Image optimizer\n"
4
  "Report-Msgid-Bugs-To: \n"
5
- "POT-Creation-Date: 2018-08-26 01:25+0300\n"
6
- "PO-Revision-Date: 2018-08-26 01:34+0300\n"
7
- "Last-Translator: Alexander Parfilov <alexparfilov@gmail.com>\n"
8
- "Language-Team: \n"
9
  "Language: ru_RU\n"
10
  "MIME-Version: 1.0\n"
11
  "Content-Type: text/plain; charset=UTF-8\n"
12
  "Content-Transfer-Encoding: 8bit\n"
13
- "X-Generator: Poedit 2.1.1\n"
14
  "X-Poedit-Basepath: ..\n"
15
- "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
16
- "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
17
  "X-Poedit-SourceCharset: UTF-8\n"
18
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c\n"
19
  "X-Poedit-SearchPath-0: .\n"
20
  "X-Poedit-SearchPathExcluded-0: libs\n"
21
  "X-Poedit-SearchPathExcluded-1: .git\n"
 
22
 
23
- #: admin/pages/log.php:41 admin/pages/log.php:84 admin/pages/log.php:95
24
- #: admin/pages/settings.php:257
 
 
 
 
 
 
 
 
25
  msgid "Error Log"
26
  msgstr "Журнал ошибок"
27
 
28
- #: admin/pages/log.php:83 admin/pages/statistic.php:84
29
- #: includes/classes/class.media-library.php:387
 
 
 
 
 
 
30
  msgid "Image optimizer"
31
  msgstr "Оптим. изображений"
32
 
33
- #: admin/pages/log.php:98
34
  msgid ""
35
  "In this section, you can track image optimization errors. Sending this log "
36
  "to us, will help in solving possible optimization issues."
@@ -38,41 +56,57 @@ msgstr ""
38
  "В этом разделе вы можете отследить ошибки оптимизации изображений. Отправка "
39
  "этого лога нам, поможет в решении возможных проблем с оптимизацией."
40
 
41
- #: admin/pages/settings.php:40 admin/pages/settings.php:167
42
- msgid "Main Settings"
43
- msgstr "Основные настройки"
 
 
 
 
44
 
45
- #: admin/pages/settings.php:143
46
  msgid "Folder wp-content/uploads/ is unavailable for writing"
47
  msgstr "Папка wp-content/uploads/ недоступна для записи"
48
 
49
- #: admin/pages/settings.php:147
50
  msgid "Folder wp-content/uploads/wio-backup/ is unavailable for writing"
51
  msgstr "Папка wp-content/uploads/wio-backup/ недоступна для записи"
52
 
53
- #: admin/pages/settings.php:150
54
  msgid "Cron is disabled in wp-config.php"
55
- msgstr "Crom отключен в wp-config.php"
56
 
57
- #: admin/pages/settings.php:167
 
 
 
 
58
  msgid "This section you can set main images optimization settings."
59
  msgstr ""
60
  "В этом разделе вы можете настроить основные параметры оптимизации "
61
  "изображений."
62
 
63
- #: admin/pages/settings.php:173
64
  msgid "Optimization server"
65
  msgstr "Сервер оптимизации"
66
 
67
- #: admin/pages/settings.php:177
68
  msgid "Server 1 (recommended) - image size limit up to 5 MB"
69
  msgstr "Сервер 1 (рекомендуется) - вес изображений не должен превышать 5МБ"
70
 
71
- #: admin/pages/settings.php:182
72
  msgid "Server 2 - image size limit up to 1 MB"
73
  msgstr "Сервер 2 - вес изображений не должен превышать 1МБ"
74
 
75
- #: admin/pages/settings.php:186
 
 
 
 
 
 
 
 
76
  msgid ""
77
  "We use several free servers for image optimization and can’t fully guarantee "
78
  "their stable performance. The server can be not available in some countries "
@@ -91,17 +125,15 @@ msgstr ""
91
  "оптимизированных изображений и на уровень оптимизации. По умолчанию всегда "
92
  "установлен самый лучший сервер с наименьшими ограничениями."
93
 
94
- #: admin/pages/settings.php:195
95
  msgid "Compression mode"
96
  msgstr "Режим сжатия"
97
 
98
- #: admin/pages/settings.php:199 admin/pages/statistic.php:353
99
- #: includes/classes/class.media-library.php:462
100
- #: includes/classes/class.media-library.php:489
101
- msgid "Normal"
102
- msgstr "Нормальный"
103
 
104
- #: admin/pages/settings.php:200
105
  msgid ""
106
  "This mode provides lossless compression and your images will be optimized "
107
  "without visible changes. If you want an ideal image quality, we recommend "
@@ -114,13 +146,11 @@ msgstr ""
114
  "приблизительно в 2 раза. Если вам этого недостаточно, попробуйте другие "
115
  "режимы."
116
 
117
- #: admin/pages/settings.php:204 admin/pages/statistic.php:354
118
- #: includes/classes/class.media-library.php:464
119
- #: includes/classes/class.media-library.php:495
120
- msgid "Medium"
121
- msgstr "Средний"
122
 
123
- #: admin/pages/settings.php:205
124
  msgid ""
125
  "This mode provides an ideal optimization of your images without significant "
126
  "quality loss. The file size will be reduced approximately 5 times with a "
@@ -132,13 +162,13 @@ msgstr ""
132
  "раз при незначительном снижении качества изображений. Чаще всего, "
133
  "невооружённым взглядом это даже не заметно."
134
 
135
- #: admin/pages/settings.php:209 admin/pages/statistic.php:355
136
- #: includes/classes/class.media-library.php:466
137
- #: includes/classes/class.media-library.php:501
138
  msgid "High"
139
  msgstr "Высокий"
140
 
141
- #: admin/pages/settings.php:210
142
  msgid ""
143
  "This mode will use all available optimization methods for maximum image "
144
  "compression. The file size will be reduced approximately 7 times. The "
@@ -152,15 +182,28 @@ msgstr ""
152
  "этот режим, если вам требуется максимальное снижение веса, и вы готовы "
153
  "смириться с потерей качества изображений."
154
 
155
- #: admin/pages/settings.php:214
 
 
 
 
 
156
  msgid "Select the compression mode appropriate for your case."
157
  msgstr "Выберите режим сжатия, подходящий для вашей ситуации."
158
 
159
- #: admin/pages/settings.php:235
 
 
 
 
 
 
 
 
160
  msgid "Auto optimization on upload"
161
  msgstr "Автоматическая оптимизация изображений при загрузке"
162
 
163
- #: admin/pages/settings.php:237
164
  msgid ""
165
  "Automatically compress all images that you upload directly to the WordPress "
166
  "media library, when editing pages and posts or using themes and plugins."
@@ -169,11 +212,11 @@ msgstr ""
169
  "медиабиблиотеку WordPress напрямую, при редактировании страниц и записей или "
170
  "с использованием тем и плагинов."
171
 
172
- #: admin/pages/settings.php:246
173
  msgid "Backup images"
174
  msgstr "Резервное копирование изображений"
175
 
176
- #: admin/pages/settings.php:248
177
  msgid ""
178
  "Before optimizing, all your images will be saved in a separate folder for "
179
  "future recovery."
@@ -181,16 +224,16 @@ msgstr ""
181
  "Перед началом оптимизации, все ваши изображения будут сохранены в отдельной "
182
  "папке для возможности восстановления в будущем."
183
 
184
- #: admin/pages/settings.php:259
185
  msgid "Enable error logging. The log will be displayed on a separate tab."
186
  msgstr ""
187
  "Включить ведение журнала ошибок. Он будет отображаться на отдельной вкладке."
188
 
189
- #: admin/pages/settings.php:274
190
  msgid "Leave EXIF data"
191
  msgstr "Оставлять данные EXIF"
192
 
193
- #: admin/pages/settings.php:276
194
  msgid ""
195
  "EXIF is information stored in photos: camera model, shutter speed, exposure "
196
  "compensation, ISO, GPS, etc. By default, the plugin removes EXIF extended "
@@ -202,20 +245,20 @@ msgstr ""
202
  "удаляет расширенные данные EXIF. Если ваш проект посвящён фотографии и вам "
203
  "нужно отображать эти данные, то включите эту опцию."
204
 
205
- #: admin/pages/settings.php:282
206
  msgid "Optimization"
207
  msgstr "Оптимизация"
208
 
209
- #: admin/pages/settings.php:282
210
  msgid "Here you can specify additional image optimization options."
211
  msgstr ""
212
  "Здесь вы можете задать дополнительные параметры оптимизации изображений."
213
 
214
- #: admin/pages/settings.php:290
215
  msgid "Resizing large images"
216
  msgstr "Изменение размера больших изображений"
217
 
218
- #: admin/pages/settings.php:292
219
  msgid ""
220
  "When you upload images from a camera or stock, they may be too high "
221
  "resolution and it is not necessary for web. The option allows you to "
@@ -225,11 +268,11 @@ msgstr ""
225
  "слишком высокого разрешения и для веба это не нужно. Опция позволяет "
226
  "автоматически изменять разрешение изображений при загрузке."
227
 
228
- #: admin/pages/settings.php:308
229
- msgid "Enter the maximum size (px)"
230
- msgstr "Введите максимальный размер (px)"
231
 
232
- #: admin/pages/settings.php:310
233
  msgid ""
234
  "Set the maximum images resolution on the long side. For horizontal images, "
235
  "this will be the width, and for vertical images - the height. The resolution "
@@ -240,11 +283,15 @@ msgstr ""
240
  "Разрешение изображений будет изменено пропорционально в соответствии с "
241
  "заданным значением."
242
 
243
- #: admin/pages/settings.php:328
 
 
 
 
244
  msgid "Optimize thumbnails"
245
  msgstr "Оптимизировать миниатюры"
246
 
247
- #: admin/pages/settings.php:331
248
  msgid ""
249
  "Choose which sizes of thumbnails should be optimized and uncheck those that "
250
  "do not need optimization."
@@ -252,359 +299,502 @@ msgstr ""
252
  "Выберите какие размеры миниатюр следует оптимизировать и снимите галочки с "
253
  "тех, оптимизация которых не нужна."
254
 
255
- #: admin/pages/settings.php:338
256
- msgid "Scheduled optimization"
257
- msgstr "Оптимизация по расписанию"
258
 
259
- #: admin/pages/settings.php:338
260
- msgid "Schedule your images optimization."
261
- msgstr "Настройка оптимизации изображений по расписанию."
 
 
262
 
263
- #: admin/pages/settings.php:346
264
- msgid "Enable Scheduled Optimization"
265
- msgstr "Включить оптимизацию по расписанию"
266
 
267
- #: admin/pages/settings.php:348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  msgid ""
269
- "You can enable image optimization on schedule. The plugin will optimize "
270
- "specified number of images automatically after selected time."
 
271
  msgstr ""
272
- "Вы можете включить оптимизацию изображений по расписанию. Через выбранное "
273
- "время плагин будет оптимизировать заданное количество изображений "
274
- "автоматически."
275
 
276
- #: admin/pages/settings.php:363 includes/classes/class.cron.php:56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  msgid "1 min"
278
- msgstr "1 мин"
279
 
280
- #: admin/pages/settings.php:364 includes/classes/class.cron.php:60
281
  msgid "2 min"
282
- msgstr "2 мин"
283
 
284
- #: admin/pages/settings.php:365 includes/classes/class.cron.php:64
285
  msgid "5 min"
286
- msgstr "5 мин"
287
 
288
- #: admin/pages/settings.php:366 includes/classes/class.cron.php:68
289
  msgid "10 min"
290
- msgstr "10 мин"
291
 
292
- #: admin/pages/settings.php:367 includes/classes/class.cron.php:72
293
  msgid "30 min"
294
- msgstr "30 мин"
295
 
296
- #: admin/pages/settings.php:368
297
  msgid "Hour"
298
- msgstr "1 час"
299
 
300
- #: admin/pages/settings.php:369
301
  msgid "Day"
302
- msgstr "1 день"
303
 
304
- #: admin/pages/settings.php:372
305
  msgid "Run every"
306
- msgstr "Запускать через"
307
 
308
- #: admin/pages/settings.php:373
309
  msgid "Select time at which the task will be repeated."
310
- msgstr "Выберите время, через которое будет повторяться задание."
311
 
312
- #: admin/pages/settings.php:380
313
  msgid "Images per iteration"
314
- msgstr "Количество изображений за итерацию"
315
 
316
- #: admin/pages/settings.php:382
317
  msgid ""
318
  "Specify the number of images that will be optimized during the job. For "
319
  "example, if you enter 5 and select 5 min, the plugin will optimize 5 images "
320
  "every 5 minutes."
321
  msgstr ""
322
- "Задайте количество изображений, которое будут оптимизировано во время "
323
- "выполнения задания. Например, если вы введёте 5 и выберете 5 мин., то плагин "
324
- "будет оптимизировать 5 изображений каждые 5 минут."
325
 
326
- #: admin/pages/settings.php:411
327
- msgid "Manage backups"
328
- msgstr "Управление резервными копиями"
329
 
330
- #: admin/pages/settings.php:412
331
- msgid "You can restore the original images from a backup or clear them."
 
 
332
  msgstr ""
333
- "Вы можете восстановить оригинальные изображения из резервной копии или "
334
- "очистить их."
335
 
336
- #: admin/pages/settings.php:419
337
- msgid "Are you sure?"
338
- msgstr "Вы уверены?"
339
 
340
- #: admin/pages/settings.php:419
341
- msgid "Restore"
342
- msgstr "Восстановить"
 
 
 
 
343
 
344
- #: admin/pages/settings.php:420
345
- msgid "Are you sure that you want to clear image backups folder?"
 
 
 
346
  msgstr ""
347
- "Вы действительно хотите очистить папку с резервными копиями изображений?"
348
 
349
- #: admin/pages/settings.php:420
350
- msgid "Clear Backup"
351
- msgstr "Очистить резервные копии"
352
 
353
- #: admin/pages/settings.php:427
354
- msgid "Restore completed."
355
- msgstr "Восстановление завершено."
356
 
357
- #: admin/pages/settings.php:428
358
- msgid "The backup folder was cleared."
359
- msgstr "Папка с резервными копиями очищена."
360
 
361
- #: admin/pages/statistic.php:46
362
- msgid "Robin image optimizer"
363
  msgstr ""
364
 
365
- #: admin/pages/statistic.php:85
366
- msgid "General"
367
- msgstr "Основные"
368
 
369
- #: admin/pages/statistic.php:165
370
- msgid "Image optimization dashboard"
371
- msgstr "Панель управления оптимизацией изображений"
372
 
373
- #: admin/pages/statistic.php:168
374
- msgid ""
375
- "Monitor image optimization statistics and run on demand or scheduled "
376
- "optimization."
377
  msgstr ""
378
- "Отслеживайте статистику оптимизации изображений и запускайте оптимизацию по "
379
- "требованию или по расписанию."
380
 
381
- #: admin/pages/statistic.php:176
382
- msgid ""
383
- "There are limitations for the specified server (server 1) – you can’t "
384
- "optimize images with the size greater than <span style=\"color:red\">5MB</"
385
- "span>. But you can enable the “Resizing large images” feature to reduce the "
386
- "image weight due to the proportional resizing before sending the file to the "
387
- "optimization server. "
388
- msgstr ""
389
- "Для выбранного вами сервера (сервер 1) есть ограничения, вы не можете "
390
- "оптимизировать изображения весом более <span style=\"color:red\">5МБ</span>. "
391
- "Но вы можете включить опцию \"Изменение размера больших изображений\", чтобы "
392
- "уменьшить вес изображений за счет пропорционального изменения размера, до "
393
- "его отправки на сервер оптимизации"
394
-
395
- #: admin/pages/statistic.php:178
396
- msgid ""
397
- "There are limitations for the specified server (server 2) – you can’t "
398
- "optimize images with the size greater than <span style=\"color:red\">1MB</"
399
- "span>. But you can enable the “Resizing large images” feature to reduce the "
400
- "image weight due to the proportional resizing before sending the file to the "
401
- "optimization server. Note: this server supports only one compression mode – "
402
- "“Normal”."
403
- msgstr ""
404
- "Для выбранного вами сервера (сервер 2) есть ограничения, вы не можете "
405
- "оптимизировать изображения весом более <span style=\"color:red\">1МБ</span>. "
406
- "Но вы можете включить опцию \"Изменение размера больших изображений\", чтобы "
407
- "уменьшить вес изображений за счет пропорционального изменения размера, до "
408
- "его отправки на сервер оптимизации. Также данный сервер поддерживает только "
409
- "один режим сжатия \"Нормальный\"."
410
-
411
- #: admin/pages/statistic.php:189
412
  msgid "You optimized"
413
  msgstr "Вы оптимизировали"
414
 
415
- #: admin/pages/statistic.php:189
416
  msgid "of your website's images"
417
  msgstr "изображений сайта"
418
 
419
- #: admin/pages/statistic.php:196 admin/pages/statistic.php:321
420
- msgid "Unoptimized"
421
- msgstr "Не оптимизировано"
422
-
423
- #: admin/pages/statistic.php:197 admin/pages/statistic.php:322
424
- msgid "Optimized"
425
- msgstr "Оптимизировано"
426
-
427
- #: admin/pages/statistic.php:198 admin/pages/statistic.php:323
428
- msgid "Error"
429
- msgstr "Ошибка"
430
-
431
- #: admin/pages/statistic.php:201
432
  msgid "Statistics"
433
  msgstr "Статистика"
434
 
435
- #: admin/pages/statistic.php:207
436
  msgid ""
437
  "that's the number of original images<br> you optimized with Image Optimizer"
438
  msgstr ""
439
  "количество исходных изображений<br> которые вы оптимизировали с Image "
440
  "Optimizer"
441
 
442
- #: admin/pages/statistic.php:213 admin/pages/statistic.php:327
443
- msgid "Original size"
444
- msgstr "Оригинальный размер"
445
-
446
- #: admin/pages/statistic.php:218 admin/pages/statistic.php:332
447
- msgid "Optimized size"
448
- msgstr "Оптимизированный размер"
449
-
450
- #: admin/pages/statistic.php:229
451
  msgid "that's the size you saved <br>by using Image Optimizer"
452
  msgstr ""
453
  "вы сохранили благодаря<br>\n"
454
  "Image Optimizer"
455
 
456
- #: admin/pages/statistic.php:241 admin/pages/statistic.php:345
457
- msgid "All images from the media library are optimized."
458
- msgstr "Все изображения из медиабиблиотеки оптимизированы."
459
-
460
- #: admin/pages/statistic.php:243 admin/pages/statistic.php:347
461
- msgid "Optimization in progress. Remained"
462
- msgstr "Идёт оптимизация. Осталось"
463
-
464
- #: admin/pages/statistic.php:243
465
  msgid "images."
466
  msgstr "изображений."
467
 
468
- #: admin/pages/statistic.php:263 admin/pages/statistic.php:271
469
- msgid "Run"
470
- msgstr "Запустить"
471
 
472
- #: admin/pages/statistic.php:269 admin/pages/statistic.php:275
473
- msgid "Stop"
474
- msgstr "Остановить"
 
 
475
 
476
- #: admin/pages/statistic.php:275
477
- msgid "Do you want to start optimization without backup?"
478
- msgstr "Вы хотите начать оптимизацию без возможности восстановления?"
479
 
480
- #: admin/pages/statistic.php:275
481
- msgid "Resume"
482
- msgstr "Возобновить"
483
 
484
- #: admin/pages/statistic.php:310
485
- msgid "Images optimization"
486
- msgstr "Оптимизация изображений"
487
 
488
- #: includes/class.plugin.php:85
489
- msgid "An invalid instance of the class was passed."
490
- msgstr "Был принят недопустимый экземпляр класса."
491
 
492
- #: includes/classes/class.attachment.php:163
493
- msgid "Failed to get optimized image from remote server"
494
- msgstr "Не удалось получить оптимизированное изображение с удаленного сервера"
495
 
496
- #: includes/classes/class.backup.php:77
497
- msgid "Unable to create backup folder."
498
- msgstr "Невозможно создать папку для резервного копирования."
499
 
500
- #: includes/classes/class.backup.php:132
501
- msgid "Unable to create backup subfolder."
502
- msgstr "Невозможно создать подпапку для резервного копирования."
503
 
504
- #: includes/classes/class.backup.php:260
505
- msgid "Unable to restore from a backup. There is no file."
506
- msgstr "Не удалось восстановить из резервной копии. Нет файла."
507
 
508
- #: includes/classes/class.cron.php:76
509
- msgid "60 min"
510
- msgstr "60 мин"
 
511
 
512
- #: includes/classes/class.cron.php:80
513
- msgid "daily"
514
- msgstr "ежедневно"
 
515
 
516
- #: includes/classes/class.image-processor-smushpro.php:43
517
- msgid "file not found"
518
- msgstr "файл не найден"
519
 
520
- #: includes/classes/class.image-processor-smushpro.php:48
521
- msgid "curl not work"
522
- msgstr "curl не работает"
523
 
524
- #: includes/classes/class.media-library.php:157
525
- msgid "No access for writing backups."
526
- msgstr "Нет доступа для записи резервных копий."
527
 
528
- #: includes/classes/class.media-library.php:163
529
- msgid "The uploads folder is not writable."
530
- msgstr "Папка uploads недоступна для записи."
 
 
 
 
 
 
531
 
532
- #: includes/classes/class.media-library.php:445
 
 
 
 
533
  msgid "New Filesize:"
534
  msgstr "Новый размер:"
535
 
536
- #: includes/classes/class.media-library.php:448
537
  msgid "Original Saving:"
538
  msgstr "Сжатие:"
539
 
540
- #: includes/classes/class.media-library.php:454
541
  msgid "Original Filesize:"
542
  msgstr "Размер оригинала:"
543
 
544
- #: includes/classes/class.media-library.php:457
545
  msgid "Level:"
546
  msgstr "Уровень:"
547
 
548
- #: includes/classes/class.media-library.php:473
 
 
 
 
 
 
 
 
549
  msgid "Thumbnails Optimized:"
550
  msgstr "Оптимизированные миниатюры:"
551
 
552
- #: includes/classes/class.media-library.php:477
553
  msgid "Overall Saving:"
554
  msgstr "Общее сохранение:"
555
 
556
- #: includes/classes/class.media-library.php:482
557
  msgid "Error Message:"
558
  msgstr "Сообщение об ошибке:"
559
 
560
- #: includes/classes/class.media-library.php:488
561
- #: includes/classes/class.media-library.php:494
562
- #: includes/classes/class.media-library.php:500
563
- #: includes/classes/class.media-library.php:515
564
  msgid "Optimization in progress"
565
  msgstr "Выполняется оптимизация"
566
 
567
- #: includes/classes/class.media-library.php:489
568
- #: includes/classes/class.media-library.php:495
569
- #: includes/classes/class.media-library.php:501
570
  msgid "Re-Optimize to"
571
  msgstr "Пережать в"
572
 
573
- #: includes/classes/class.media-library.php:506
574
  msgid "Recovery in progress"
575
  msgstr "Восстанавливается"
576
 
577
- #: includes/classes/class.media-library.php:506
578
  msgid "Restore original"
579
  msgstr "Восстановить оригинал"
580
 
581
- #: includes/classes/class.media-library.php:515
582
  msgid "Optimize"
583
  msgstr "Оптимизировать"
584
 
585
- #: robin-image-optimizer.php:27
 
 
 
 
586
  msgid ""
587
- "We found that you have the \"Clearfy - wordpress optimization plugin\" "
588
- "plugin installed, this plugin already has disable comments functions, so you "
589
- "can deactivate plugin \"Image optimizer\"!"
590
  msgstr ""
591
- "Мы обнаружили, что у вас установлен плагин «Clearfy - wordpress optimization "
592
- "plugin», этот плагин уже имеет функции оптимизации изображений, поэтому вы "
593
- "можете отключить плагин «Оптимизатор изображений»!"
594
 
595
- #: robin-image-optimizer.php:106
596
- msgid "Webcraftic Robin image optimizer"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  msgstr ""
598
 
599
- #~ msgid "Webcraftic Image optimizer"
600
- #~ msgstr "Webcraftic Image optimizer"
 
601
 
602
- #~ msgid ""
603
- #~ "Optimize images without losing quality, speed up your website load, "
604
- #~ "improve SEO and save money on server and CDN bandwidth."
605
- #~ msgstr ""
606
- #~ "Оптимизируйте изображения без потери качества, ускоряйте загрузку сайта, "
607
- #~ "улучшайте SEO и экономьте на трафик сервера и CDN."
608
 
609
- #~ msgid "Webcraftic <wordpress.webraftic@gmail.com>"
610
- #~ msgstr "Webcraftic <wordpress.webraftic@gmail.com>"
 
1
+ #
2
+ # Alexander Parfilov <alexparfilov@gmail.com>, 2018.
3
+ #
4
  msgid ""
5
  msgstr ""
6
  "Project-Id-Version: Image optimizer\n"
7
  "Report-Msgid-Bugs-To: \n"
8
+ "POT-Creation-Date: 2019-03-06 20:10+0000\n"
9
+ "PO-Revision-Date: 2019-03-06 20:10+0000\n"
10
+ "Last-Translator: wordpress.webraftic@gmail.com\n"
11
+ "Language-Team: Русский\n"
12
  "Language: ru_RU\n"
13
  "MIME-Version: 1.0\n"
14
  "Content-Type: text/plain; charset=UTF-8\n"
15
  "Content-Transfer-Encoding: 8bit\n"
16
+ "X-Generator: Loco https://localise.biz/\n"
17
  "X-Poedit-Basepath: ..\n"
18
+ "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
19
+ "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20
  "X-Poedit-SourceCharset: UTF-8\n"
21
  "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c\n"
22
  "X-Poedit-SearchPath-0: .\n"
23
  "X-Poedit-SearchPathExcluded-0: libs\n"
24
  "X-Poedit-SearchPathExcluded-1: .git\n"
25
+ "X-Loco-Version: 2.2.0; wp-5.1"
26
 
27
+ #: robin-image-optimizer.php:89
28
+ msgid "Webcraftic Robin image optimizer"
29
+ msgstr ""
30
+
31
+ #: includes/class.plugin.php:56
32
+ msgid "An invalid instance of the class was passed."
33
+ msgstr "Был принят недопустимый экземпляр класса."
34
+
35
+ #: admin/pages/log.php:76 admin/pages/log.php:125 admin/pages/log.php:135
36
+ #: admin/pages/settings.php:264
37
  msgid "Error Log"
38
  msgstr "Журнал ошибок"
39
 
40
+ #: admin/pages/log.php:77
41
+ msgid "Plugin debug report"
42
+ msgstr ""
43
+
44
+ #: admin/pages/log.php:125 admin/pages/statistic.php:154
45
+ #: admin/pages/statistic.php:161
46
+ #: admin/includes/classes/class.nextgen-landing.php:38
47
+ #: admin/includes/classes/class.nextgen-landing.php:39
48
  msgid "Image optimizer"
49
  msgstr "Оптим. изображений"
50
 
51
+ #: admin/pages/log.php:138
52
  msgid ""
53
  "In this section, you can track image optimization errors. Sending this log "
54
  "to us, will help in solving possible optimization issues."
56
  "В этом разделе вы можете отследить ошибки оптимизации изображений. Отправка "
57
  "этого лога нам, поможет в решении возможных проблем с оптимизацией."
58
 
59
+ #: admin/pages/settings.php:67
60
+ msgid "Settings"
61
+ msgstr ""
62
+
63
+ #: admin/pages/settings.php:68
64
+ msgid "Plugin configuration"
65
+ msgstr ""
66
 
67
+ #: admin/pages/settings.php:124
68
  msgid "Folder wp-content/uploads/ is unavailable for writing"
69
  msgstr "Папка wp-content/uploads/ недоступна для записи"
70
 
71
+ #: admin/pages/settings.php:128
72
  msgid "Folder wp-content/uploads/wio-backup/ is unavailable for writing"
73
  msgstr "Папка wp-content/uploads/wio-backup/ недоступна для записи"
74
 
75
+ #: admin/pages/settings.php:131
76
  msgid "Cron is disabled in wp-config.php"
77
+ msgstr "Cron отключен в wp-config.php"
78
 
79
+ #: admin/pages/settings.php:148
80
+ msgid "Main Settings"
81
+ msgstr "Основные настройки"
82
+
83
+ #: admin/pages/settings.php:148
84
  msgid "This section you can set main images optimization settings."
85
  msgstr ""
86
  "В этом разделе вы можете настроить основные параметры оптимизации "
87
  "изображений."
88
 
89
+ #: admin/pages/settings.php:154
90
  msgid "Optimization server"
91
  msgstr "Сервер оптимизации"
92
 
93
+ #: admin/pages/settings.php:158
94
  msgid "Server 1 (recommended) - image size limit up to 5 MB"
95
  msgstr "Сервер 1 (рекомендуется) - вес изображений не должен превышать 5МБ"
96
 
97
+ #: admin/pages/settings.php:163
98
  msgid "Server 2 - image size limit up to 1 MB"
99
  msgstr "Сервер 2 - вес изображений не должен превышать 1МБ"
100
 
101
+ #: admin/pages/settings.php:168
102
+ msgid "Server 3 - you can't use it on a localhost"
103
+ msgstr ""
104
+
105
+ #: admin/pages/settings.php:172
106
+ msgid "Server 4 - image size limit up to 5 MB"
107
+ msgstr ""
108
+
109
+ #: admin/pages/settings.php:176
110
  msgid ""
111
  "We use several free servers for image optimization and can’t fully guarantee "
112
  "their stable performance. The server can be not available in some countries "
125
  "оптимизированных изображений и на уровень оптимизации. По умолчанию всегда "
126
  "установлен самый лучший сервер с наименьшими ограничениями."
127
 
128
+ #: admin/pages/settings.php:185
129
  msgid "Compression mode"
130
  msgstr "Режим сжатия"
131
 
132
+ #: admin/pages/settings.php:189
133
+ msgid "Lossless"
134
+ msgstr ""
 
 
135
 
136
+ #: admin/pages/settings.php:190
137
  msgid ""
138
  "This mode provides lossless compression and your images will be optimized "
139
  "without visible changes. If you want an ideal image quality, we recommend "
146
  "приблизительно в 2 раза. Если вам этого недостаточно, попробуйте другие "
147
  "режимы."
148
 
149
+ #: admin/pages/settings.php:194
150
+ msgid "Lossy"
151
+ msgstr ""
 
 
152
 
153
+ #: admin/pages/settings.php:195
154
  msgid ""
155
  "This mode provides an ideal optimization of your images without significant "
156
  "quality loss. The file size will be reduced approximately 5 times with a "
162
  "раз при незначительном снижении качества изображений. Чаще всего, "
163
  "невооружённым взглядом это даже не заметно."
164
 
165
+ #: admin/pages/settings.php:199 admin/pages/statistic.php:344
166
+ #: admin/includes/classes/class.optimize-template.php:513
167
+ #: admin/includes/classes/class.optimize-template.php:561
168
  msgid "High"
169
  msgstr "Высокий"
170
 
171
+ #: admin/pages/settings.php:200 admin/pages/settings.php:205
172
  msgid ""
173
  "This mode will use all available optimization methods for maximum image "
174
  "compression. The file size will be reduced approximately 7 times. The "
182
  "этот режим, если вам требуется максимальное снижение веса, и вы готовы "
183
  "смириться с потерей качества изображений."
184
 
185
+ #: admin/pages/settings.php:204
186
+ #: admin/includes/classes/class.optimize-template.php:505
187
+ msgid "Custom"
188
+ msgstr ""
189
+
190
+ #: admin/pages/settings.php:209
191
  msgid "Select the compression mode appropriate for your case."
192
  msgstr "Выберите режим сжатия, подходящий для вашей ситуации."
193
 
194
+ #: admin/pages/settings.php:231
195
+ msgid "Enter custom quality"
196
+ msgstr ""
197
+
198
+ #: admin/pages/settings.php:233
199
+ msgid "custom quality 1-100"
200
+ msgstr ""
201
+
202
+ #: admin/pages/settings.php:242
203
  msgid "Auto optimization on upload"
204
  msgstr "Автоматическая оптимизация изображений при загрузке"
205
 
206
+ #: admin/pages/settings.php:244
207
  msgid ""
208
  "Automatically compress all images that you upload directly to the WordPress "
209
  "media library, when editing pages and posts or using themes and plugins."
212
  "медиабиблиотеку WordPress напрямую, при редактировании страниц и записей или "
213
  "с использованием тем и плагинов."
214
 
215
+ #: admin/pages/settings.php:253
216
  msgid "Backup images"
217
  msgstr "Резервное копирование изображений"
218
 
219
+ #: admin/pages/settings.php:255
220
  msgid ""
221
  "Before optimizing, all your images will be saved in a separate folder for "
222
  "future recovery."
224
  "Перед началом оптимизации, все ваши изображения будут сохранены в отдельной "
225
  "папке для возможности восстановления в будущем."
226
 
227
+ #: admin/pages/settings.php:266
228
  msgid "Enable error logging. The log will be displayed on a separate tab."
229
  msgstr ""
230
  "Включить ведение журнала ошибок. Он будет отображаться на отдельной вкладке."
231
 
232
+ #: admin/pages/settings.php:281
233
  msgid "Leave EXIF data"
234
  msgstr "Оставлять данные EXIF"
235
 
236
+ #: admin/pages/settings.php:283
237
  msgid ""
238
  "EXIF is information stored in photos: camera model, shutter speed, exposure "
239
  "compensation, ISO, GPS, etc. By default, the plugin removes EXIF extended "
245
  "удаляет расширенные данные EXIF. Если ваш проект посвящён фотографии и вам "
246
  "нужно отображать эти данные, то включите эту опцию."
247
 
248
+ #: admin/pages/settings.php:289
249
  msgid "Optimization"
250
  msgstr "Оптимизация"
251
 
252
+ #: admin/pages/settings.php:289
253
  msgid "Here you can specify additional image optimization options."
254
  msgstr ""
255
  "Здесь вы можете задать дополнительные параметры оптимизации изображений."
256
 
257
+ #: admin/pages/settings.php:297
258
  msgid "Resizing large images"
259
  msgstr "Изменение размера больших изображений"
260
 
261
+ #: admin/pages/settings.php:299
262
  msgid ""
263
  "When you upload images from a camera or stock, they may be too high "
264
  "resolution and it is not necessary for web. The option allows you to "
268
  "слишком высокого разрешения и для веба это не нужно. Опция позволяет "
269
  "автоматически изменять разрешение изображений при загрузке."
270
 
271
+ #: admin/pages/settings.php:315
272
+ msgid "Enter the maximum width (px)"
273
+ msgstr ""
274
 
275
+ #: admin/pages/settings.php:317 admin/pages/settings.php:327
276
  msgid ""
277
  "Set the maximum images resolution on the long side. For horizontal images, "
278
  "this will be the width, and for vertical images - the height. The resolution "
283
  "Разрешение изображений будет изменено пропорционально в соответствии с "
284
  "заданным значением."
285
 
286
+ #: admin/pages/settings.php:325
287
+ msgid "Enter the maximum height (px)"
288
+ msgstr ""
289
+
290
+ #: admin/pages/settings.php:345
291
  msgid "Optimize thumbnails"
292
  msgstr "Оптимизировать миниатюры"
293
 
294
+ #: admin/pages/settings.php:348
295
  msgid ""
296
  "Choose which sizes of thumbnails should be optimized and uncheck those that "
297
  "do not need optimization."
299
  "Выберите какие размеры миниатюр следует оптимизировать и снимите галочки с "
300
  "тех, оптимизация которых не нужна."
301
 
302
+ #: admin/pages/settings.php:373
303
+ msgid "Manage backups"
304
+ msgstr "Управление резервными копиями"
305
 
306
+ #: admin/pages/settings.php:376
307
+ msgid "You can restore the original images from a backup or clear them."
308
+ msgstr ""
309
+ "Вы можете восстановить оригинальные изображения из резервной копии или "
310
+ "очистить их."
311
 
312
+ #: admin/pages/settings.php:386
313
+ msgid "Are you sure?"
314
+ msgstr "Вы уверены?"
315
 
316
+ #: admin/pages/settings.php:387
317
+ msgid "Restore"
318
+ msgstr "Восстановить"
319
+
320
+ #: admin/pages/settings.php:389
321
+ msgid "Are you sure that you want to clear image backups folder?"
322
+ msgstr ""
323
+ "Вы действительно хотите очистить папку с резервными копиями изображений?"
324
+
325
+ #: admin/pages/settings.php:390
326
+ msgid "Clear Backup"
327
+ msgstr "Очистить резервные копии"
328
+
329
+ #: admin/pages/settings.php:403
330
+ msgid "Select all"
331
+ msgstr ""
332
+
333
+ #: admin/pages/settings.php:428
334
+ msgid "Start"
335
+ msgstr ""
336
+
337
+ #: admin/pages/settings.php:440
338
+ msgid "Restore completed."
339
+ msgstr "Восстановление завершено."
340
+
341
+ #: admin/pages/settings.php:443
342
+ msgid "The backup folder was cleared."
343
+ msgstr "Папка с резервными копиями очищена."
344
+
345
+ #: admin/pages/statistic.php:71 admin/pages/statistic.php:154
346
+ msgid "Robin image optimizer"
347
+ msgstr ""
348
+
349
+ #: admin/pages/statistic.php:72
350
+ msgid "Compress bulk of images"
351
+ msgstr ""
352
+
353
+ #: admin/pages/statistic.php:123
354
+ #, php-format
355
  msgid ""
356
+ "There were big changes in database schema. Please click <a href=\"#\" "
357
+ "id=\"wbcr-wio-meta-migration-action\" data-nonce=\"%s\">here</a> to upgrade "
358
+ "it to the latest version"
359
  msgstr ""
 
 
 
360
 
361
+ #: admin/pages/statistic.php:161
362
+ msgid "Bulk optimization"
363
+ msgstr ""
364
+
365
+ #: admin/pages/statistic.php:271
366
+ msgid "Images optimization"
367
+ msgstr "Оптимизация изображений"
368
+
369
+ #: admin/pages/statistic.php:287
370
+ #: admin/includes/classes/class.optimize-template.php:148
371
+ msgid "Unoptimized"
372
+ msgstr "Не оптимизировано"
373
+
374
+ #: admin/pages/statistic.php:293
375
+ #: admin/includes/classes/class.optimize-template.php:154
376
+ msgid "Optimized"
377
+ msgstr "Оптимизировано"
378
+
379
+ #: admin/pages/statistic.php:299
380
+ #: admin/includes/classes/class.optimize-template.php:160
381
+ msgid "Error"
382
+ msgstr "Ошибка"
383
+
384
+ #: admin/pages/statistic.php:307
385
+ #: admin/includes/classes/class.optimize-template.php:177
386
+ msgid "Original size"
387
+ msgstr "Оригинальный размер"
388
+
389
+ #: admin/pages/statistic.php:314
390
+ #: admin/includes/classes/class.optimize-template.php:184
391
+ msgid "Optimized size"
392
+ msgstr "Оптимизированный размер"
393
+
394
+ #: admin/pages/statistic.php:329
395
+ #: admin/includes/classes/class.optimize-template.php:211
396
+ msgid "All images from the media library are optimized."
397
+ msgstr "Все изображения из медиабиблиотеки оптимизированы."
398
+
399
+ #: admin/pages/statistic.php:331
400
+ #: admin/includes/classes/class.optimize-template.php:213
401
+ msgid "Optimization in progress. Remained"
402
+ msgstr "Идёт оптимизация. Осталось"
403
+
404
+ #: admin/pages/statistic.php:340
405
+ #: admin/includes/classes/class.optimize-template.php:541
406
+ msgid "Normal"
407
+ msgstr "Нормальный"
408
+
409
+ #: admin/pages/statistic.php:342
410
+ #: admin/includes/classes/class.optimize-template.php:551
411
+ msgid "Medium"
412
+ msgstr "Средний"
413
+
414
+ #: admin/ajax/meta-migrations.php:169
415
+ #, php-format
416
+ msgid "Items migrated: %s. Please, click the same action to proceed."
417
+ msgstr ""
418
+
419
+ #: admin/ajax/meta-migrations.php:177
420
+ msgid "No more items to be migrated. Finishing-up..."
421
+ msgstr ""
422
+
423
+ #: admin/ajax/select-server.php:19 admin/ajax/check-servers-status.php:24
424
+ msgid "Server name is empty!"
425
+ msgstr ""
426
+
427
+ #: includes/classes/class.backup.php:78 includes/classes/class.backup.php:97
428
+ msgid "Unable to create backup folder."
429
+ msgstr "Невозможно создать папку для резервного копирования."
430
+
431
+ #: includes/classes/class.backup.php:168
432
+ msgid "Unable to create backup subfolder."
433
+ msgstr "Невозможно создать подпапку для резервного копирования."
434
+
435
+ #: includes/classes/class.backup.php:266
436
+ msgid "Unable to create temp folder."
437
+ msgstr ""
438
+
439
+ #: includes/classes/class.backup.php:280
440
+ msgid "Could not copy the file to the temporary directory"
441
+ msgstr ""
442
+
443
+ #: includes/classes/class.backup.php:291
444
+ msgid ""
445
+ "It is not possible to create a temporary file, the backup folder is not "
446
+ "writable."
447
+ msgstr ""
448
+
449
+ #: includes/classes/class.backup.php:359
450
+ msgid "Unable to restore from a backup. There is no file."
451
+ msgstr "Не удалось восстановить из резервной копии. Нет файла."
452
+
453
+ #: includes/classes/class.attachment.php:289
454
+ msgid "Failed to get optimized image from remote server"
455
+ msgstr "Не удалось получить оптимизированное изображение с удаленного сервера"
456
+
457
+ #: includes/classes/class.media-library.php:206
458
+ msgid "No access for writing backups."
459
+ msgstr "Нет доступа для записи резервных копий."
460
+
461
+ #: includes/classes/class.media-library.php:214
462
+ msgid "The uploads folder is not writable."
463
+ msgstr "Папка uploads недоступна для записи."
464
+
465
+ #: includes/classes/class.cron.php:54
466
+ msgid "Scheduled optimization"
467
+ msgstr ""
468
+
469
+ #: includes/classes/class.cron.php:54
470
+ msgid "Schedule your images optimization."
471
+ msgstr ""
472
+
473
+ #: includes/classes/class.cron.php:61
474
  msgid "1 min"
475
+ msgstr ""
476
 
477
+ #: includes/classes/class.cron.php:62
478
  msgid "2 min"
479
+ msgstr ""
480
 
481
+ #: includes/classes/class.cron.php:63
482
  msgid "5 min"
483
+ msgstr ""
484
 
485
+ #: includes/classes/class.cron.php:64
486
  msgid "10 min"
487
+ msgstr ""
488
 
489
+ #: includes/classes/class.cron.php:65
490
  msgid "30 min"
491
+ msgstr ""
492
 
493
+ #: includes/classes/class.cron.php:66
494
  msgid "Hour"
495
+ msgstr ""
496
 
497
+ #: includes/classes/class.cron.php:67
498
  msgid "Day"
499
+ msgstr ""
500
 
501
+ #: includes/classes/class.cron.php:70
502
  msgid "Run every"
503
+ msgstr ""
504
 
505
+ #: includes/classes/class.cron.php:71
506
  msgid "Select time at which the task will be repeated."
507
+ msgstr ""
508
 
509
+ #: includes/classes/class.cron.php:78
510
  msgid "Images per iteration"
511
+ msgstr ""
512
 
513
+ #: includes/classes/class.cron.php:80
514
  msgid ""
515
  "Specify the number of images that will be optimized during the job. For "
516
  "example, if you enter 5 and select 5 min, the plugin will optimize 5 images "
517
  "every 5 minutes."
518
  msgstr ""
 
 
 
519
 
520
+ #: admin/includes/classes/class.optimize-template.php:53
521
+ msgid "Image optimization dashboard"
522
+ msgstr "Панель управления оптимизацией изображений"
523
 
524
+ #: admin/includes/classes/class.optimize-template.php:55
525
+ msgid ""
526
+ "Monitor image optimization statistics and run on demand or scheduled "
527
+ "optimization."
528
  msgstr ""
529
+ "Отслеживайте статистику оптимизации изображений и запускайте оптимизацию по "
530
+ "требованию или по расписанию."
531
 
532
+ #: admin/includes/classes/class.optimize-template.php:102
533
+ msgid "Select optimization server:"
534
+ msgstr ""
535
 
536
+ #: admin/includes/classes/class.optimize-template.php:103
537
+ msgid ""
538
+ "Please, find the list of available servers for image optimization below. If "
539
+ "the server has a state “Down”, it means that the server is not available, "
540
+ "and you should choose another one. “Stable” means that the server is "
541
+ "available and you can use it."
542
+ msgstr ""
543
 
544
+ #: admin/includes/classes/class.optimize-template.php:110
545
+ #: admin/includes/classes/class.optimize-template.php:113
546
+ #: admin/includes/classes/class.optimize-template.php:116
547
+ #: admin/includes/classes/class.optimize-template.php:119
548
+ msgid "Server"
549
  msgstr ""
 
550
 
551
+ #: admin/includes/classes/class.optimize-template.php:110
552
+ msgid "recommended"
553
+ msgstr ""
554
 
555
+ #: admin/includes/classes/class.optimize-template.php:110
556
+ msgid "image size limit up to 5 MB"
557
+ msgstr ""
558
 
559
+ #: admin/includes/classes/class.optimize-template.php:113
560
+ msgid "image size limit up to 1 MB"
561
+ msgstr ""
562
 
563
+ #: admin/includes/classes/class.optimize-template.php:116
564
+ msgid "you can't use it on a localhost"
565
  msgstr ""
566
 
567
+ #: admin/includes/classes/class.optimize-template.php:119
568
+ msgid "image compressor test"
569
+ msgstr ""
570
 
571
+ #: admin/includes/classes/class.optimize-template.php:123
572
+ msgid "Status:"
573
+ msgstr ""
574
 
575
+ #: admin/includes/classes/class.optimize-template.php:123
576
+ msgid "Stable"
 
 
577
  msgstr ""
 
 
578
 
579
+ #: admin/includes/classes/class.optimize-template.php:123
580
+ msgid "Down"
581
+ msgstr ""
582
+
583
+ #: admin/includes/classes/class.optimize-template.php:139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  msgid "You optimized"
585
  msgstr "Вы оптимизировали"
586
 
587
+ #: admin/includes/classes/class.optimize-template.php:141
588
  msgid "of your website's images"
589
  msgstr "изображений сайта"
590
 
591
+ #: admin/includes/classes/class.optimize-template.php:167
 
 
 
 
 
 
 
 
 
 
 
 
592
  msgid "Statistics"
593
  msgstr "Статистика"
594
 
595
+ #: admin/includes/classes/class.optimize-template.php:172
596
  msgid ""
597
  "that's the number of original images<br> you optimized with Image Optimizer"
598
  msgstr ""
599
  "количество исходных изображений<br> которые вы оптимизировали с Image "
600
  "Optimizer"
601
 
602
+ #: admin/includes/classes/class.optimize-template.php:199
 
 
 
 
 
 
 
 
603
  msgid "that's the size you saved <br>by using Image Optimizer"
604
  msgstr ""
605
  "вы сохранили благодаря<br>\n"
606
  "Image Optimizer"
607
 
608
+ #: admin/includes/classes/class.optimize-template.php:214
 
 
 
 
 
 
 
 
609
  msgid "images."
610
  msgstr "изображений."
611
 
612
+ #: admin/includes/classes/class.optimize-template.php:295
613
+ msgid "Optimization log"
614
+ msgstr ""
615
 
616
+ #: admin/includes/classes/class.optimize-template.php:296
617
+ msgid ""
618
+ "Optimization log shows the last 100 optimized images. You can check the "
619
+ "quality of the image by clicking on the file name."
620
+ msgstr ""
621
 
622
+ #: admin/includes/classes/class.optimize-template.php:303
623
+ msgid "File name"
624
+ msgstr ""
625
 
626
+ #: admin/includes/classes/class.optimize-template.php:304
627
+ msgid "Inital size"
628
+ msgstr ""
629
 
630
+ #: admin/includes/classes/class.optimize-template.php:305
631
+ msgid "Current size"
632
+ msgstr ""
633
 
634
+ #: admin/includes/classes/class.optimize-template.php:307
635
+ msgid "WebP size"
636
+ msgstr ""
637
 
638
+ #: admin/includes/classes/class.optimize-template.php:309
639
+ msgid "Original Saving"
640
+ msgstr ""
641
 
642
+ #: admin/includes/classes/class.optimize-template.php:310
643
+ msgid "Compressed thumbnails"
644
+ msgstr ""
645
 
646
+ #: admin/includes/classes/class.optimize-template.php:311
647
+ msgid "Overall Saving"
648
+ msgstr ""
649
 
650
+ #: admin/includes/classes/class.optimize-template.php:317
651
+ msgid "You don't have optimized images."
652
+ msgstr ""
653
 
654
+ #: admin/includes/classes/class.optimize-template.php:379
655
+ #: admin/includes/classes/class.optimize-template.php:386
656
+ msgid "Run"
657
+ msgstr "Запустить"
658
 
659
+ #: admin/includes/classes/class.optimize-template.php:384
660
+ #: admin/includes/classes/class.optimize-template.php:399
661
+ msgid "Stop"
662
+ msgstr "Остановить"
663
 
664
+ #: admin/includes/classes/class.optimize-template.php:390
665
+ msgid "Information"
666
+ msgstr ""
667
 
668
+ #: admin/includes/classes/class.optimize-template.php:390
669
+ msgid "Optimize now"
670
+ msgstr ""
671
 
672
+ #: admin/includes/classes/class.optimize-template.php:390
673
+ msgid "Scheduled"
674
+ msgstr ""
675
 
676
+ #: admin/includes/classes/class.optimize-template.php:394
677
+ msgid ""
678
+ "Are you sure that you want to leave the page? The optimization process is "
679
+ "not over yet, stay on the page until the end of the optimization process."
680
+ msgstr ""
681
+
682
+ #: admin/includes/classes/class.optimize-template.php:397
683
+ msgid "Do you want to start optimization without backup?"
684
+ msgstr "Вы хотите начать оптимизацию без возможности восстановления?"
685
 
686
+ #: admin/includes/classes/class.optimize-template.php:398
687
+ msgid "Resume"
688
+ msgstr "Возобновить"
689
+
690
+ #: admin/includes/classes/class.optimize-template.php:487
691
  msgid "New Filesize:"
692
  msgstr "Новый размер:"
693
 
694
+ #: admin/includes/classes/class.optimize-template.php:490
695
  msgid "Original Saving:"
696
  msgstr "Сжатие:"
697
 
698
+ #: admin/includes/classes/class.optimize-template.php:496
699
  msgid "Original Filesize:"
700
  msgstr "Размер оригинала:"
701
 
702
+ #: admin/includes/classes/class.optimize-template.php:499
703
  msgid "Level:"
704
  msgstr "Уровень:"
705
 
706
+ #: admin/includes/classes/class.optimize-template.php:509
707
+ msgid "lossless"
708
+ msgstr ""
709
+
710
+ #: admin/includes/classes/class.optimize-template.php:511
711
+ msgid "lossy"
712
+ msgstr ""
713
+
714
+ #: admin/includes/classes/class.optimize-template.php:521
715
  msgid "Thumbnails Optimized:"
716
  msgstr "Оптимизированные миниатюры:"
717
 
718
+ #: admin/includes/classes/class.optimize-template.php:525
719
  msgid "Overall Saving:"
720
  msgstr "Общее сохранение:"
721
 
722
+ #: admin/includes/classes/class.optimize-template.php:530
723
  msgid "Error Message:"
724
  msgstr "Сообщение об ошибке:"
725
 
726
+ #: admin/includes/classes/class.optimize-template.php:539
727
+ #: admin/includes/classes/class.optimize-template.php:549
728
+ #: admin/includes/classes/class.optimize-template.php:559
729
+ #: admin/includes/classes/class.optimize-template.php:581
730
  msgid "Optimization in progress"
731
  msgstr "Выполняется оптимизация"
732
 
733
+ #: admin/includes/classes/class.optimize-template.php:541
734
+ #: admin/includes/classes/class.optimize-template.php:551
735
+ #: admin/includes/classes/class.optimize-template.php:561
736
  msgid "Re-Optimize to"
737
  msgstr "Пережать в"
738
 
739
+ #: admin/includes/classes/class.optimize-template.php:569
740
  msgid "Recovery in progress"
741
  msgstr "Восстанавливается"
742
 
743
+ #: admin/includes/classes/class.optimize-template.php:570
744
  msgid "Restore original"
745
  msgstr "Восстановить оригинал"
746
 
747
+ #: admin/includes/classes/class.optimize-template.php:583
748
  msgid "Optimize"
749
  msgstr "Оптимизировать"
750
 
751
+ #: admin/pages/parts/optimize-popup.php:1
752
+ msgid "What you need to know before starting optimization."
753
+ msgstr ""
754
+
755
+ #: admin/pages/parts/optimize-popup.php:13
756
  msgid ""
757
+ "Remember that the optimization of a large number of images may take some "
758
+ "time depending on your server and network speed."
 
759
  msgstr ""
 
 
 
760
 
761
+ #: admin/pages/parts/optimize-popup.php:25
762
+ msgid ""
763
+ "If you choose to optimize now, you should keep this page open until a bulk "
764
+ "optimization is performed. If you leave, the optimization process will stop "
765
+ "and you will need to run it again.."
766
+ msgstr ""
767
+
768
+ #: admin/pages/parts/optimize-popup.php:36
769
+ msgid "Have questions? See the documentation."
770
+ msgstr ""
771
+
772
+ #: admin/pages/parts/optimize-popup.php:36
773
+ msgid "Open documentation."
774
+ msgstr ""
775
+
776
+ #: includes/classes/processors/class.image-processor-smushpro.php:51
777
+ msgid "file not found"
778
+ msgstr "файл не найден"
779
+
780
+ #: includes/classes/processors/class.image-processor-smushpro.php:56
781
+ msgid "curl not work"
782
+ msgstr "curl не работает"
783
+
784
+ #. Description of the plugin
785
+ msgid ""
786
+ "Optimize images without losing quality, speed up your website load, improve "
787
+ "SEO and save money on server and CDN bandwidth."
788
  msgstr ""
789
 
790
+ #. URI of the plugin
791
+ msgid "https://wordpress.org/plugins/robin-image-optimizer/"
792
+ msgstr ""
793
 
794
+ #. Author of the plugin
795
+ msgid "Webcraftic <wordpress.webraftic@gmail.com>"
796
+ msgstr ""
 
 
 
797
 
798
+ #. Author URI of the plugin
799
+ msgid "https://clearfy.pro"
800
+ msgstr ""
libs/addons/admin/ajax/folders.php ADDED
@@ -0,0 +1,434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * AJAX обработчик выбора папки
5
+ * todo: добавить обработку ошибок
6
+ * todo: добавить nonce
7
+ */
8
+ add_action( 'wp_ajax_wriop_browse_dir', function () {
9
+ if ( ! current_user_can( 'manage_options' ) ) {
10
+ wp_die( - 1 );
11
+ }
12
+
13
+ if ( is_main_site() ) {
14
+ $base = get_home_path();
15
+ $root = wp_normalize_path( $base );
16
+ } else {
17
+ $up = wp_upload_dir();
18
+ $root = wp_normalize_path( $up['basedir'] );
19
+ }
20
+
21
+ $dir = trim( WRIO_Plugin::app()->request->post( 'dir', null, 'rawurldecode' ) );
22
+
23
+ $multiselect = WRIO_Plugin::app()->request->post( 'multiSelect' );
24
+ $multiselect = $multiselect == 'true' ? true : false;
25
+
26
+ $only_folders = WRIO_Plugin::app()->request->post( 'onlyFolders' );
27
+ $only_folders = $only_folders == 'true' ? true : false;
28
+ $only_folders = $dir == '/' || $only_folders;
29
+
30
+ $only_files = WRIO_Plugin::app()->request->post( 'onlyFiles' );
31
+ $only_files = $only_files == 'true' ? true : false;
32
+
33
+ $selected_dir = trailingslashit( $root ) . ( $dir == '/' ? '' : $dir );
34
+
35
+ // set checkbox if multiSelect set to true
36
+ $checkbox = $multiselect ? "<input type='checkbox' />" : null;
37
+
38
+ $upload_dir = wp_upload_dir();
39
+ $upload_dir_path = trailingslashit( str_replace( untrailingslashit( ABSPATH ), '', $upload_dir['basedir'] ) );
40
+
41
+ $exclude_dirs = [
42
+ $upload_dir_path . 'wio_backup',
43
+ '/wp-admin',
44
+ '/wp-includes'
45
+ ];
46
+ // исключаем все директории /wp-content/uploads/2019 - они уже оптимизируются в медиабиблиотеке.
47
+ // с основания WP в 2003 году до текущего года + 1 на всякий случай.
48
+ $year = date( 'Y' ) + 1;
49
+ for ( $i = 2003; $i <= $year; $i ++ ) {
50
+ $exclude_dirs[] = $upload_dir_path . $i;
51
+ }
52
+
53
+ if ( file_exists( $selected_dir ) && is_dir( $selected_dir ) ) {
54
+
55
+ $files = scandir( $selected_dir );
56
+ $return_dir = substr( $selected_dir, strlen( $root ) );
57
+
58
+ natcasesort( $files );
59
+
60
+ if ( count( $files ) > 2 ) { // The 2 accounts for . and ..
61
+ echo "<ul class='jqueryFileTree'>";
62
+ $counter = 0;
63
+ foreach ( $files as $file ) {
64
+ // если в папке очень много файлов, то показываем не все.
65
+ if ( $counter ++ > 200 ) {
66
+ break;
67
+ }
68
+ // если это папка бекап или другое исключение - пропускаем
69
+ if ( in_array( $return_dir . $file, $exclude_dirs ) ) {
70
+ continue;
71
+ }
72
+
73
+ $htmlRel = str_replace( "'", "&apos;", $return_dir . $file );
74
+ $htmlName = htmlentities( $file );
75
+ $ext = preg_replace( '/^.*\./', '', $file );
76
+
77
+ if ( file_exists( $selected_dir . $file ) && $file != '.' && $file != '..' ) {
78
+ //KEEP the spaces in front of the rel values - it's a trick to make WP Hide not replace the wp-content path
79
+ if ( is_dir( $selected_dir . $file ) && ( ! $only_files || $only_folders ) ) {
80
+ echo "<li class='directory collapsed'>{$checkbox}<a rel=' " . $htmlRel . "/'>" . $htmlName . "</a></li>";
81
+ } else if ( ! $only_folders || $only_files ) {
82
+ echo "<li class='file ext_{$ext}'>{$checkbox}<a rel=' " . $htmlRel . "'>" . $htmlName . "</a></li>";
83
+ }
84
+ }
85
+ }
86
+
87
+ echo "</ul>";
88
+ }
89
+ }
90
+ die();
91
+ } );
92
+
93
+ /**
94
+ * AJAX обработчик добавления папки
95
+ */
96
+ add_action( 'wp_ajax_wrio-add-custom-folder', function () {
97
+ check_admin_referer( 'bulk_optimization' );
98
+
99
+ if ( ! current_user_can( 'manage_options' ) ) {
100
+ wp_die( - 1 );
101
+ }
102
+
103
+ $path = WRIO_Plugin::app()->request->request( 'path', null, true );
104
+
105
+ if ( empty( $path ) ) {
106
+ wp_die( - 1 );
107
+ }
108
+
109
+ $cf = WRIO_Custom_Folders::get_instance();
110
+ $folder = $cf->addFolder( $path );
111
+
112
+ if ( is_wp_error( $folder ) ) {
113
+ wp_send_json_error( [
114
+ 'error_message' => $folder->get_error_message(),
115
+ 'error_code' => $folder->get_error_code()
116
+ ] );
117
+ }
118
+
119
+ wp_send_json_success( $folder->toArray() );
120
+ } );
121
+
122
+ /**
123
+ * AJAX индексация папки
124
+ */
125
+ add_action( 'wp_ajax_wrio-scan-folder', function () {
126
+ check_admin_referer( 'bulk_optimization' );
127
+
128
+ if ( ! current_user_can( 'manage_options' ) ) {
129
+ wp_die( - 1 );
130
+ }
131
+
132
+ $uid = WRIO_Plugin::app()->request->request( 'uid', null, true );
133
+ $offset = WRIO_Plugin::app()->request->request( 'offset', 0, true );
134
+ $total = WRIO_Plugin::app()->request->request( 'total', 0, true );
135
+
136
+ if ( empty( $uid ) ) {
137
+ wp_die( - 1 );
138
+ }
139
+
140
+ $max_process_elements = 100; // сколько элементов за итерацию индексирования
141
+
142
+ $cf = WRIO_Custom_Folders::get_instance();
143
+ $folder = $cf->getFolder( $uid );
144
+
145
+ if ( ! $total ) {
146
+ $total = $folder->reCountFiles();
147
+ $cf->saveFolders();
148
+ }
149
+
150
+ $processed_count = $folder->indexing( $offset, $max_process_elements );
151
+ $offset = $offset + $processed_count;
152
+
153
+ $results = [
154
+ 'offset' => $offset,
155
+ 'total' => $total,
156
+ 'complete' => false,
157
+ 'percent' => 0,
158
+ ];
159
+
160
+ if ( $total ) {
161
+ $results['percent'] = 100 - ( ( $total - $offset ) * 100 / $total );
162
+ /**
163
+ * Операция индексирования состоит из двух этапов. Проверка существующих файлов и поиск новых файлов.
164
+ * Поэтому процент делим на 2 и добавляем 50% т.к. это вторая часть.
165
+ */
166
+ $results['percent'] = $results['percent'] / 2 + 50;
167
+ }
168
+
169
+ if ( $offset >= $total ) {
170
+ $results['percent'] = 100;
171
+ $results['complete'] = true;
172
+ }
173
+
174
+ wp_send_json_success( $results );
175
+ } );
176
+
177
+ /**
178
+ * AJAX обработчик удаления папки
179
+ */
180
+ add_action( 'wp_ajax_wriop_remove_folder', function () {
181
+ $uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
182
+ if ( ! $uid ) {
183
+ die();
184
+ }
185
+ $cf = WRIO_Custom_Folders::get_instance();
186
+ $cf->removeFolder( $uid );
187
+ $cf->saveFolders();
188
+
189
+ die();
190
+ } );
191
+
192
+ /**
193
+ * AJAX проверка проиндексированных файлов
194
+ */
195
+ add_action( 'wp_ajax_wriop_folder_sync_index', function () {
196
+ $uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
197
+ $offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0;
198
+ $total = isset( $_POST['total'] ) ? intval( $_POST['total'] ) : 0;
199
+ $max_process_elements = 20; // сколько элементов за итерацию индексирования
200
+
201
+ $cf = WRIO_Custom_Folders::get_instance();
202
+ $folder = $cf->getFolder( $uid );
203
+ $total = $folder->countIndexedFiles();
204
+ $processed_count = $folder->syncIndex( $offset, $max_process_elements );
205
+ $offset = $offset + $processed_count;
206
+ $results = [
207
+ 'offset' => $offset,
208
+ 'total' => $total,
209
+ 'complete' => false,
210
+ 'percent' => 0,
211
+ ];
212
+ if ( $total ) {
213
+ $results['percent'] = 100 - ( ( $total - $offset ) * 100 / $total );
214
+ /**
215
+ * Операция индексирования состоит из двух этапов. Проверка существующих файлов и поиск новых файлов.
216
+ * Поэтому процент делим на 2. Это первая часть.
217
+ */
218
+ $results['percent'] = $results['percent'] / 2;
219
+ }
220
+
221
+ if ( $offset >= $total ) {
222
+ $results['percent'] = 100;
223
+ $results['complete'] = true;
224
+ }
225
+
226
+ wp_send_json( $results );
227
+ } );
228
+
229
+ /**
230
+ * AJAX массовая оптимизация
231
+ */
232
+ /*add_action( 'wp_ajax_wriop_process_cf_images', function () {
233
+ check_admin_referer( 'wio-iph' );
234
+ $reset_current_error = (bool) WRIO_Plugin::app()->request->request( 'reset_current_errors' );
235
+
236
+ // в ajax запросе мы не знаем, получен ли он из мультиадминки или из обычной. Поэтому проверяем параметр, полученный из frontend
237
+ if ( isset( $_POST['multisite'] ) and $_POST['multisite'] ) {
238
+ $multisite = new WIO_Multisite;
239
+ $multisite->initHooks();
240
+ }
241
+
242
+ $cf = WRIO_Custom_Folders::get_instance();
243
+
244
+ /*$folders = $cf->getFolders();
245
+
246
+ if ( ! empty( $folders ) ) {
247
+ foreach ( (array) $folders as $folder ) {
248
+ $folder = $cf->getFolder( $folder->get( 'uid' ) );
249
+ $count_files = $folder->countFiles();
250
+ $count_indexed_files = $folder->countIndexedFiles();
251
+ $test = 'fsdf';
252
+ }
253
+ }*/
254
+
255
+ /*if ( $reset_current_error ) {
256
+ // сбрасываем текущие ошибки оптимизации
257
+ $cf->resetCurrentErrors();
258
+ }
259
+
260
+ $max_process_per_request = 1;
261
+ $optimized_data = $cf->processUnoptimizedImages( $max_process_per_request );
262
+
263
+ // если изображения закончились - посылаем команду завершения
264
+ if ( $optimized_data['remain'] <= 0 ) {
265
+ $optimized_data['end'] = true;
266
+ }
267
+ wp_send_json( $optimized_data );
268
+ } );*/
269
+
270
+ /**
271
+ * AJAX массовая оптимизация выбранной папки
272
+ */
273
+ /*add_action( 'wp_ajax_wriop_process_cf_folder_images', function () {
274
+ check_admin_referer( 'wio-iph' );
275
+
276
+ // в ajax запросе мы не знаем, получен ли он из мультиадминки или из обычной. Поэтому проверяем параметр, полученный из frontend
277
+ if ( isset( $_POST['multisite'] ) and $_POST['multisite'] ) {
278
+ $multisite = new WIO_Multisite;
279
+ $multisite->initHooks();
280
+ }
281
+
282
+ $cf = WRIO_Custom_Folders::get_instance();
283
+ $max_process_per_request = 1;
284
+
285
+ add_filter( 'wriop_cf_current_folder', function ( $folder_uid ) {
286
+ $folder_uid = isset( $_POST['uid'] ) ? $_POST['uid'] : false;
287
+
288
+ return $folder_uid;
289
+ } );
290
+
291
+ $optimized_data = $cf->processUnoptimizedImages( $max_process_per_request );
292
+
293
+ // если изображения закончились - посылаем команду завершения
294
+ if ( $optimized_data['remain'] <= 0 ) {
295
+ $optimized_data['end'] = true;
296
+ }
297
+ wp_send_json( $optimized_data );
298
+ } );*/
299
+
300
+ /**
301
+ * Переоптимизация cf_image. AJAX
302
+ *
303
+ */
304
+ add_action( 'wp_ajax_wio_cf_reoptimize_image', function () {
305
+ $image_id = (int) $_POST['id'];
306
+ $backup = WRIOP_Backup::get_instance();
307
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
308
+ $cf = WRIO_Custom_Folders::get_instance();
309
+
310
+ if ( $backup_origin_images and ! $backup->isBackupWritable() ) {
311
+ echo $cf->getMediaColumnContent( $image_id );
312
+ die();
313
+ }
314
+
315
+ wp_suspend_cache_addition( true );
316
+
317
+ $default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
318
+ $level = isset( $_POST['level'] ) ? sanitize_text_field( $_POST['level'] ) : $default_level;
319
+
320
+ $optimized_data = $cf->optimizeImage( $image_id, $level );
321
+
322
+ if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
323
+ echo 'processing'; // эту строку не локализировать!
324
+ die();
325
+ }
326
+
327
+ echo $cf->getMediaColumnContent( $image_id );
328
+ die();
329
+ } );
330
+
331
+ /**
332
+ * Восстановление
333
+ *
334
+ */
335
+ add_action( 'wp_ajax_wio_cf_restore_image', function () {
336
+ wp_suspend_cache_addition( true );
337
+ $image_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
338
+
339
+ $cf = WRIO_Custom_Folders::get_instance();
340
+ $cf_image = $cf->getImage( $image_id );
341
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
342
+ if ( $cf_image->isOptimized() ) {
343
+ $restored = $cf_image->restore();
344
+ if ( ! is_wp_error( $restored ) ) {
345
+ $optimization_data = $cf_image->getOptimizationData();
346
+ $optimized_size = $optimization_data->get_final_size();
347
+ $original_size = $optimization_data->get_original_size();
348
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
349
+ $image_statistics->deductFromField( 'original_size', $original_size );
350
+ $image_statistics->save();
351
+
352
+ $folder = $cf->getFolder( $cf_image->get( 'folder_uid' ) );
353
+ $folder->reCountOptimizedFiles();
354
+ $cf->saveFolders();
355
+ }
356
+ }
357
+
358
+ echo $cf->getMediaColumnContent( $image_id );
359
+ die();
360
+ } );
361
+
362
+ /**
363
+ * AJAX обработчик восстановления из резервной копии
364
+ */
365
+ /*add_action( 'wp_ajax_wio_cf_restore_backup', function () {
366
+ check_admin_referer( 'wio-iph' );
367
+ $max_process_per_request = 10; // сколько картинок восстанавливаем за 1 запрос
368
+ $total = sanitize_text_field( $_POST['total'] );
369
+ if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] ) {
370
+ switch_to_blog( intval( $_POST['blog_id'] ) );
371
+ }
372
+ $folder_uid = isset( $_POST['uid'] ) ? sanitize_text_field( $_POST['uid'] ) : false;
373
+ $cf = WRIO_Custom_Folders::get_instance();
374
+ if ( $total == '?' ) {
375
+ $total = RIO_Process_Queue::count_by_type_status( 'cf_image', 'success' );
376
+ }
377
+ $restored_data = $cf->restoreFolderFromBackup( $folder_uid, $max_process_per_request );
378
+ if ( isset( $_POST['blog_id'] ) && $_POST['blog_id'] ) {
379
+ restore_current_blog();
380
+ }
381
+ $restored_data['total'] = $total;
382
+ if ( $total ) {
383
+ $restored_data['percent'] = 100 - ( $restored_data['remain'] * 100 / $total );
384
+ } else {
385
+ $restored_data['percent'] = 0;
386
+ }
387
+ // если изображения закончились - посылаем команду завершения
388
+ if ( $restored_data['remain'] <= 0 ) {
389
+ $restored_data['end'] = true;
390
+ }
391
+ wp_send_json( $restored_data );
392
+ } );*/
393
+
394
+ /**
395
+ * Загружает шаблон для всплывающий окон
396
+ */
397
+ /*add_action( 'wp_ajax_wio_cf_get_template_part', function () {
398
+ $template = sanitize_text_field( $_POST['template'] );
399
+ $templates = [
400
+ 'select_folder' => 'select-folder.php',
401
+ 'restore_folder' => 'restore-folder.php',
402
+ 'sync_folder' => 'sync-folder.php',
403
+ 'optimize_folder' => 'optimize-folder.php',
404
+ 'sync_all_folders' => 'sync-all-folders.php',
405
+ 'folders_table_body' => 'folders-table-body.php',
406
+ ];
407
+ if ( isset( $templates[ $template ] ) ) {
408
+ $template_file = WRIOP_PLUGIN_DIR . '/admin/pages/parts/' . $templates[ $template ];
409
+ if ( file_exists( $template_file ) ) {
410
+ include( $template_file );
411
+ }
412
+ }
413
+ die();
414
+ } );*/
415
+
416
+ /**
417
+ * Загружает шаблон для всплывающий окон
418
+ */
419
+ add_action( 'wp_ajax_wio_cf_reload_ui', function () {
420
+ $template_file = WRIOP_PLUGIN_DIR . '/admin/pages/parts/folders-table-body.php';
421
+ $folders_table = '';
422
+ if ( is_file( $template_file ) ) {
423
+ ob_start();
424
+ include( $template_file );
425
+ $folders_table = ob_get_contents();
426
+ ob_end_clean();
427
+ }
428
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
429
+ $responce = [
430
+ 'folders_table' => $folders_table,
431
+ 'statistic' => $image_statistics->load(),
432
+ ];
433
+ wp_send_json( $responce );
434
+ } );
libs/addons/admin/ajax/optimization.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Переоптимизация аттачмента
10
+ */
11
+ function wbcr_riop_reoptimizeImage() {
12
+ $image_id = (int) $_POST['id'];
13
+ $backup = WRIOP_Backup::get_instance();
14
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
15
+ $nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
16
+ if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
17
+ echo $nextgen_gallery->getMediaColumnContent( $image_id );
18
+ die();
19
+ }
20
+ wp_suspend_cache_addition( true );
21
+ $default_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
22
+ $level = isset( $_POST['level'] ) ? sanitize_text_field( $_POST['level'] ) : $default_level;
23
+
24
+ $optimized_data = $nextgen_gallery->optimizeNextgenImage( $image_id, $level );
25
+
26
+ if ( $optimized_data && isset( $optimized_data['processing'] ) ) {
27
+ echo 'processing';
28
+ die();
29
+ }
30
+
31
+ echo $nextgen_gallery->getMediaColumnContent( $image_id );
32
+ die();
33
+ }
34
+
35
+ /**
36
+ * Восстановление
37
+ */
38
+ function wbcr_riop_restoreImage() {
39
+ wp_suspend_cache_addition( true );
40
+ $image_id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
41
+
42
+ $nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
43
+ $nextgen_image = $nextgen_gallery->getNextgenImage( $image_id );
44
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
45
+ if ( $nextgen_image->isOptimized() ) {
46
+ $restored = $nextgen_image->restore();
47
+
48
+ if ( ! is_wp_error( $restored ) ) {
49
+ $optimization_data = $nextgen_image->getOptimizationData();
50
+ $optimized_size = $optimization_data->get_final_size();
51
+ $original_size = $optimization_data->get_original_size();
52
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
53
+ $image_statistics->deductFromField( 'original_size', $original_size );
54
+ $image_statistics->save();
55
+ }
56
+ }
57
+
58
+ $nextgen_gallery = WRIO_Nextgen_Gallery::get_instance();
59
+ echo $nextgen_gallery->getMediaColumnContent( $image_id );
60
+ die();
61
+ }
libs/addons/admin/assets/css/custom-folders.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /**
2
+ * Bulk optimization
3
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>
4
+ * @copyright Alex Kovalev 23.08.2017
5
+ */
6
+ /*# sourceMappingURL=custom-folders.css.map */
libs/addons/admin/assets/css/custom-folders.css.map ADDED
@@ -0,0 +1 @@
 
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"custom-folders.css"}
libs/addons/admin/assets/css/custom-folders.less ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Bulk optimization
3
+ * @author Alex Kovalev <alex.kovalevv@gmail.com>
4
+ * @copyright Alex Kovalev 23.08.2017
5
+ */
6
+
7
+ #WBCR {
8
+ // less code
9
+
10
+
11
+ }
{updates → libs/addons/admin/assets/css}/index.php RENAMED
File without changes
libs/addons/admin/assets/css/jquery-file-tree.css ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #wrio-file-tree {
2
+ min-height: 100px;
3
+ max-height: 400px;
4
+ overflow: auto;
5
+ padding: 10px;
6
+ border: 1px solid #888;
7
+ background: #fff url("../img/quick-start-loader.gif") center center no-repeat;
8
+ }
9
+
10
+ div.sp-folder-picker {
11
+ margin: 20px 0; /* 15% from the top and centered */
12
+ border: 1px solid #888;
13
+ max-height: 400px;
14
+ overflow: auto;
15
+ }
16
+
17
+ /*UL.jqueryFileTree LI.directory.selected {
18
+ background-color: #209fd2;
19
+ }*/
20
+
21
+ UL.jqueryFileTree {
22
+ font-family: Verdana, sans-serif;
23
+ font-size: 11px;
24
+ line-height: 18px;
25
+ padding: 0;
26
+ margin: 0;
27
+ display: none;
28
+ background: #fff;
29
+ }
30
+
31
+ UL.jqueryFileTree LI {
32
+ list-style: none;
33
+ text-align: left;
34
+ padding: 0;
35
+ padding-left: 20px;
36
+ margin: 0;
37
+ white-space: nowrap;
38
+ }
39
+
40
+ UL.jqueryFileTree LI.directory {
41
+ background: url(../img/file-tree/directory.png) left top no-repeat;
42
+ }
43
+
44
+ UL.jqueryFileTree LI.directory-locked {
45
+ background: url(../img/file-tree/directory-lock.png) left top no-repeat;
46
+ }
47
+
48
+ UL.jqueryFileTree LI.expanded {
49
+ background: url(../img/file-tree/folder_open.png) left top no-repeat;
50
+ }
51
+
52
+ UL.jqueryFileTree LI.file {
53
+ background: url(../img/file-tree/file.png) left top no-repeat;
54
+ }
55
+
56
+ UL.jqueryFileTree LI.file-locked {
57
+ background: url(../img/file-tree/file-lock.png) left top no-repeat !important;
58
+ }
59
+
60
+ UL.jqueryFileTree LI.wait {
61
+ background: url(../img/file-tree/spinner.gif) left top no-repeat;
62
+ }
63
+
64
+ UL.jqueryFileTree LI.selected > a {
65
+ font-weight: bold;
66
+ }
67
+
68
+ UL.jqueryFileTree LI.ext_3gp {
69
+ background: url(../img/file-tree/film.png) left top no-repeat;
70
+ }
71
+
72
+ UL.jqueryFileTree LI.ext_afp {
73
+ background: url(../img/file-tree/code.png) left top no-repeat;
74
+ }
75
+
76
+ UL.jqueryFileTree LI.ext_afpa {
77
+ background: url(../img/file-tree/code.png) left top no-repeat;
78
+ }
79
+
80
+ UL.jqueryFileTree LI.ext_asp {
81
+ background: url(../img/file-tree/code.png) left top no-repeat;
82
+ }
83
+
84
+ UL.jqueryFileTree LI.ext_aspx {
85
+ background: url(../img/file-tree/code.png) left top no-repeat;
86
+ }
87
+
88
+ UL.jqueryFileTree LI.ext_avi {
89
+ background: url(../img/file-tree/film.png) left top no-repeat;
90
+ }
91
+
92
+ UL.jqueryFileTree LI.ext_bat {
93
+ background: url(../img/file-tree/application.png) left top no-repeat;
94
+ }
95
+
96
+ UL.jqueryFileTree LI.ext_bmp {
97
+ background: url(../img/file-tree/picture.png) left top no-repeat;
98
+ }
99
+
100
+ UL.jqueryFileTree LI.ext_c {
101
+ background: url(../img/file-tree/code.png) left top no-repeat;
102
+ }
103
+
104
+ UL.jqueryFileTree LI.ext_cfm {
105
+ background: url(../img/file-tree/code.png) left top no-repeat;
106
+ }
107
+
108
+ UL.jqueryFileTree LI.ext_cgi {
109
+ background: url(../img/file-tree/code.png) left top no-repeat;
110
+ }
111
+
112
+ UL.jqueryFileTree LI.ext_com {
113
+ background: url(../img/file-tree/application.png) left top no-repeat;
114
+ }
115
+
116
+ UL.jqueryFileTree LI.ext_cpp {
117
+ background: url(../img/file-tree/code.png) left top no-repeat;
118
+ }
119
+
120
+ UL.jqueryFileTree LI.ext_css {
121
+ background: url(../img/file-tree/css.png) left top no-repeat;
122
+ }
123
+
124
+ UL.jqueryFileTree LI.ext_doc {
125
+ background: url(../img/file-tree/doc.png) left top no-repeat;
126
+ }
127
+
128
+ UL.jqueryFileTree LI.ext_exe {
129
+ background: url(../img/file-tree/application.png) left top no-repeat;
130
+ }
131
+
132
+ UL.jqueryFileTree LI.ext_gif {
133
+ background: url(../img/file-tree/picture.png) left top no-repeat;
134
+ }
135
+
136
+ UL.jqueryFileTree LI.ext_fla {
137
+ background: url(../img/file-tree/flash.png) left top no-repeat;
138
+ }
139
+
140
+ UL.jqueryFileTree LI.ext_h {
141
+ background: url(../img/file-tree/code.png) left top no-repeat;
142
+ }
143
+
144
+ UL.jqueryFileTree LI.ext_htm {
145
+ background: url(../img/file-tree/html.png) left top no-repeat;
146
+ }
147
+
148
+ UL.jqueryFileTree LI.ext_html {
149
+ background: url(../img/file-tree/html.png) left top no-repeat;
150
+ }
151
+
152
+ UL.jqueryFileTree LI.ext_jar {
153
+ background: url(../img/file-tree/java.png) left top no-repeat;
154
+ }
155
+
156
+ UL.jqueryFileTree LI.ext_jpg {
157
+ background: url(../img/file-tree/picture.png) left top no-repeat;
158
+ }
159
+
160
+ UL.jqueryFileTree LI.ext_jpeg {
161
+ background: url(../img/file-tree/picture.png) left top no-repeat;
162
+ }
163
+
164
+ UL.jqueryFileTree LI.ext_js {
165
+ background: url(../img/file-tree/script.png) left top no-repeat;
166
+ }
167
+
168
+ UL.jqueryFileTree LI.ext_lasso {
169
+ background: url(../img/file-tree/code.png) left top no-repeat;
170
+ }
171
+
172
+ UL.jqueryFileTree LI.ext_log {
173
+ background: url(../img/file-tree/txt.png) left top no-repeat;
174
+ }
175
+
176
+ UL.jqueryFileTree LI.ext_m4p {
177
+ background: url(../img/file-tree/music.png) left top no-repeat;
178
+ }
179
+
180
+ UL.jqueryFileTree LI.ext_mov {
181
+ background: url(../img/file-tree/film.png) left top no-repeat;
182
+ }
183
+
184
+ UL.jqueryFileTree LI.ext_mp3 {
185
+ background: url(../img/file-tree/music.png) left top no-repeat;
186
+ }
187
+
188
+ UL.jqueryFileTree LI.ext_mp4 {
189
+ background: url(../img/file-tree/film.png) left top no-repeat;
190
+ }
191
+
192
+ UL.jqueryFileTree LI.ext_mpg {
193
+ background: url(../img/file-tree/film.png) left top no-repeat;
194
+ }
195
+
196
+ UL.jqueryFileTree LI.ext_mpeg {
197
+ background: url(../img/file-tree/film.png) left top no-repeat;
198
+ }
199
+
200
+ UL.jqueryFileTree LI.ext_ogg {
201
+ background: url(../img/file-tree/music.png) left top no-repeat;
202
+ }
203
+
204
+ UL.jqueryFileTree LI.ext_ogv {
205
+ background: url(../img/file-tree/film.png) left top no-repeat;
206
+ }
207
+
208
+ UL.jqueryFileTree LI.ext_pcx {
209
+ background: url(../img/file-tree/picture.png) left top no-repeat;
210
+ }
211
+
212
+ UL.jqueryFileTree LI.ext_pdf {
213
+ background: url(../img/file-tree/pdf.png) left top no-repeat;
214
+ }
215
+
216
+ UL.jqueryFileTree LI.ext_php {
217
+ background: url(../img/file-tree/php.png) left top no-repeat;
218
+ }
219
+
220
+ UL.jqueryFileTree LI.ext_png {
221
+ background: url(../img/file-tree/picture.png) left top no-repeat;
222
+ }
223
+
224
+ UL.jqueryFileTree LI.ext_ppt {
225
+ background: url(../img/file-tree/ppt.png) left top no-repeat;
226
+ }
227
+
228
+ UL.jqueryFileTree LI.ext_psd {
229
+ background: url(../img/file-tree/psd.png) left top no-repeat;
230
+ }
231
+
232
+ UL.jqueryFileTree LI.ext_pl {
233
+ background: url(../img/file-tree/script.png) left top no-repeat;
234
+ }
235
+
236
+ UL.jqueryFileTree LI.ext_py {
237
+ background: url(../img/file-tree/script.png) left top no-repeat;
238
+ }
239
+
240
+ UL.jqueryFileTree LI.ext_rb {
241
+ background: url(../img/file-tree/ruby.png) left top no-repeat;
242
+ }
243
+
244
+ UL.jqueryFileTree LI.ext_rbx {
245
+ background: url(../img/file-tree/ruby.png) left top no-repeat;
246
+ }
247
+
248
+ UL.jqueryFileTree LI.ext_rhtml {
249
+ background: url(../img/file-tree/ruby.png) left top no-repeat;
250
+ }
251
+
252
+ UL.jqueryFileTree LI.ext_rpm {
253
+ background: url(../img/file-tree/linux.png) left top no-repeat;
254
+ }
255
+
256
+ UL.jqueryFileTree LI.ext_ruby {
257
+ background: url(../img/file-tree/ruby.png) left top no-repeat;
258
+ }
259
+
260
+ UL.jqueryFileTree LI.ext_sql {
261
+ background: url(../img/file-tree/db.png) left top no-repeat;
262
+ }
263
+
264
+ UL.jqueryFileTree LI.ext_swf {
265
+ background: url(../img/file-tree/flash.png) left top no-repeat;
266
+ }
267
+
268
+ UL.jqueryFileTree LI.ext_tif {
269
+ background: url(../img/file-tree/picture.png) left top no-repeat;
270
+ }
271
+
272
+ UL.jqueryFileTree LI.ext_tiff {
273
+ background: url(../img/file-tree/picture.png) left top no-repeat;
274
+ }
275
+
276
+ UL.jqueryFileTree LI.ext_txt {
277
+ background: url(../img/file-tree/txt.png) left top no-repeat;
278
+ }
279
+
280
+ UL.jqueryFileTree LI.ext_vb {
281
+ background: url(../img/file-tree/code.png) left top no-repeat;
282
+ }
283
+
284
+ UL.jqueryFileTree LI.ext_wav {
285
+ background: url(../img/file-tree/music.png) left top no-repeat;
286
+ }
287
+
288
+ UL.jqueryFileTree LI.ext_webm {
289
+ background: url(../img/file-tree/film.png) left top no-repeat;
290
+ }
291
+
292
+ UL.jqueryFileTree LI.ext_wmv {
293
+ background: url(../img/file-tree/film.png) left top no-repeat;
294
+ }
295
+
296
+ UL.jqueryFileTree LI.ext_xls {
297
+ background: url(../img/file-tree/xls.png) left top no-repeat;
298
+ }
299
+
300
+ UL.jqueryFileTree LI.ext_xml {
301
+ background: url(../img/file-tree/code.png) left top no-repeat;
302
+ }
303
+
304
+ UL.jqueryFileTree LI.ext_zip {
305
+ background: url(../img/file-tree/zip.png) left top no-repeat;
306
+ }
307
+
308
+ UL.jqueryFileTree A {
309
+ color: #333;
310
+ text-decoration: none;
311
+ display: inline-block;
312
+ padding: 0 2px;
313
+ cursor: pointer;
314
+ }
315
+
316
+ UL.jqueryFileTree A:hover {
317
+ background: #BDF;
318
+ }
libs/addons/admin/assets/css/other-media.css ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wriop-files-list #preview {
2
+ width: 80px;
3
+ }
4
+
5
+ .wriop-files-list #file {
6
+ width: 250px;
7
+ }
8
+
9
+ .wriop-files-list #status {
10
+ width: 100px;
11
+ }
12
+
13
+ .wriop-files-list #optimization {
14
+ width: 200px;
15
+ }
16
+
17
+ /* Filter block */
18
+ .wriop-files-list .wp-filter {
19
+ padding: 15px 20px;
20
+ }
21
+
22
+ .wriop-files-list .filter-items select {
23
+ height: auto;
24
+ padding: 6px;
25
+ margin-right: 12px;
26
+ }
27
+
28
+ .wriop-files-list .filter-items .button {
29
+ height: auto;
30
+ padding: 2px 12px 3px;
31
+ }
32
+
33
+ /* Empty table */
34
+ .wriop-files-list .no-items td {
35
+ padding: 35px;
36
+ text-align: center;
37
+ font-size: 18px;
38
+ }
39
+
40
+ .wriop-files-list .no-items td a {
41
+ text-decoration: underline;
42
+ }
43
+
44
+ /* Th sortable */
45
+ .wriop-files-list .sortable a {
46
+ color: #000;
47
+ }
48
+
49
+ /* Global links */
50
+ .wriop-files-list a {
51
+ color: #3694AE;
52
+ }
53
+
54
+ /* Global TDs */
55
+ .wriop-files-list tbody td,
56
+ .wriop-files-list tbody th,
57
+ .wriop-files-list.wriop-files-list tbody .check-column {
58
+ vertical-align: top;
59
+ text-align: left;
60
+ /*padding-top: 20px;
61
+ padding-bottom: 20px;*/
62
+ color: #626E7B;
63
+ }
64
+
65
+ .wriop-files-list .column-file,
66
+ .wriop-files-list .column-folder,
67
+ .wriop-files-list .column-status {
68
+ padding-top: 28px;
69
+ }
70
+
71
+ /* Col Title */
72
+ .wriop-files-list .column-preview strong  {
73
+ font-weight: normal;
74
+ font-size: 14px;
75
+ }
76
+
77
+ .wriop-files-list .column-preview strong a {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ word-break: break-all;
81
+ word-wrap: break-word;
82
+ font-weight: normal;
83
+ }
84
+
85
+ .wriop-files-list .filename {
86
+ font-size: 12px;
87
+ font-weight: bold;
88
+ }
89
+
90
+ .wriop-files-list .media-icon {
91
+ position: relative;
92
+ width: 60px;
93
+ overflow: hidden;
94
+ flex-shrink: 0;
95
+ }
96
+
97
+ .media-icon .centered {
98
+ position: absolute;
99
+ left: 0;
100
+ top: 0;
101
+ width: 100%;
102
+ height: 100%;
103
+ transform: translate(50%, 50%);
104
+ }
105
+
106
+ .media-icon .centered img {
107
+ position: absolute;
108
+ left: 0;
109
+ top: 0;
110
+ transform: translate(-50%, -50%);
111
+ }
112
+
113
+ table.media .column-preview .media-icon.landscape img {
114
+ max-width: none;
115
+ width: auto;
116
+ height: 60px;
117
+ }
118
+
119
+ table.media .column-preview .media-icon.portrait img {
120
+ width: 60px;
121
+ }
122
+
123
+ /* Optimization datas Col */
124
+ .wriop-files-list ul.wriop-datas-list {
125
+ font-size: 11px;
126
+ }
127
+
128
+ .wriop-files-list ul.wriop-datas-list .big {
129
+ font-size: 13px;
130
+ }
131
+
132
+ .wriop-files-list ul.wriop-datas-list span.wriopy-chart-value {
133
+ font-size: 12px;
134
+ }
135
+
136
+ .wriop-files-list ul.wriop-datas-list .wriop-chart-container {
137
+ margin-right: 2px;
138
+ }
139
+
140
+ .wriop-files-list ul.wriop-datas-list canvas {
141
+ width: 18px !important;
142
+ height: 18px !important;
143
+ }
144
+
145
+ /* Optimization Level Col */
146
+ .wriop-files-list .optimization_level {
147
+ text-align: center;
148
+ font-weight: bold;
149
+ font-size: 14px;
150
+ text-transform: uppercase;
151
+ letter-spacing: 0.02em;
152
+ }
153
+
154
+ .wriopy-files-list .column-optimization_level,
155
+ .wriop-files-list .column-optimization_level a {
156
+ text-align: center;
157
+ }
158
+
159
+ .wriop-files-list .column-optimization_level a span {
160
+ float: none;
161
+ display: inline-block;
162
+ vertical-align: middle;
163
+ }
164
+
165
+ .wriop-files-list .column-optimization_level .sorting-indicator {
166
+ vertical-align: -10px;
167
+ }
168
+
169
+ /* Actions col */
170
+ .wriop-files-list .column-actions .button,
171
+ .wriop-files-list .column-actions .button-primary {
172
+ padding: 5px 20px;
173
+ font-size: 14px;
174
+ height: auto;
175
+ }
176
+
177
+ .wriop-files-list .column-actions .button-primary {
178
+ background: #3694AE;
179
+ color: #FFF;
180
+ border: 0;
181
+ box-shadow: none;
182
+ text-shadow: none;
183
+ }
184
+
185
+ .wriop-files-list .column-actions a,
186
+ .status a.button-wriop-refresh-status {
187
+ display: inline-block;
188
+ margin: .3em 0;
189
+ font-size: 12px;
190
+ font-weight: bold;
191
+ }
192
+
193
+ .wriop-files-list .wriop-status-already_optimized {
194
+ font-weight: bold;
195
+ color: #8BC34A;
196
+ }
197
+
198
+ .wriop-files-list .column-actions a .dashicons,
199
+ .wriop-files-list .column-actions a .dashicons:before,
200
+ .status a.button-wriop-refresh-status .dashicons,
201
+ .status a.button-wriop-refresh-status .dashicons:before {
202
+ margin-right: 2px;
203
+ font-size: 17px;
204
+ height: 17px;
205
+ width: 17px;
206
+ }
207
+
208
+ .wriop-files-list .wio-reoptimize {
209
+ margin-top: 15px;
210
+ }
libs/addons/admin/assets/img/file-tree/application.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/code.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/css.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/db.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/directory-lock.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/directory.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/doc.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/file-lock.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/file.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/film.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/flash.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/folder_open.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/html.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/java.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/linux.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/music.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/pdf.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/php.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/picture.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/ppt.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/psd.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/ruby.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/script.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/spinner.gif ADDED
Binary file
libs/addons/admin/assets/img/file-tree/txt.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/xls.png ADDED
Binary file
libs/addons/admin/assets/img/file-tree/zip.png ADDED
Binary file
libs/addons/admin/assets/img/quick-start-loader.gif ADDED
Binary file
libs/addons/admin/assets/index.php ADDED
File without changes
libs/addons/admin/assets/js/custom-folders.js ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * General
3
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
4
+ * @copyright (c) 10.09.2017, Webcraftic
5
+ * @version 1.0
6
+ */
7
+ (function($) {
8
+
9
+ var customFolders = {
10
+
11
+ selectedFolder: null,
12
+
13
+ init: function() {
14
+ if( wrio_l18n_bulk_page === undefined || wrio_settings_bulk_page === undefined ) {
15
+ console.log('[Error]: Required global variables are not declared.');
16
+ return;
17
+ }
18
+
19
+ this.i18n = wrio_l18n_bulk_page;
20
+ this.settings = wrio_settings_bulk_page;
21
+ this.startOptButton = $('#wrio-start-optimization');
22
+
23
+ this.registerEvents();
24
+ },
25
+
26
+ registerEvents: function() {
27
+ var self = this;
28
+
29
+ $("#wrio-add-new-folder").on('click', function() {
30
+ swal({
31
+ title: self.i18n.modal_cf_title,
32
+ html: $('#wrio-tmpl-select-custom-folders').html(),
33
+ type: '',
34
+ customClass: 'wrio-modal wrio-modal-optimization-way',
35
+ showCancelButton: true,
36
+ showCloseButton: true,
37
+ padding: 0,
38
+ width: 654,
39
+ confirmButtonText: self.i18n.button_select,
40
+ cancelButtonText: self.i18n.button_cancel,
41
+ reverseButtons: false,
42
+ onOpen: function(modal) {
43
+
44
+ $(modal).find("#wrio-file-tree").fileTree({
45
+ script: ajaxurl + '?action=wriop_browse_dir',
46
+ multiFolder: false,
47
+ onlyFolders: true
48
+ });
49
+
50
+ $(document).on('click', '#wrio-file-tree .directory > a', function() {
51
+ var subPath = $(this).attr("rel").trim();
52
+ if( subPath ) {
53
+ var fullPath = subPath;
54
+ if( fullPath.slice(-1) === '/' ) {
55
+ fullPath = fullPath.slice(0, -1);
56
+ }
57
+ $("#wbcr-rio-selected-path").val(fullPath);
58
+ self.selectedFolder = fullPath;
59
+ }
60
+ });
61
+
62
+ }
63
+ }).then(function() {
64
+ self.addNewFolder();
65
+ }).catch(swal.noop);
66
+
67
+ return false;
68
+ });
69
+ },
70
+
71
+ addNewFolder: function() {
72
+ var self = this;
73
+
74
+ var data = {
75
+ action: 'wrio-add-custom-folder',
76
+ path: self.selectedFolder,
77
+ _wpnonce: self.settings.nonce,
78
+ };
79
+
80
+ $.post(ajaxurl, data, function(response) {
81
+ if( !response || !response.success ) {
82
+ console.log('[Error]: Failed ajax request (Add new folder).');
83
+ console.log(data);
84
+ console.log(response);
85
+
86
+ if( response.data && response.data.error_message ) {
87
+ // todo: так как фреймворк не используется в аддоне, нужно доработать этот кусок кода. Он не
88
+ // может быть скомпилирован.
89
+ var noticeId = $.wbcr_factory_clearfy_208.app.showNotice(response.data.error_message, 'danger');
90
+
91
+ setTimeout(function() {
92
+ $.wbcr_factory_clearfy_208.app.hideNotice(noticeId);
93
+ }, 5000);
94
+ }
95
+ return;
96
+ }
97
+
98
+ var tr = $('<tr>'),
99
+ td = $('<td>'),
100
+ path = $('<span>'),
101
+ button = $('<button>'),
102
+ compressed_msg;
103
+
104
+ path.addClass('wrio-table-highlighter').text(response.data.path);
105
+
106
+ button.addClass('wbcr-rio-remove-folder')
107
+ .addClass('btn')
108
+ .addClass('btn-default')
109
+ .attr('data-confirm', self.i18n.alert_remove_folder)
110
+ .html(' <span class="dashicons dashicons-no"></span>');
111
+
112
+ compressed_msg = self.i18n.compressed_in_folder.replace('%d', 0);
113
+ compressed_msg = compressed_msg.replace('%s', '<span id="wrio-cf-total-' + response.data.uid + '">0</span>');
114
+
115
+ tr.addClass('wrio-table-item')
116
+ .append(td.clone().addClass('wrio-table-spinner'))
117
+ .append(td.clone().html(compressed_msg))
118
+ .append(td.clone().append(path))
119
+ .append(td.clone().attr('data-uid', response.data.uid).append(button));
120
+
121
+ $('.wbcr-rio-folders-table tbody').append(tr);
122
+
123
+ self.scanFolder({
124
+ action: 'wrio-scan-folder',
125
+ uid: response.data.uid,
126
+ total: 0,
127
+ offset: 0,
128
+ _wpnonce: self.settings.nonce,
129
+ }, function(result) {
130
+
131
+ tr.find('td').eq(0).removeClass('wrio-table-spinner');
132
+ tr.find('td').eq(1).find('span').text(result.total);
133
+
134
+ reload_ui(); // обновляем интерфейс
135
+ });
136
+ });
137
+ },
138
+
139
+ scanFolder: function(data, callback) {
140
+ var self = this;
141
+
142
+ $.post(ajaxurl, data, function(response) {
143
+ console.log(response);
144
+
145
+ if( !response || !response.success ) {
146
+ console.log('[Error]: Failed ajax request (Scan folder).');
147
+ console.log(data);
148
+ console.log(response);
149
+
150
+ if( response.data && response.data.error_message ) {
151
+ // todo: так как фреймворк не используется в аддоне, нужно доработать этот кусок кода. Он не
152
+ // может быть скомпилирован.
153
+ var noticeId = $.wbcr_factory_clearfy_208.app.showNotice(response.data.error_message, 'danger');
154
+
155
+ setTimeout(function() {
156
+ $.wbcr_factory_clearfy_208.app.hideNotice(noticeId);
157
+ }, 5000);
158
+ }
159
+ return;
160
+ }
161
+
162
+ data.total = response.data.total;
163
+ data.offset = response.data.offset;
164
+
165
+ $('#wrio-cf-total-' + data.uid).text(data.offset);
166
+
167
+ if( response.data.complete ) {
168
+ callback && callback(response.data);
169
+ } else {
170
+ self.scanFolder(data, callback);
171
+ }
172
+ }).fail(function(xhr, status, error) {
173
+ // error handling
174
+ });
175
+ }
176
+ };
177
+
178
+ $(document).ready(function() {
179
+ customFolders.init();
180
+ });
181
+
182
+ $(document).on('click', '.wbcr-rio-scan-folder', function() {
183
+ $('#wbcr-rio-popup').empty();
184
+ tb_show('Sync Folder', '#TB_inline?&width=500&height=200&inlineId=wbcr-rio-popup');
185
+ $('#TB_ajaxContent').html('Loading...');
186
+ var data = {
187
+ action: 'wio_cf_get_template_part',
188
+ template: 'sync_folder'
189
+ };
190
+ var self = $(this);
191
+ $.post(ajaxurl, data, function(response) {
192
+ $('#TB_ajaxContent').html(response);
193
+ var ai_data = {
194
+ 'action': 'wriop_folder_sync_index',
195
+ 'uid': self.closest('td').data('uid'),
196
+ 'total': 0,
197
+ 'offset': 0,
198
+ };
199
+ send_indexing_data(ai_data);
200
+ });
201
+ });
202
+
203
+ $('.wbcr-rio-scan-all-folders').on('click', function() {
204
+ $('#wbcr-rio-popup').empty();
205
+ tb_show('Sync All Folders', '#TB_inline?&width=500&height=200&inlineId=wbcr-rio-popup');
206
+ $('#TB_ajaxContent').html('Loading...');
207
+ var data = {
208
+ action: 'wio_cf_get_template_part',
209
+ template: 'sync_all_folders'
210
+ };
211
+ var self = $(this);
212
+ $.post(ajaxurl, data, function(response) {
213
+ $('#TB_ajaxContent').html(response);
214
+
215
+ });
216
+ });
217
+
218
+ $(document).on('click', '.wbcr-rio-optimize-folder', function() {
219
+ $('#wbcr-rio-popup').empty();
220
+ tb_show('Optimize Folder', '#TB_inline?&width=500&height=200&inlineId=wbcr-rio-popup');
221
+ $('#TB_ajaxContent').html('Loading...');
222
+ var data = {
223
+ action: 'wio_cf_get_template_part',
224
+ template: 'optimize_folder'
225
+ };
226
+ var self = $(this);
227
+ $.post(ajaxurl, data, function(response) {
228
+ $('#TB_ajaxContent').html(response);
229
+ var ai_data = {
230
+ 'uid': self.closest('td').data('uid'),
231
+ 'action': 'wriop_process_cf_folder_images',
232
+ '_wpnonce': $('#wio-iph-nonce').val()
233
+ };
234
+ send_optimize_post_data(ai_data);
235
+ });
236
+ });
237
+
238
+ $(document).on('click', '.wbcr-rio-restore-folder', function() {
239
+ if( !confirm($(this).data('confirm')) ) {
240
+ return false;
241
+ }
242
+ $('#wbcr-rio-popup').empty();
243
+ tb_show('Restore Folder', '#TB_inline?&width=500&height=200&inlineId=wbcr-rio-popup');
244
+ $('#TB_ajaxContent').html('Loading...');
245
+ var data = {
246
+ action: 'wio_cf_get_template_part',
247
+ template: 'restore_folder'
248
+ };
249
+ var self = $(this);
250
+ $.post(ajaxurl, data, function(response) {
251
+ $('#TB_ajaxContent').html(response);
252
+ var ai_data = {
253
+ 'total': '?',
254
+ 'uid': self.closest('td').data('uid'),
255
+ 'action': 'wio_cf_restore_backup',
256
+ '_wpnonce': $('#wio-iph-nonce').val()
257
+ };
258
+ send_backup_post_data(ai_data);
259
+ });
260
+ return false;
261
+ });
262
+
263
+ $(document).on('click', '.wbcr-rio-remove-folder', function() {
264
+ if( !confirm($(this).data('confirm')) ) {
265
+ return false;
266
+ }
267
+ var data = {
268
+ action: 'wriop_remove_folder',
269
+ uid: $(this).closest('td').data('uid')
270
+ };
271
+ $(this).closest('tr').remove();
272
+ $.post(ajaxurl, data, function(response) {
273
+ reload_ui(); // обновляем интерфейс
274
+ });
275
+ });
276
+
277
+ function reload_ui() {
278
+ var data = {
279
+ action: 'wio_cf_reload_ui',
280
+ };
281
+ $.post(ajaxurl, data, function(response) {
282
+ if( response.folders_table ) {
283
+ $('.wbcr-rio-folders-table tbody').html(response.folders_table);
284
+ }
285
+ if( response.statistic ) {
286
+ redraw_statistics(response.statistic);
287
+ }
288
+ });
289
+ }
290
+
291
+ function redraw_statistics(statistic) {
292
+ $('#wio-main-chart').attr('data-unoptimized', statistic.unoptimized)
293
+ .attr('data-optimized', statistic.optimized)
294
+ .attr('data-errors', statistic.error);
295
+ $('#wio-total-optimized-attachments').text(statistic.optimized); // optimized
296
+ $('#wio-original-size').text(bytesToSize(statistic.original_size));
297
+ $('#wio-optimized-size').text(bytesToSize(statistic.optimized_size));
298
+ $('#wio-total-optimized-attachments-pct').text(statistic.save_size_percent + '%');
299
+ $('#wio-overview-chart-percent').html(statistic.optimized_percent + '<span>%</span>');
300
+ $('.wio-total-percent').text(statistic.optimized_percent + '%');
301
+ $('#wio-optimized-bar').css('width', statistic.percent_line + '%');
302
+
303
+ $('#wio-unoptimized-num').text(statistic.unoptimized);
304
+ $('#wio-optimized-num').text(statistic.optimized);
305
+ $('#wio-error-num').text(statistic.error);
306
+
307
+ window.wio_chart.data.datasets[0].data[0] = statistic.unoptimized; // unoptimized
308
+ window.wio_chart.data.datasets[0].data[1] = statistic.optimized; // optimized
309
+ window.wio_chart.data.datasets[0].data[2] = statistic.error; // errors
310
+ window.wio_chart.update();
311
+ }
312
+
313
+ function bytesToSize(bytes) {
314
+ var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
315
+ if( bytes == 0 ) {
316
+ return '0 Byte';
317
+ }
318
+ var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
319
+ if( i == 0 ) {
320
+ return bytes + ' ' + sizes[i];
321
+ }
322
+ return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];
323
+ }
324
+
325
+ function send_backup_post_data(data) {
326
+ $.post(ajaxurl, data, function(response) {
327
+ if( !response.end ) {
328
+ data.total = response.total;
329
+ send_backup_post_data(data);
330
+ $('#wio-restore-backup-progress').find('.progress-bar').css('width', response.percent + '%');
331
+ } else {
332
+ $('#wio-restore-backup-progress').find('.progress-bar').css('width', '100%');
333
+ $('#wio-restore-backup-success-msg').show();
334
+ $('#wio-restore-backup-progress-msg').hide();
335
+ reload_ui(); // обновляем интерфейс
336
+ }
337
+ });
338
+ }
339
+
340
+ function send_optimize_post_data(data) {
341
+ $.post(ajaxurl, data, function(response) {
342
+ if( !response.end ) {
343
+ send_optimize_post_data(data);
344
+ $('#wio-optimize-progress').find('.progress-bar').css('width', response.statistic.optimized_percent + '%');
345
+ } else {
346
+ $('#wio-optimize-progress').find('.progress-bar').css('width', '100%');
347
+ $('#wio-optimize-success-msg').show();
348
+ $('#wio-optimize-progress-msg').hide();
349
+ reload_ui(); // обновляем интерфейс
350
+ }
351
+ });
352
+ }
353
+
354
+ function send_indexing_data(data) {
355
+ $.post(ajaxurl, data, function(response) {
356
+ data.total = response.total;
357
+ data.offset = response.offset;
358
+ if( response.complete ) {
359
+ $('#wio-sync-progress').find('.progress-bar').css('width', '50%');
360
+ data.action = 'wriop_folder_indexing';
361
+ data.offset = 0;
362
+ data.total = 0;
363
+ send_check_index_data(data);
364
+ } else {
365
+ send_indexing_data(data);
366
+ $('#wio-sync-progress').find('.progress-bar').css('width', response.percent + '%');
367
+ }
368
+ });
369
+ }
370
+
371
+ function send_check_index_data(data) {
372
+ $.post(ajaxurl, data, function(response) {
373
+ data.total = response.total;
374
+ data.offset = response.offset;
375
+ if( response.complete ) {
376
+ $('#wio-sync-progress').find('.progress-bar').css('width', '100%');
377
+ $('#wio-sync-success-msg').show();
378
+ $('#wio-sync-progress-msg').hide();
379
+ reload_ui(); // обновляем интерфейс
380
+ } else {
381
+ send_check_index_data(data);
382
+ $('#wio-sync-progress').find('.progress-bar').css('width', response.percent + '%');
383
+ }
384
+ });
385
+ }
386
+
387
+ function first_indexing(data) {
388
+ $.post(ajaxurl, data, function(response) {
389
+ data.total = response.total;
390
+ data.offset = response.offset;
391
+ $('.wbcr-rio-indexing-counter').text(data.offset);
392
+ if( response.complete ) {
393
+ $("#wbcr-rio-add-folder").show();
394
+ $('#wbcr-rio-indexing-text').hide();
395
+ $('#wbcr-rio-indexing-finish-text').show();
396
+ reload_ui(); // обновляем интерфейс
397
+ } else {
398
+ first_indexing(data);
399
+ }
400
+ });
401
+ }
402
+
403
+ })(jQuery);
libs/addons/admin/assets/js/general.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * General
3
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
4
+ * @copyright (c) 10.09.2017, Webcraftic
5
+ * @version 1.0
6
+ */
7
+
8
+
9
+ (function($) {
10
+ 'use strict';
11
+
12
+ // less code
13
+
14
+ })(jQuery);
libs/addons/admin/assets/js/index.php ADDED
File without changes
libs/addons/admin/assets/js/jquery-file-tree.js ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /*
3
+ * jQueryFileTree Plugin
4
+ *
5
+ * @author - Cory S.N. LaViska - A Beautiful Site (http://abeautifulsite.net/) - 24 March 2008
6
+ * @author - Dave Rogers - (https://github.com/daverogers/)
7
+ *
8
+ * Usage: $('.fileTreeDemo').fileTree({ options }, callback )
9
+ *
10
+ * TERMS OF USE
11
+ *
12
+ * This plugin is dual-licensed under the GNU General Public License and the MIT License and
13
+ * is copyright 2008 A Beautiful Site, LLC.
14
+ */
15
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
16
+
17
+ (function($, window) {
18
+ var FileTree;
19
+ FileTree = (function() {
20
+ function FileTree(el, args, callback) {
21
+ this.onEvent = bind(this.onEvent, this);
22
+ var $el, _this, defaults;
23
+ $el = $(el);
24
+ _this = this;
25
+ defaults = {
26
+ root: '/',
27
+ script: '/files/filetree',
28
+ folderEvent: 'click',
29
+ expandSpeed: 500,
30
+ collapseSpeed: 500,
31
+ expandEasing: 'swing',
32
+ collapseEasing: 'swing',
33
+ multiFolder: true,
34
+ loadMessage: 'Loading...',
35
+ errorMessage: 'Unable to get file tree information',
36
+ multiSelect: false,
37
+ onlyFolders: false,
38
+ onlyFiles: false,
39
+ preventLinkAction: false
40
+ };
41
+ this.jqft = {
42
+ container: $el
43
+ };
44
+ this.options = $.extend(defaults, args);
45
+ this.callback = callback;
46
+ this.data = {};
47
+ $el.html('<ul class="jqueryFileTree start"><li class="wait">' + this.options.loadMessage + '<li></ul>');
48
+ _this.showTree($el, escape(this.options.root), function() {
49
+ return _this._trigger('filetreeinitiated', {});
50
+ });
51
+ $el.delegate("li a", this.options.folderEvent, _this.onEvent);
52
+ }
53
+
54
+ FileTree.prototype.onEvent = function(event) {
55
+ var $ev, _this, callback, jqft, options, ref;
56
+ $ev = $(event.target);
57
+ options = this.options;
58
+ jqft = this.jqft;
59
+ _this = this;
60
+ callback = this.callback;
61
+ _this.data = {};
62
+ _this.data.li = $ev.closest('li');
63
+ _this.data.type = (ref = _this.data.li.hasClass('directory')) != null ? ref : {
64
+ 'directory': 'file'
65
+ };
66
+ _this.data.value = $ev.text();
67
+ _this.data.rel = $ev.prop('rel');
68
+ _this.data.container = jqft.container;
69
+ if (options.preventLinkAction) {
70
+ event.preventDefault();
71
+ }
72
+ if ($ev.parent().hasClass('directory')) {
73
+ _this.jqft.container.find('LI.directory').removeClass('selected');
74
+ $ev.parent().addClass('selected');
75
+ if ($ev.parent().hasClass('collapsed')) {
76
+ if (!options.multiFolder) {
77
+ $ev.parent().parent().find('UL').slideUp({
78
+ duration: options.collapseSpeed,
79
+ easing: options.collapseEasing
80
+ });
81
+ $ev.parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
82
+ }
83
+ $ev.parent().removeClass('collapsed').addClass('expanded');
84
+ $ev.parent().find('UL').remove();
85
+ return _this.showTree($ev.parent(), $ev.attr('rel'), function() {
86
+ _this._trigger('filetreeexpanded', _this.data);
87
+ return callback != null;
88
+ });
89
+ } else {
90
+ return $ev.parent().find('UL').slideUp({
91
+ duration: options.collapseSpeed,
92
+ easing: options.collapseEasing,
93
+ start: function() {
94
+ return _this._trigger('filetreecollapse', _this.data);
95
+ },
96
+ complete: function() {
97
+ $ev.parent().removeClass('expanded').addClass('collapsed');
98
+ _this._trigger('filetreecollapsed', _this.data);
99
+ return callback != null;
100
+ }
101
+ });
102
+ }
103
+ } else {
104
+ if (!options.multiSelect) {
105
+ jqft.container.find('li').removeClass('selected');
106
+ $ev.parent().addClass('selected');
107
+ } else {
108
+ if ($ev.parent().find('input').is(':checked')) {
109
+ $ev.parent().find('input').prop('checked', false);
110
+ $ev.parent().removeClass('selected');
111
+ } else {
112
+ $ev.parent().find('input').prop('checked', true);
113
+ $ev.parent().addClass('selected');
114
+ }
115
+ }
116
+ _this._trigger('filetreeclicked', _this.data);
117
+ return typeof callback === "function" ? callback($ev.attr('rel')) : void 0;
118
+ }
119
+ };
120
+
121
+ FileTree.prototype.showTree = function(el, dir, finishCallback) {
122
+ var $el, _this, data, handleFail, handleResult, options, result;
123
+ $el = $(el);
124
+ options = this.options;
125
+ _this = this;
126
+ $el.addClass('wait');
127
+ $(".jqueryFileTree.start").remove();
128
+ data = {
129
+ dir: dir,
130
+ onlyFolders: options.onlyFolders,
131
+ onlyFiles: options.onlyFiles,
132
+ multiSelect: options.multiSelect
133
+ };
134
+ handleResult = function(result) {
135
+ var li;
136
+ $el.find('.start').html('');
137
+ $el.removeClass('wait').append(result);
138
+ if (options.root === dir) {
139
+ $el.find('UL:hidden').show(typeof callback !== "undefined" && callback !== null);
140
+ } else {
141
+ if (jQuery.easing[options.expandEasing] === void 0) {
142
+ console.log('Easing library not loaded. Include jQueryUI or 3rd party lib.');
143
+ options.expandEasing = 'swing';
144
+ }
145
+ $el.find('UL:hidden').slideDown({
146
+ duration: options.expandSpeed,
147
+ easing: options.expandEasing,
148
+ start: function() {
149
+ return _this._trigger('filetreeexpand', _this.data);
150
+ },
151
+ complete: finishCallback
152
+ });
153
+ }
154
+ li = $('[rel="' + decodeURIComponent(dir) + '"]').parent();
155
+ if (options.multiSelect && li.children('input').is(':checked')) {
156
+ li.find('ul li input').each(function() {
157
+ $(this).prop('checked', true);
158
+ return $(this).parent().addClass('selected');
159
+ });
160
+ }
161
+ return false;
162
+ };
163
+ handleFail = function() {
164
+ $el.find('.start').html('');
165
+ $el.removeClass('wait').append("<p>" + options.errorMessage + "</p>");
166
+ return false;
167
+ };
168
+ if (typeof options.script === 'function') {
169
+ result = options.script(data);
170
+ if (typeof result === 'string' || result instanceof jQuery) {
171
+ return handleResult(result);
172
+ } else {
173
+ return handleFail();
174
+ }
175
+ } else {
176
+ return $.ajax({
177
+ url: options.script,
178
+ type: 'POST',
179
+ dataType: 'HTML',
180
+ data: data
181
+ }).done(function(result) {
182
+ return handleResult(result);
183
+ }).fail(function() {
184
+ return handleFail();
185
+ });
186
+ }
187
+ };
188
+
189
+ FileTree.prototype._trigger = function(eventType, data) {
190
+ var $el;
191
+ $el = this.jqft.container;
192
+ return $el.triggerHandler(eventType, data);
193
+ };
194
+
195
+ return FileTree;
196
+
197
+ })();
198
+ return $.fn.extend({
199
+ fileTree: function(args, callback) {
200
+ return this.each(function() {
201
+ var $this, data;
202
+ $this = $(this);
203
+ data = $this.data('fileTree');
204
+ if (!data) {
205
+ $this.data('fileTree', (data = new FileTree(this, args, callback)));
206
+ }
207
+ if (typeof args === 'string') {
208
+ return data[option].apply(data);
209
+ }
210
+ });
211
+ }
212
+ });
213
+ })(window.jQuery, window);
libs/addons/admin/boot.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Обычно в этом файле размещает код, который отвечает за уведомление, совместимость с другими плагинами,
4
+ * незначительные функции, которые должны быть выполнены на всех страницах админ панели.
5
+ *
6
+ * В этом файле должен быть размещен код, которые относится только к области администрирования.
7
+ *
8
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
9
+ * @copyright Webcraftic 19.09.2018
10
+ * @version 1.0
11
+ */
12
+
13
+ // Exit if accessed directly
14
+ use WRIO\WEBP\HTML\Delivery;
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+ /**
20
+ * Flush configuration after saving the settings
21
+ *
22
+ * @param WHM_Plugin $plugin
23
+ * @param Wbcr_FactoryPages412_ImpressiveThemplate $page
24
+ *
25
+ * @return bool
26
+ */
27
+ add_action( 'wbcr/factory/pages/impressive/after_form_save', function ( $plugin, $page ) {
28
+ $is_rio_plugin = WRIO_Plugin::app()->getPluginName() == $plugin->getPluginName();
29
+ $is_settings_page = "rio_settings" == $page->id;
30
+ $is_apache = WRIO\WEBP\Server::is_apache();
31
+ $is_use_htaccess = WRIO\WEBP\Server::server_use_htaccess();
32
+
33
+ if ( $is_rio_plugin && $is_settings_page ) {
34
+ if ( WRIO\WEBP\HTML\Delivery::is_webp_enabled() && WRIO\WEBP\HTML\Delivery::is_redirect_delivery_mode() ) {
35
+ if ( $is_apache && $is_use_htaccess ) {
36
+ WRIO\WEBP\Server::htaccess_update_webp_rules();
37
+ }
38
+
39
+ return;
40
+ }
41
+
42
+ if ( $is_apache && $is_use_htaccess ) {
43
+ WRIO\WEBP\Server::htaccess_clear_webp_rules();
44
+ }
45
+ }
46
+ }, 10, 2 );
47
+
48
+
libs/addons/admin/filters/backup.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Used to process different filters.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
6
+ * @copyright (c) 2018 Webraftic Ltd
7
+ * @version 1.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Processing restore back-up of custom folder and Nextgen.
17
+ *
18
+ * @return array {
19
+ * Processing result.
20
+ * @type int $remane Count of remained images to be processed.
21
+ * @type int $total Count of total processed items.
22
+ * }
23
+ * @since 1.0.4
24
+ */
25
+ add_filter( 'wbcr/rio/backup/restore_filter', function ( $limit ) {
26
+
27
+ $remane_count = 0;
28
+ $total = 0;
29
+
30
+ $premium_backup = WRIOP_Backup::get_instance();
31
+
32
+ if ( wrio_is_active_nextgen_gallery() ) {
33
+ /* NextGen*/
34
+ $nextgen_restored = $premium_backup->restoreAllNextGen( $limit );
35
+
36
+ if ( isset( $nextgen_restored['remane'] ) ) {
37
+ $remane_count += $nextgen_restored['remane'];
38
+ }
39
+
40
+ $nextgen_count = RIO_Process_Queue::count_by_type_status( 'nextgen', 'success' );
41
+
42
+ if ( is_numeric( $nextgen_count ) && (int) $nextgen_count > 0 ) {
43
+ $total += $nextgen_count;
44
+ }
45
+
46
+ unset( $nextgen_count );
47
+ }
48
+
49
+ /* Custom folders */
50
+ $cf_restored = $premium_backup->restoreAllCustomFolders( $limit );
51
+
52
+ if ( isset( $cf_restored['remane'] ) ) {
53
+ $remane_count += $cf_restored['remane'];
54
+ }
55
+
56
+ $cf_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'success' );
57
+
58
+ if ( is_numeric( $cf_count ) && (int) $cf_count > 0 ) {
59
+
60
+ $total += $cf_count;
61
+ }
62
+
63
+ unset( $cf_count );
64
+
65
+ return [
66
+ 'remane' => $remane_count,
67
+ 'total' => $total,
68
+ ];
69
+ } );
libs/addons/admin/filters/settings-page.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Adds hooks to the main plugin settings page.
4
+ *
5
+ * @author Webcraftic <wordpress.webraftic@gmail.com>, Alexander Kovalev <alex.kovalevv@gmail.com>
6
+ * @copyright (c) 22.04.2019, Webcraftic
7
+ * @version 1.0
8
+ */
9
+
10
+ use WRIO\WEBP\HTML\Delivery as WEBP_Delivery;
11
+ use WRIO\WEBP\Server;
12
+
13
+ /**
14
+ * Used to save webp options.
15
+ *
16
+ * @since 1.0.4
17
+ */
18
+ add_action( "wrio/settings_page/berfore_form_save", function () {
19
+ $is_webp_enabled = (int) WRIO_Plugin::app()->request->post( WRIO_Plugin::app()->getPrefix() . 'convert_webp_format' );
20
+
21
+ if ( ! $is_webp_enabled ) {
22
+ return;
23
+ }
24
+
25
+ $allow_redirection_mode = Server::is_apache() && Server::server_use_htaccess();
26
+ $delivery_mode = WRIO_Plugin::app()->request->post( 'wrio_webp_delivery_mode', WEBP_Delivery::DEFAULT_DELIVERY_MODE );
27
+
28
+ if ( WEBP_Delivery::REDIRECT_DELIVERY_MODE == $delivery_mode && ! $allow_redirection_mode ) {
29
+ $delivery_mode = WEBP_Delivery::DEFAULT_DELIVERY_MODE;
30
+ }
31
+
32
+ WRIO_Plugin::app()->updatePopulateOption( 'webp_delivery_mode', $delivery_mode );
33
+ } );
34
+
35
+ /**
36
+ * This hook prints options for delivering webp images.
37
+ *
38
+ * @since 1.0.4
39
+ */
40
+ add_action( "wrio/settings_page/conver_webp_options", function () {
41
+ $allow_redirection_mode = Server::is_apache() && Server::server_use_htaccess();
42
+ $delivery_mode = WRIO_Plugin::app()->getPopulateOption( 'webp_delivery_mode', WEBP_Delivery::DEFAULT_DELIVERY_MODE );
43
+
44
+ $server = 'apache';
45
+
46
+ if ( Server::is_apache() ) {
47
+ $server = 'apache';
48
+ } else if ( Server::is_nginx() ) {
49
+ $server = 'nginx';
50
+ } else if ( Server::is_iss() ) {
51
+ $server = 'iss';
52
+ }
53
+
54
+ // Help
55
+ //http://robin-image-optimizer.webcraftic.com/what-is-webp-format-and-how-webp-images-can-speed-up-your-wordpress-website/
56
+ $docs_url = WRIO_Plugin::app()->get_support()->get_tracking_page_url( 'what-is-webp-format-and-how-webp-images-can-speed-up-your-wordpress-website', 'settings-page' );
57
+
58
+ $view = \WRIO_Views::get_instance( WRIOP_PLUGIN_DIR );
59
+
60
+ $view->print_template( "part-settings-page-webp-options", [
61
+ 'server' => $server,
62
+ 'delivery_mode' => $delivery_mode,
63
+ 'allow_redirection_mode' => $allow_redirection_mode,
64
+ 'docs_url' => $docs_url
65
+ ] );
66
+ } );
libs/addons/admin/index.php ADDED
File without changes
libs/addons/admin/pages/class-rio-statistic-folders-page.php ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Класс отвечает за работу страницы статистики
9
+ *
10
+ * @author Eugene Jokerov <jokerov@gmail.com>
11
+ * @copyright (c) 2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WRIO_StatisticFolders extends WRIO_StatisticPage {
15
+
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public $id = 'io_folders_statistic';
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $internal = true;
25
+
26
+
27
+ /**
28
+ * {@inheritdoc}
29
+ */
30
+ public $page_parent_page = 'none';
31
+
32
+ /**
33
+ * @var string
34
+ */
35
+ public $page_menu_dashicon = 'dashicons-images-alt';
36
+
37
+ /**
38
+ * {@inheritdoc}
39
+ */
40
+ public $add_link_to_plugin_actions = false;
41
+
42
+ /**
43
+ * {@inheritdoc}
44
+ */
45
+ protected $scope = 'custom-folders';
46
+
47
+
48
+ /**
49
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
50
+ * @since 1.0
51
+ * @var WRIO_Views
52
+ */
53
+ private $parent_view;
54
+
55
+ /**
56
+ * @param WRIO_Plugin $plugin
57
+ */
58
+ public function __construct( WRIO_Plugin $plugin ) {
59
+ parent::__construct( $plugin );
60
+
61
+ $this->parent_view = $this->view;
62
+ $this->view = new WRIO_Views( WRIOP_PLUGIN_DIR );
63
+ }
64
+
65
+ /**
66
+ * {@inheritdoc}
67
+ */
68
+ public function getMenuTitle() {
69
+ return __( 'Custom Folders', 'robin-image-optimizer' );
70
+ }
71
+
72
+ /**
73
+ * {@inheritdoc}
74
+ */
75
+ public function getPageTitle() {
76
+ return __( 'Custom Folders', 'robin-image-optimizer' );
77
+ }
78
+
79
+ /**
80
+ * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
81
+ * Меню текущего плагина будет добавлено в общее меню Clearfy
82
+ *
83
+ * @return string
84
+ */
85
+ public function getMenuScope() {
86
+ if ( $this->clearfy_collaboration ) {
87
+ $this->page_parent_page = 'rio_general';
88
+
89
+ return 'wbcr_clearfy';
90
+ }
91
+
92
+ return $this->plugin->getPluginName();
93
+ }
94
+
95
+ /**
96
+ * {@inheritdoc}
97
+ */
98
+ public function assets( $scripts, $styles ) {
99
+ parent::assets( $scripts, $styles );
100
+
101
+ $this->styles->add( WRIOP_PLUGIN_URL . '/admin/assets/css/jquery-file-tree.css' );
102
+ $this->scripts->add( WRIOP_PLUGIN_URL . '/admin/assets/js/jquery-file-tree.js' );
103
+ $this->scripts->add( WRIOP_PLUGIN_URL . '/admin/assets/css/custom-folders.css' );
104
+ $this->scripts->add( WRIOP_PLUGIN_URL . '/admin/assets/js/custom-folders.js' );
105
+ }
106
+
107
+ /**
108
+ * {@inheritdoc}
109
+ */
110
+ public function showPageContent() {
111
+ $is_premium = wrio_is_license_activate();
112
+ $statistics = WRIO_Image_Statistic_Folders::get_instance();
113
+
114
+ $template_data = [
115
+ 'is_premium' => $is_premium,
116
+ 'scope' => $this->scope
117
+ ];
118
+
119
+ //do_action( 'wbcr/rio/multisite_current_blog' );
120
+
121
+ // Page header
122
+ $this->parent_view->print_template( 'part-page-header', [
123
+ 'title' => __( 'Image optimization dashboard', 'robin-image-optimizer' ),
124
+ 'description' => __( 'Monitor image optimization statistics and run on demand or scheduled optimization.', 'robin-image-optimizer' )
125
+ ], $this );
126
+
127
+ // Page tabs
128
+ $this->parent_view->print_template( 'part-bulk-optimization-tabs', $template_data, $this );
129
+
130
+ ?>
131
+ <div class="wbcr-factory-page-group-body" style="padding:0; border-top: 1px solid #d4d4d4;">
132
+ <?php
133
+ // Servers
134
+ $this->parent_view->print_template( 'part-bulk-optimization-servers', $template_data, $this );
135
+
136
+ // Statistic
137
+ $this->parent_view->print_template( 'part-bulk-optimization-statistic', array_merge( $template_data, [
138
+ 'stats' => $statistics->get()
139
+ ] ), $this );
140
+
141
+ // Folders table
142
+ $this->view->print_template( 'part-bulk-optimization-table-folders', $template_data, $this );
143
+
144
+ // Optimization log
145
+ $this->parent_view->print_template( 'part-bulk-optimization-log', array_merge( $template_data, [
146
+ 'process_log' => $statistics->get_last_optimized_images()
147
+ ] ), $this );
148
+ ?>
149
+ </div>
150
+ <script type="text/html" id="wrio-tmpl-bulk-optimization">
151
+ <?php $this->parent_view->print_template( 'modal-bulk-optimization' ); ?>
152
+ </script>
153
+ <script type="text/html" id="wrio-tmpl-select-custom-folders">
154
+ <?php $this->view->print_template( 'modal-select-custom-folders' ); ?>
155
+ </script>
156
+ <?php
157
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
158
+ }
159
+
160
+ protected function get_i18n() {
161
+ $i18n = parent::get_i18n();
162
+
163
+ $i18n['modal_cf_title'] = __( 'Select custom folder', 'robin-image-optimizer' );
164
+ //$i18n['modal_cf_description'] = __( 'Select a directory for optimization. All nested images and folders will be optimized recursively.', 'robin-image-optimizer' );
165
+ $i18n['button_select'] = __( 'Select', 'robin-image-optimizer' );
166
+ $i18n['button_cancel'] = __( 'Cancel', 'robin-image-optimizer' );
167
+ $i18n['button_remove'] = __( 'Remove', 'robin-image-optimizer' );
168
+ $i18n['alert_remove_folder'] = __( 'Exclude directory from optimization?', 'robin-image-optimizer' );
169
+ $i18n['found_images'] = __( 'Selected directory is being indexed. Found %d images.', 'robin-image-optimizer' );
170
+ $i18n['scan_complete'] = __( 'Indexing complete. Directory successfully added and ready for optimization.', 'robin-image-optimizer' );
171
+ $i18n['compressed_in_folder'] = __( 'Compressed %d of %s<br>images', 'robin-image-optimizer' );
172
+ $i18n['optimization_complete'] = __( 'All images from custom folders are optimized.', 'robin-image-optimizer' );
173
+
174
+ return $i18n;
175
+ }
176
+ }
libs/addons/admin/pages/class-rio-statistic-nextgen-page.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // Exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Класс отвечает за работу страницы статистики
9
+ *
10
+ * @author Eugene Jokerov <jokerov@gmail.com>
11
+ * @copyright (c) 2018, Webcraftic
12
+ * @version 1.0
13
+ */
14
+ class WRIO_StatisticNextgenPage extends WRIO_StatisticPage {
15
+
16
+ /**
17
+ * {@inheritdoc}
18
+ */
19
+ public $id = 'io_nextgen_gallery_statistic';
20
+
21
+ /**
22
+ * {@inheritdoc}
23
+ */
24
+ public $page_menu_dashicon = 'dashicons-images-alt';
25
+
26
+ /**
27
+ * {@inheritdoc}
28
+ */
29
+ public $internal = true;
30
+
31
+ /**
32
+ * none - to hide page from plugin menu
33
+ * {@inheritdoc}
34
+ */
35
+ public $page_parent_page = 'none';
36
+
37
+
38
+ /**
39
+ * {@inheritdoc}
40
+ */
41
+ public $add_link_to_plugin_actions = false;
42
+
43
+ /**
44
+ * {@inheritdoc}
45
+ */
46
+ protected $scope = 'nextgen-gallery';
47
+
48
+ /**
49
+ * @param WRIO_Plugin $plugin
50
+ */
51
+ public function __construct( WRIO_Plugin $plugin ) {
52
+ $this->plugin = $plugin;
53
+
54
+ parent::__construct( $plugin );
55
+ }
56
+
57
+ /**
58
+ * {@inheritdoc}
59
+ */
60
+ public function getMenuTitle() {
61
+ return __( 'NextGen Gallery', 'robin-image-optimizer' );
62
+ }
63
+
64
+ /**
65
+ * {@inheritdoc}
66
+ */
67
+ public function getPageTitle() {
68
+ return __( 'NextGen Gallery', 'robin-image-optimizer' );
69
+ }
70
+
71
+ /**
72
+ * Подменяем простраинство имен для меню плагина, если активирован плагин Clearfy
73
+ * Меню текущего плагина будет добавлено в общее меню Clearfy
74
+ *
75
+ * @return string
76
+ */
77
+ public function getMenuScope() {
78
+ if ( $this->clearfy_collaboration ) {
79
+ $this->page_parent_page = 'rio_general';
80
+
81
+ return 'wbcr_clearfy';
82
+ }
83
+
84
+ return $this->plugin->getPluginName();
85
+ }
86
+
87
+ /**
88
+ * {@inheritdoc}
89
+ */
90
+ public function assets( $scripts, $styles ) {
91
+ parent::assets( $scripts, $styles );
92
+ }
93
+
94
+ /**
95
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
96
+ * @since 1.3.0
97
+ * @return object|\WRIO_Image_Statistic
98
+ */
99
+ protected function get_statisctic_data() {
100
+ return WRIO_Image_Statistic_Nextgen::get_instance();
101
+ }
102
+ }
libs/addons/admin/pages/index.php ADDED
File without changes
libs/addons/assets/img/.htaccess ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <IfModule mod_rewrite.c>
2
+ RewriteEngine On
3
+
4
+ ##### TRY FIRST the file appended with .webp (ex. test.jpg.webp) #####
5
+ # Does browser explicitly support webp?
6
+ RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
7
+ # OR Is request from Page Speed
8
+ RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
9
+ # OR does this browser explicitly support webp
10
+ RewriteCond %{HTTP_ACCEPT} image/webp
11
+ # AND is the request a jpg or png?
12
+ RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
13
+ # AND does a .ext.webp image exist?
14
+ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
15
+ # THEN send the webp image and set the env var webp
16
+ RewriteRule ^(.+)$ $1.webp [NC,T=image/webp,E=webp,L]
17
+
18
+ ##### IF NOT, try the file with replaced extension (test.webp) #####
19
+ RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
20
+ RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
21
+ RewriteCond %{HTTP_ACCEPT} image/webp
22
+ # AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
23
+ RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
24
+ # AND does a .ext.webp image exist?
25
+ RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
26
+ # THEN send the webp image and set the env var webp
27
+ RewriteRule (.+)\.(?:jpe?g|png)$ $1.webp [NC,T=image/webp,E=webp,L]
28
+
29
+ </IfModule>
30
+ <IfModule mod_headers.c>
31
+ # If REDIRECT_webp env var exists, append Accept to the Vary header
32
+ Header append Vary Accept env=REDIRECT_webp
33
+ </IfModule>
34
+
35
+ <IfModule mod_mime.c>
36
+ AddType image/webp .webp
37
+ </IfModule>
libs/addons/assets/img/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
libs/addons/assets/img/test.jpg ADDED
Binary file
libs/addons/assets/img/test.jpg.webp ADDED
Binary file
libs/addons/assets/img/test.webp ADDED
Binary file
libs/addons/assets/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
libs/addons/assets/js/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
libs/addons/assets/js/picturefill.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /*! picturefill - v3.0.2 - 2016-02-12
2
+ * https://scottjehl.github.io/picturefill/
3
+ * Copyright (c) 2016 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT
4
+ */
5
+ !function(a){var b=navigator.userAgent;a.HTMLPictureElement&&/ecko/.test(b)&&b.match(/rv\:(\d+)/)&&RegExp.$1<45&&addEventListener("resize",function(){var b,c=document.createElement("source"),d=function(a){var b,d,e=a.parentNode;"PICTURE"===e.nodeName.toUpperCase()?(b=c.cloneNode(),e.insertBefore(b,e.firstElementChild),setTimeout(function(){e.removeChild(b)})):(!a._pfLastSize||a.offsetWidth>a._pfLastSize)&&(a._pfLastSize=a.offsetWidth,d=a.sizes,a.sizes+=",100vw",setTimeout(function(){a.sizes=d}))},e=function(){var a,b=document.querySelectorAll("picture > img, img[srcset][sizes]");for(a=0;a<b.length;a++)d(b[a])},f=function(){clearTimeout(b),b=setTimeout(e,99)},g=a.matchMedia&&matchMedia("(orientation: landscape)"),h=function(){f(),g&&g.addListener&&g.addListener(f)};return c.srcset="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",/^[c|i]|d$/.test(document.readyState||"")?h():document.addEventListener("DOMContentLoaded",h),f}())}(window),function(a,b,c){"use strict";function d(a){return" "===a||" "===a||"\n"===a||"\f"===a||"\r"===a}function e(b,c){var d=new a.Image;return d.onerror=function(){A[b]=!1,ba()},d.onload=function(){A[b]=1===d.width,ba()},d.src=c,"pending"}function f(){M=!1,P=a.devicePixelRatio,N={},O={},s.DPR=P||1,Q.width=Math.max(a.innerWidth||0,z.clientWidth),Q.height=Math.max(a.innerHeight||0,z.clientHeight),Q.vw=Q.width/100,Q.vh=Q.height/100,r=[Q.height,Q.width,P].join("-"),Q.em=s.getEmValue(),Q.rem=Q.em}function g(a,b,c,d){var e,f,g,h;return"saveData"===B.algorithm?a>2.7?h=c+1:(f=b-c,e=Math.pow(a-.6,1.5),g=f*e,d&&(g+=.1*e),h=a+g):h=c>1?Math.sqrt(a*b):a,h>c}function h(a){var b,c=s.getSet(a),d=!1;"pending"!==c&&(d=r,c&&(b=s.setRes(c),s.applySetCandidate(b,a))),a[s.ns].evaled=d}function i(a,b){return a.res-b.res}function j(a,b,c){var d;return!c&&b&&(c=a[s.ns].sets,c=c&&c[c.length-1]),d=k(b,c),d&&(b=s.makeUrl(b),a[s.ns].curSrc=b,a[s.ns].curCan=d,d.res||aa(d,d.set.sizes)),d}function k(a,b){var c,d,e;if(a&&b)for(e=s.parseSet(b),a=s.makeUrl(a),c=0;c<e.length;c++)if(a===s.makeUrl(e[c].url)){d=e[c];break}return d}function l(a,b){var c,d,e,f,g=a.getElementsByTagName("source");for(c=0,d=g.length;d>c;c++)e=g[c],e[s.ns]=!0,f=e.getAttribute("srcset"),f&&b.push({srcset:f,media:e.getAttribute("media"),type:e.getAttribute("type"),sizes:e.getAttribute("sizes")})}function m(a,b){function c(b){var c,d=b.exec(a.substring(m));return d?(c=d[0],m+=c.length,c):void 0}function e(){var a,c,d,e,f,i,j,k,l,m=!1,o={};for(e=0;e<h.length;e++)f=h[e],i=f[f.length-1],j=f.substring(0,f.length-1),k=parseInt(j,10),l=parseFloat(j),X.test(j)&&"w"===i?((a||c)&&(m=!0),0===k?m=!0:a=k):Y.test(j)&&"x"===i?((a||c||d)&&(m=!0),0>l?m=!0:c=l):X.test(j)&&"h"===i?((d||c)&&(m=!0),0===k?m=!0:d=k):m=!0;m||(o.url=g,a&&(o.w=a),c&&(o.d=c),d&&(o.h=d),d||c||a||(o.d=1),1===o.d&&(b.has1x=!0),o.set=b,n.push(o))}function f(){for(c(T),i="",j="in descriptor";;){if(k=a.charAt(m),"in descriptor"===j)if(d(k))i&&(h.push(i),i="",j="after descriptor");else{if(","===k)return m+=1,i&&h.push(i),void e();if("("===k)i+=k,j="in parens";else{if(""===k)return i&&h.push(i),void e();i+=k}}else if("in parens"===j)if(")"===k)i+=k,j="in descriptor";else{if(""===k)return h.push(i),void e();i+=k}else if("after descriptor"===j)if(d(k));else{if(""===k)return void e();j="in descriptor",m-=1}m+=1}}for(var g,h,i,j,k,l=a.length,m=0,n=[];;){if(c(U),m>=l)return n;g=c(V),h=[],","===g.slice(-1)?(g=g.replace(W,""),e()):f()}}function n(a){function b(a){function b(){f&&(g.push(f),f="")}function c(){g[0]&&(h.push(g),g=[])}for(var e,f="",g=[],h=[],i=0,j=0,k=!1;;){if(e=a.charAt(j),""===e)return b(),c(),h;if(k){if("*"===e&&"/"===a[j+1]){k=!1,j+=2,b();continue}j+=1}else{if(d(e)){if(a.charAt(j-1)&&d(a.charAt(j-1))||!f){j+=1;continue}if(0===i){b(),j+=1;continue}e=" "}else if("("===e)i+=1;else if(")"===e)i-=1;else{if(","===e){b(),c(),j+=1;continue}if("/"===e&&"*"===a.charAt(j+1)){k=!0,j+=2;continue}}f+=e,j+=1}}}function c(a){return k.test(a)&&parseFloat(a)>=0?!0:l.test(a)?!0:"0"===a||"-0"===a||"+0"===a?!0:!1}var e,f,g,h,i,j,k=/^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i,l=/^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i;for(f=b(a),g=f.length,e=0;g>e;e++)if(h=f[e],i=h[h.length-1],c(i)){if(j=i,h.pop(),0===h.length)return j;if(h=h.join(" "),s.matchesMedia(h))return j}return"100vw"}b.createElement("picture");var o,p,q,r,s={},t=!1,u=function(){},v=b.createElement("img"),w=v.getAttribute,x=v.setAttribute,y=v.removeAttribute,z=b.documentElement,A={},B={algorithm:""},C="data-pfsrc",D=C+"set",E=navigator.userAgent,F=/rident/.test(E)||/ecko/.test(E)&&E.match(/rv\:(\d+)/)&&RegExp.$1>35,G="currentSrc",H=/\s+\+?\d+(e\d+)?w/,I=/(\([^)]+\))?\s*(.+)/,J=a.picturefillCFG,K="position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)",L="font-size:100%!important;",M=!0,N={},O={},P=a.devicePixelRatio,Q={px:1,"in":96},R=b.createElement("a"),S=!1,T=/^[ \t\n\r\u000c]+/,U=/^[, \t\n\r\u000c]+/,V=/^[^ \t\n\r\u000c]+/,W=/[,]+$/,X=/^\d+$/,Y=/^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/,Z=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)},$=function(a){var b={};return function(c){return c in b||(b[c]=a(c)),b[c]}},_=function(){var a=/^([\d\.]+)(em|vw|px)$/,b=function(){for(var a=arguments,b=0,c=a[0];++b in a;)c=c.replace(a[b],a[++b]);return c},c=$(function(a){return"return "+b((a||"").toLowerCase(),/\band\b/g,"&&",/,/g,"||",/min-([a-z-\s]+):/g,"e.$1>=",/max-([a-z-\s]+):/g,"e.$1<=",/calc([^)]+)/g,"($1)",/(\d+[\.]*[\d]*)([a-z]+)/g,"($1 * e.$2)",/^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi,"")+";"});return function(b,d){var e;if(!(b in N))if(N[b]=!1,d&&(e=b.match(a)))N[b]=e[1]*Q[e[2]];else try{N[b]=new Function("e",c(b))(Q)}catch(f){}return N[b]}}(),aa=function(a,b){return a.w?(a.cWidth=s.calcListLength(b||"100vw"),a.res=a.w/a.cWidth):a.res=a.d,a},ba=function(a){if(t){var c,d,e,f=a||{};if(f.elements&&1===f.elements.nodeType&&("IMG"===f.elements.nodeName.toUpperCase()?f.elements=[f.elements]:(f.context=f.elements,f.elements=null)),c=f.elements||s.qsa(f.context||b,f.reevaluate||f.reselect?s.sel:s.selShort),e=c.length){for(s.setupRun(f),S=!0,d=0;e>d;d++)s.fillImg(c[d],f);s.teardownRun(f)}}};o=a.console&&console.warn?function(a){console.warn(a)}:u,G in v||(G="src"),A["image/jpeg"]=!0,A["image/gif"]=!0,A["image/png"]=!0,A["image/svg+xml"]=b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1"),s.ns=("pf"+(new Date).getTime()).substr(0,9),s.supSrcset="srcset"in v,s.supSizes="sizes"in v,s.supPicture=!!a.HTMLPictureElement,s.supSrcset&&s.supPicture&&!s.supSizes&&!function(a){v.srcset="data:,a",a.src="data:,a",s.supSrcset=v.complete===a.complete,s.supPicture=s.supSrcset&&s.supPicture}(b.createElement("img")),s.supSrcset&&!s.supSizes?!function(){var a="data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw==",c="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",d=b.createElement("img"),e=function(){var a=d.width;2===a&&(s.supSizes=!0),q=s.supSrcset&&!s.supSizes,t=!0,setTimeout(ba)};d.onload=e,d.onerror=e,d.setAttribute("sizes","9px"),d.srcset=c+" 1w,"+a+" 9w",d.src=c}():t=!0,s.selShort="picture>img,img[srcset]",s.sel=s.selShort,s.cfg=B,s.DPR=P||1,s.u=Q,s.types=A,s.setSize=u,s.makeUrl=$(function(a){return R.href=a,R.href}),s.qsa=function(a,b){return"querySelector"in a?a.querySelectorAll(b):[]},s.matchesMedia=function(){return a.matchMedia&&(matchMedia("(min-width: 0.1em)")||{}).matches?s.matchesMedia=function(a){return!a||matchMedia(a).matches}:s.matchesMedia=s.mMQ,s.matchesMedia.apply(this,arguments)},s.mMQ=function(a){return a?_(a):!0},s.calcLength=function(a){var b=_(a,!0)||!1;return 0>b&&(b=!1),b},s.supportsType=function(a){return a?A[a]:!0},s.parseSize=$(function(a){var b=(a||"").match(I);return{media:b&&b[1],length:b&&b[2]}}),s.parseSet=function(a){return a.cands||(a.cands=m(a.srcset,a)),a.cands},s.getEmValue=function(){var a;if(!p&&(a=b.body)){var c=b.createElement("div"),d=z.style.cssText,e=a.style.cssText;c.style.cssText=K,z.style.cssText=L,a.style.cssText=L,a.appendChild(c),p=c.offsetWidth,a.removeChild(c),p=parseFloat(p,10),z.style.cssText=d,a.style.cssText=e}return p||16},s.calcListLength=function(a){if(!(a in O)||B.uT){var b=s.calcLength(n(a));O[a]=b?b:Q.width}return O[a]},s.setRes=function(a){var b;if(a){b=s.parseSet(a);for(var c=0,d=b.length;d>c;c++)aa(b[c],a.sizes)}return b},s.setRes.res=aa,s.applySetCandidate=function(a,b){if(a.length){var c,d,e,f,h,k,l,m,n,o=b[s.ns],p=s.DPR;if(k=o.curSrc||b[G],l=o.curCan||j(b,k,a[0].set),l&&l.set===a[0].set&&(n=F&&!b.complete&&l.res-.1>p,n||(l.cached=!0,l.res>=p&&(h=l))),!h)for(a.sort(i),f=a.length,h=a[f-1],d=0;f>d;d++)if(c=a[d],c.res>=p){e=d-1,h=a[e]&&(n||k!==s.makeUrl(c.url))&&g(a[e].res,c.res,p,a[e].cached)?a[e]:c;break}h&&(m=s.makeUrl(h.url),o.curSrc=m,o.curCan=h,m!==k&&s.setSrc(b,h),s.setSize(b))}},s.setSrc=function(a,b){var c;a.src=b.url,"image/svg+xml"===b.set.type&&(c=a.style.width,a.style.width=a.offsetWidth+1+"px",a.offsetWidth+1&&(a.style.width=c))},s.getSet=function(a){var b,c,d,e=!1,f=a[s.ns].sets;for(b=0;b<f.length&&!e;b++)if(c=f[b],c.srcset&&s.matchesMedia(c.media)&&(d=s.supportsType(c.type))){"pending"===d&&(c=d),e=c;break}return e},s.parseSets=function(a,b,d){var e,f,g,h,i=b&&"PICTURE"===b.nodeName.toUpperCase(),j=a[s.ns];(j.src===c||d.src)&&(j.src=w.call(a,"src"),j.src?x.call(a,C,j.src):y.call(a,C)),(j.srcset===c||d.srcset||!s.supSrcset||a.srcset)&&(e=w.call(a,"srcset"),j.srcset=e,h=!0),j.sets=[],i&&(j.pic=!0,l(b,j.sets)),j.srcset?(f={srcset:j.srcset,sizes:w.call(a,"sizes")},j.sets.push(f),g=(q||j.src)&&H.test(j.srcset||""),g||!j.src||k(j.src,f)||f.has1x||(f.srcset+=", "+j.src,f.cands.push({url:j.src,d:1,set:f}))):j.src&&j.sets.push({srcset:j.src,sizes:null}),j.curCan=null,j.curSrc=c,j.supported=!(i||f&&!s.supSrcset||g&&!s.supSizes),h&&s.supSrcset&&!j.supported&&(e?(x.call(a,D,e),a.srcset=""):y.call(a,D)),j.supported&&!j.srcset&&(!j.src&&a.src||a.src!==s.makeUrl(j.src))&&(null===j.src?a.removeAttribute("src"):a.src=j.src),j.parsed=!0},s.fillImg=function(a,b){var c,d=b.reselect||b.reevaluate;a[s.ns]||(a[s.ns]={}),c=a[s.ns],(d||c.evaled!==r)&&((!c.parsed||b.reevaluate)&&s.parseSets(a,a.parentNode,b),c.supported?c.evaled=r:h(a))},s.setupRun=function(){(!S||M||P!==a.devicePixelRatio)&&f()},s.supPicture?(ba=u,s.fillImg=u):!function(){var c,d=a.attachEvent?/d$|^c/:/d$|^c|^i/,e=function(){var a=b.readyState||"";f=setTimeout(e,"loading"===a?200:999),b.body&&(s.fillImgs(),c=c||d.test(a),c&&clearTimeout(f))},f=setTimeout(e,b.body?9:99),g=function(a,b){var c,d,e=function(){var f=new Date-d;b>f?c=setTimeout(e,b-f):(c=null,a())};return function(){d=new Date,c||(c=setTimeout(e,b))}},h=z.clientHeight,i=function(){M=Math.max(a.innerWidth||0,z.clientWidth)!==Q.width||z.clientHeight!==h,h=z.clientHeight,M&&s.fillImgs()};Z(a,"resize",g(i,99)),Z(b,"readystatechange",e)}(),s.picturefill=ba,s.fillImgs=ba,s.teardownRun=u,ba._=s,a.picturefillCFG={pf:s,push:function(a){var b=a.shift();"function"==typeof s[b]?s[b].apply(s,a):(B[b]=a[0],S&&s.fillImgs({reselect:!0}))}};for(;J&&J.length;)a.picturefillCFG.push(J.shift());a.picturefill=ba,"object"==typeof module&&"object"==typeof module.exports?module.exports=ba:"function"==typeof define&&define.amd&&define("picturefill",function(){return ba}),s.supPicture||(A["image/webp"]=e("image/webp","data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=="))}(window,document);
libs/addons/includes/classes/class.backup.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с резервным копированием изображений
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIOP_Backup extends WIO_Backup {
16
+
17
+ CONST CF_BACKUP_DIR_NAME = 'custom-folders';
18
+ CONST NEXTGEN_BACKUP_DIR_NAME = 'nextgen-gallery';
19
+
20
+ /**
21
+ * The single instance of the class.
22
+ *
23
+ * @since 1.3.0
24
+ * @access protected
25
+ * @var object
26
+ */
27
+ protected static $_instance;
28
+
29
+ /**
30
+ * Получает путь к папке с резервными копиями
31
+ *
32
+ * @param array $gallery_meta метаданные аттачмента
33
+ *
34
+ * @return string
35
+ */
36
+ public function getNextgenBackupDir( $gallery_meta ) {
37
+ $backup_dir = $this->getBackupDir();
38
+ $backup_dir .= self::NEXTGEN_BACKUP_DIR_NAME . '/' . $gallery_meta->gid;
39
+
40
+ if ( ! is_dir( $backup_dir ) ) {
41
+ $backup_dir = $this->mkdir( $backup_dir );
42
+
43
+ if ( is_wp_error( $backup_dir ) ) {
44
+ return $backup_dir;
45
+ }
46
+ }
47
+
48
+ return trailingslashit( $backup_dir );
49
+ }
50
+
51
+ /**
52
+ * Делаем резервную копию NextGEN
53
+ *
54
+ * @param WRIO_Image_Nextgen $nextgen_image аттачмент
55
+ *
56
+ * @return bool|WP_Error
57
+ */
58
+ public function backupNextgen( $nextgen_image ) {
59
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
60
+
61
+ if ( ! $backup_origin_images ) {
62
+ return false; // если бекап не требуется
63
+ }
64
+
65
+ $original_file = $nextgen_image->get( 'path' );
66
+ $original_thumbnail_file = $nextgen_image->get( 'thumbnail_path' );
67
+ $backup_dir = $this->getNextgenBackupDir( $nextgen_image->get( 'gallery_meta' ) );
68
+
69
+ if ( is_wp_error( $backup_dir ) ) {
70
+ return $backup_dir;
71
+ }
72
+
73
+ $backup_file = $backup_dir . $nextgen_image->get( 'file' );
74
+
75
+ if ( is_file( $original_file ) ) {
76
+ if ( ! @copy( $original_file, $backup_file ) ) {
77
+ WRIO_Logger::error( sprintf( 'Failed to swap original file %s with %s as copy() failed', $backup_file, $original_file ) );
78
+
79
+ return false;
80
+ }
81
+ }
82
+
83
+ $backup_thumbnail_file = $backup_dir . $nextgen_image->get( 'thumbnail_file' );
84
+
85
+ if ( is_file( $original_thumbnail_file ) ) {
86
+ if ( @copy( $original_thumbnail_file, $backup_thumbnail_file ) ) {
87
+ WRIO_Logger::error( sprintf( 'Failed to swap thumbnail file %s with %s as copy() failed', $backup_thumbnail_file, $original_thumbnail_file ) );
88
+
89
+ return false;
90
+ }
91
+ }
92
+
93
+ return true;
94
+ }
95
+
96
+ /**
97
+ * Restore NextGen images piece by piece.
98
+ *
99
+ * @param int $limit Limit on number of NextGen image to restore. Default: 50, maximum 1000.
100
+ *
101
+ * @return array {
102
+ * Result of process: how many images restored and how many remain.
103
+ * @type int $processed Count of processed images.
104
+ * @type int $remane Count of remained images to be processed.
105
+ * }
106
+ */
107
+ public function restoreAllNextGen( $limit = 50 ) {
108
+
109
+ if ( ! is_numeric( $limit ) || is_numeric( $limit ) && $limit > 1000 ) {
110
+ $limit = 50;
111
+ }
112
+
113
+ $queue_table = RIO_Process_Queue::table_name();
114
+ $nextgen_sql = "SELECT * FROM {$queue_table} WHERE `item_type` = %s AND `result_status` = %s LIMIT %d";
115
+
116
+ global $wpdb;
117
+
118
+ $nextgen_images = $wpdb->get_results( $wpdb->prepare( $nextgen_sql, 'nextgen', RIO_Process_Queue::STATUS_SUCCESS, $limit ) );
119
+
120
+ $result = [
121
+ 'processed' => 0,
122
+ 'remane' => 0,
123
+ ];
124
+
125
+ if ( empty( $nextgen_images ) ) {
126
+ return $result;
127
+ }
128
+
129
+ foreach ( $nextgen_images as $nextgen_image ) {
130
+ $nextgen_model = new WRIO_Image_Nextgen( $nextgen_image->object_id );
131
+
132
+ $restored = $nextgen_model->restore();
133
+
134
+ if ( ! is_wp_error( $restored ) ) {
135
+ $result['processed'] = $result['processed'] ++;
136
+ } else {
137
+ WRIO_Logger::error( sprintf( 'Failed to restore nextgen image ID: %s as %s::%s failed with message: %s', $nextgen_image->object_id, get_class( 'WRIO_Image_Nextgen' ), 'restore()', $restored->get_error_message() ) );
138
+ }
139
+ }
140
+
141
+ $nextgen_sql_remane = "SELECT COUNT(*) AS remane FROM {$queue_table} WHERE `item_type` = %s AND `result_status` = %s";
142
+
143
+ $nextgen_image_remane = $wpdb->get_var( $wpdb->prepare( $nextgen_sql_remane, 'nextgen', RIO_Process_Queue::STATUS_SUCCESS ) );
144
+
145
+ if ( $nextgen_image_remane === null ) {
146
+ WRIO_Logger::error( sprintf( 'Failed to get remained number of nextgen image by SQL: %s', $nextgen_sql_remane ) );
147
+ }
148
+
149
+ $result['remane'] = $nextgen_image_remane !== null ? (int) $nextgen_image_remane : 0;
150
+
151
+ if ( $result['remane'] === 0 ) {
152
+ // Should empty original/optimized size once all backups are empty
153
+ WRIO_Plugin::app()->updateOption( 'nextgen_original_size', 0 );
154
+ WRIO_Plugin::app()->updateOption( 'nextgen_optimized_size', 0 );
155
+ }
156
+
157
+ return $result;
158
+ }
159
+
160
+ /**
161
+ * Restore Custom Folders piece by piece.
162
+ *
163
+ * @param int $limit Limit on number of Custom Folders to restore. Default: 50, maximum 1000.
164
+ *
165
+ * @return array {
166
+ * Result of process: how many folders restored and how many remain.
167
+ * @type int $processed Count of processed folders.
168
+ * @type int $remane Count of remained folders to be processed.
169
+ * }
170
+ */
171
+ public function restoreAllCustomFolders( $limit = 50 ) {
172
+ if ( ! is_numeric( $limit ) || is_numeric( $limit ) && $limit > 1000 ) {
173
+ $limit = 50;
174
+ }
175
+
176
+ $queue_table = RIO_Process_Queue::table_name();
177
+ $cf_sql = "SELECT * FROM {$queue_table} WHERE `item_type` = %s AND `result_status` = %s LIMIT %d";
178
+
179
+ global $wpdb;
180
+
181
+ $cf_images = $wpdb->get_results( $wpdb->prepare( $cf_sql, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS, $limit ) );
182
+
183
+ $result = [
184
+ 'processed' => 0,
185
+ 'remane' => 0,
186
+ ];
187
+
188
+ if ( empty( $cf_images ) ) {
189
+ return $result;
190
+ }
191
+
192
+ foreach ( $cf_images as $cf_image ) {
193
+ $cf_model = new WRIO_Folder_Image( $cf_image->object_id, $cf_image );
194
+
195
+ $restored = $cf_model->restore();
196
+
197
+ if ( ! is_wp_error( $restored ) ) {
198
+ $result['processed'] = $result['processed'] ++;
199
+ } else {
200
+ WRIO_Logger::error( sprintf( 'Failed to restore Custom Folder ID: %s as %s::%s failed with message: %s', $cf_image->object_id, get_class( 'WRIO_Folder_Image' ), 'restore()', $restored->get_error_message() ) );
201
+ }
202
+ }
203
+
204
+ $cf_sql_remane = "SELECT COUNT(*) AS remane FROM {$queue_table} WHERE `item_type` = %s AND `result_status` = %s";
205
+
206
+ $cf_image_remane = $wpdb->get_var( $wpdb->prepare( $cf_sql_remane, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS ) );
207
+
208
+ if ( $cf_image_remane === null ) {
209
+ WRIO_Logger::error( sprintf( 'Failed to get remained number of Custom Folder by SQL: %s', $cf_sql_remane ) );
210
+ }
211
+
212
+ $result['remane'] = $cf_image_remane !== null ? (int) $cf_image_remane : 0;
213
+
214
+ if ( $result['remane'] === 0 ) {
215
+ // Should empty original/optimized size once all backups are empty
216
+ WRIO_Plugin::app()->updateOption( 'folders_original_size', 0 );
217
+ WRIO_Plugin::app()->updateOption( 'folders_optimized_size', 0 );
218
+
219
+ $custom_folders = WRIO_Custom_Folders::get_instance();
220
+ $folders = $custom_folders->getFolders();
221
+
222
+ if ( ! empty( $folders ) ) {
223
+ foreach ( $folders as $folder ) {
224
+ $folder->reCountOptimizedFiles();
225
+ }
226
+ $custom_folders->saveFolders();
227
+ }
228
+ }
229
+
230
+ return $result;
231
+ }
232
+
233
+ /**
234
+ * Восстанавливаем из резервной копии NextGEN
235
+ *
236
+ * @param WRIO_Image_Nextgen $nextgen_image аттачмент
237
+ *
238
+ * @return bool|WP_Error
239
+ */
240
+
241
+ public function restoreNextgen( $nextgen_image ) {
242
+
243
+ $original_file = $nextgen_image->get( 'path' );
244
+ $original_thumbnail_file = $nextgen_image->get( 'thumbnail_path' );
245
+ $backup_dir = $this->getNextgenBackupDir( $nextgen_image->get( 'gallery_meta' ) );
246
+
247
+ if ( is_wp_error( $backup_dir ) ) {
248
+ return $backup_dir;
249
+ }
250
+
251
+ $backup_file = $backup_dir . $nextgen_image->get( 'file' );
252
+ $backup_thumbnail_file = $backup_dir . $nextgen_image->get( 'thumbnail_file' );
253
+
254
+ if ( ! is_file( $backup_file ) ) {
255
+ $error_msg = sprintf( "Unable to restore from a backup. There is no file (%s).", $backup_file );
256
+ WRIO_Logger::error( sprintf( '%s, Nextgen image id: %s', $error_msg, $nextgen_image->get( 'id' ) ) );
257
+
258
+ return new WP_Error( 'file_not_exists', $error_msg );
259
+ }
260
+
261
+ // Restore original file
262
+ if ( ! $this->restore_file( $backup_file, $original_file ) ) {
263
+ return false;
264
+ }
265
+
266
+ //Restore thumbnail file
267
+ if ( ! $this->restore_file( $backup_thumbnail_file, $original_thumbnail_file ) ) {
268
+ return false;
269
+ }
270
+
271
+ return true;
272
+ }
273
+
274
+ /**
275
+ * Получает путь к папке с резервными копиями
276
+ *
277
+ * @param string $image_abs_path абсолютный путь к файлу картинки
278
+ *
279
+ * @return string
280
+ */
281
+
282
+ public function getCFBackupDir( $image_abs_path ) {
283
+ $backup_dir = $this->getBackupDir();
284
+
285
+ $image_abs_path = wp_normalize_path( $image_abs_path );
286
+ $wp_abs_path = wp_normalize_path( ABSPATH );
287
+
288
+ // Get all subfolders in which the image is stored.
289
+ // This is necessary to create an alternate subfolders
290
+ // in directory where they are stored in backups.
291
+ $subfolders = str_replace( $wp_abs_path, '', dirname( $image_abs_path ) );
292
+
293
+ $backup_dir .= self::CF_BACKUP_DIR_NAME . '/' . $subfolders;
294
+
295
+ if ( ! is_dir( $backup_dir ) ) {
296
+ $backup_dir = $this->mkdir( $backup_dir );
297
+
298
+ if ( is_wp_error( $backup_dir ) ) {
299
+ return $backup_dir;
300
+ }
301
+ }
302
+
303
+ return trailingslashit( $backup_dir );
304
+ }
305
+
306
+ /**
307
+ * Делаем резервную копию Custom Folder Image
308
+ *
309
+ * @param WRIO_Folder_Image $folder_image Custom Folder Image
310
+ *
311
+ * @return bool|WP_Error
312
+ */
313
+ public function backupCFImage( $folder_image ) {
314
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
315
+
316
+ if ( ! $backup_origin_images ) {
317
+ return false; // если бекап не требуется
318
+ }
319
+
320
+ $original_file = $folder_image->get( 'path' );
321
+ $backup_dir = $this->getCFBackupDir( $original_file );
322
+
323
+ if ( is_wp_error( $backup_dir ) ) {
324
+ return $backup_dir;
325
+ }
326
+
327
+ $backup_file = $backup_dir . wp_basename( $original_file );
328
+
329
+ if ( is_file( $original_file ) ) {
330
+ if ( ! @copy( $original_file, $backup_file ) ) {
331
+ WRIO_Logger::error( sprintf( 'Failed to swap original file %s with %s as copy() failed', $backup_file, $original_file ) );
332
+
333
+ return false;
334
+ }
335
+ }
336
+
337
+ return true;
338
+ }
339
+
340
+ /**
341
+ * Восстанавливаем из резервной копии Custom Folder Image
342
+ *
343
+ * @param WRIO_Folder_Image $folder_image Custom Folder Image
344
+ *
345
+ * @return bool|WP_Error
346
+ */
347
+ public function restoreCFImage( $folder_image ) {
348
+
349
+ $original_file = $folder_image->get( 'path' );
350
+ $backup_dir = $this->getCFBackupDir( $original_file );
351
+
352
+ if ( is_wp_error( $backup_dir ) ) {
353
+ return $backup_dir;
354
+ }
355
+
356
+ $backup_file = $backup_dir . wp_basename( $original_file );
357
+
358
+ if ( ! $this->restore_file( $backup_file, $original_file ) ) {
359
+ return false;
360
+ }
361
+
362
+ return true;
363
+ }
364
+ }
libs/addons/includes/classes/class.custom-folders.php ADDED
@@ -0,0 +1,659 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с кастомными папками
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Custom_Folders {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * @var WRIO_Folder[]
28
+ */
29
+ private $folders = [];
30
+
31
+ /**
32
+ * @var WRIO_Folder_Image[]
33
+ */
34
+ private $folder_images = [];
35
+
36
+ /**
37
+ * WRIO_Custom_Folders constructor.
38
+ */
39
+ public function __construct() {
40
+ $folders = WRIO_Plugin::app()->getOption( 'custom_folders', [] );
41
+
42
+ if ( ! empty( $folders ) ) {
43
+ foreach ( (array) $folders as $uid => $folder ) {
44
+ $this->folders[ $uid ] = new WRIO_Folder( $folder );
45
+ }
46
+ }
47
+
48
+ $this->init();
49
+ }
50
+
51
+ /**
52
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
53
+ * @since 1.3.0
54
+ *
55
+ * @return object|\WRIO_Custom_Folders object Main instance.
56
+ */
57
+ public static function get_instance() {
58
+ if ( ! isset( static::$_instance ) ) {
59
+ static::$_instance = new static();
60
+ }
61
+
62
+ return static::$_instance;
63
+ }
64
+
65
+ /**
66
+ * Object init.
67
+ */
68
+ public function init() {
69
+
70
+ // todo: убрать эти фильтры
71
+ add_filter( 'wbcr/rio/optimize_template/optimize_ajax_action', [ $this, 'optimizeAjaxAction' ], 10, 2 );
72
+ add_filter( 'wbcr/rio/optimize_template/reoptimize_ajax_action', [
73
+ $this,
74
+ 'reoptimizeAjaxAction',
75
+ ], 10, 2 );
76
+ add_filter( 'wbcr/rio/optimize_template/restore_ajax_action', [ $this, 'restoreAjaxAction' ], 10, 2 );
77
+
78
+ add_action( 'admin_menu', [ $this, 'add_media_page' ] );
79
+ add_action( 'admin_enqueue_scripts', [ $this, 'media_page_assets' ] );
80
+ add_action( 'wbcr/rio/optimize_template/optimized_percent', [ $this, 'optimizedPercent' ], 10, 2 );
81
+ //add_action( 'wbcr/riop/queue_item_saved', [ $this, 'webpSuccess' ], 20 );
82
+ add_action( 'wbcr/rio/webp_success', [ $this, 'webpSuccess' ], 20 );
83
+ }
84
+
85
+ public function add_media_page() {
86
+ add_submenu_page( 'upload.php', 'Other Media', 'Other Media', 'manage_options', 'rio-custom-media', [
87
+ $this,
88
+ 'custom_media_page'
89
+ ] );
90
+ }
91
+
92
+ public function media_page_assets( $hook ) {
93
+ if ( 'media_page_rio-custom-media' == $hook ) {
94
+ wp_enqueue_style( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/css/media.css', [], WRIO_Plugin::app()->getPluginVersion() );
95
+ wp_enqueue_style( 'wriop-other-media', WRIOP_PLUGIN_URL . '/admin/assets/css/other-media.css', [], WRIO_Plugin::app()->getPluginVersion() );
96
+ wp_enqueue_script( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion() );
97
+ }
98
+ }
99
+
100
+ public function custom_media_page() {
101
+ if ( ! class_exists( 'WP_List_Table' ) ) {
102
+ require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
103
+ }
104
+ require_once( WRIOP_PLUGIN_DIR . '/includes/classes/class.folders-list-table.php' );
105
+
106
+ $list_table = new WRIO_Folders_List_Table();
107
+ $list_table->prepare_items();
108
+ $list_table->display(); // выводит на экран весь блок с таблицей и фильтрами
109
+ }
110
+
111
+ /**
112
+ * Get folder by specified id.
113
+ *
114
+ * @param string $uid Folder sha256 hash.
115
+ *
116
+ * @return bool|WRIO_Folder
117
+ */
118
+ public function getFolder( $uid ) {
119
+ if ( isset( $this->folders[ $uid ] ) ) {
120
+ return $this->folders[ $uid ];
121
+ }
122
+
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Get all available folders.
128
+ *
129
+ * @return WRIO_Folder[]
130
+ */
131
+ public function getFolders() {
132
+ return $this->folders;
133
+ }
134
+
135
+ /**
136
+ * Add new folder by specified path.
137
+ *
138
+ * @param string $path Path to folder.
139
+ *
140
+ * @return WRIO_Folder|\WP_Error
141
+ */
142
+ public function addFolder( $path ) {
143
+
144
+ if ( empty( $path ) ) {
145
+ return new WP_Error( 'empty_path', 'Path is empty.' );
146
+ }
147
+
148
+ $uid = hash( 'sha256', $path );
149
+ if ( ! $this->getFolder( $uid ) ) {
150
+ $folder_data = [
151
+ 'path' => $path,
152
+ 'uid' => $uid,
153
+ ];
154
+ $new_folder = new WRIO_Folder( $folder_data );
155
+ $new_folder->reCountFiles();
156
+ $this->folders[ $uid ] = $new_folder;
157
+ $this->saveFolders();
158
+
159
+ return $new_folder;
160
+ }
161
+
162
+ return new WP_Error( 'folder_already_exists', 'Folder has already been added before.' );
163
+ }
164
+
165
+ /**
166
+ * Remove folder by sha256 hash.
167
+ *
168
+ * @param string $uid SHA256 folder hash id.
169
+ *
170
+ * @return bool
171
+ */
172
+ public function removeFolder( $uid ) {
173
+ if ( empty( $uid ) ) {
174
+ return false;
175
+ }
176
+
177
+ $folder = $this->getFolder( $uid );
178
+
179
+ if ( ! $folder ) {
180
+ return false;
181
+ }
182
+
183
+ $folder->remove();
184
+
185
+ unset( $this->folders[ $uid ] );
186
+
187
+ return true;
188
+ }
189
+
190
+ /**
191
+ * Saved all folders in options.
192
+ */
193
+ public function saveFolders() {
194
+ $folders = [];
195
+ foreach ( $this->folders as $uid => $folder ) {
196
+ $folders[ $uid ] = $folder->toArray();
197
+ }
198
+ WRIO_Plugin::app()->updateOption( 'custom_folders', $folders );
199
+ }
200
+
201
+ /**
202
+ * Возвращает объект image
203
+ *
204
+ * @param int $image_id
205
+ * @param array|false $image_meta
206
+ *
207
+ * @return WRIO_Folder_Image
208
+ */
209
+ public function getImage( $image_id, $image_meta = false ) {
210
+ if ( ! isset( $this->folder_images[ $image_id ] ) ) {
211
+ $this->folder_images[ $image_id ] = new WRIO_Folder_Image( $image_id, $image_meta );
212
+ }
213
+
214
+ return $this->folder_images[ $image_id ];
215
+ }
216
+
217
+ /**
218
+ * Оптимизирует cf_image
219
+ *
220
+ * @param int $image_id номер картинки в таблице nextgen
221
+ * @param string $level качество
222
+ *
223
+ * @return array
224
+ */
225
+ public function optimizeImage( $image_id, $level = '' ) {
226
+ $cf_image = $this->getImage( $image_id );
227
+ $optimization_data = $cf_image->getOptimizationData();
228
+
229
+ if ( 'processing' == $optimization_data->get_result_status() ) {
230
+ return $this->deferredOptimizeImage( $image_id );
231
+ }
232
+
233
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
234
+
235
+ if ( $cf_image->isOptimized() ) {
236
+ $optimized_size = $optimization_data->get_final_size();
237
+ $original_size = $optimization_data->get_original_size();
238
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
239
+ $image_statistics->deductFromField( 'original_size', $original_size );
240
+ $cf_image->restore();
241
+ }
242
+
243
+ $image_optimized_data = $cf_image->optimize( $level );
244
+
245
+ $original_size = $image_optimized_data['original_size'];
246
+ $optimized_size = $image_optimized_data['optimized_size'];
247
+
248
+ $image_statistics->addToField( 'optimized_size', $optimized_size );
249
+ $image_statistics->addToField( 'original_size', $original_size );
250
+ $image_statistics->save();
251
+
252
+ $folder = $this->getFolder( $cf_image->get( 'folder_uid' ) );
253
+ $folder->reCountOptimizedFiles();
254
+ $this->saveFolders();
255
+
256
+ return $image_optimized_data;
257
+ }
258
+
259
+ /**
260
+ * Отложенная оптимизация image. Этап 1: отправка на сервер оптимизации
261
+ *
262
+ * @param int $image_id
263
+ *
264
+ * @return array|false
265
+ */
266
+ protected function deferredOptimizeImage( $image_id ) {
267
+ $cf_image = $this->getImage( $image_id );
268
+ $optimization_data = $cf_image->getOptimizationData();
269
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
270
+
271
+ // если текущий сервер оптимизации не поддерживает отложенную оптимизацию, а в очереди есть аттачменты - ставим им ошибку
272
+ if ( ! $image_processor->isDeferred() ) {
273
+ $optimization_data->set_result_status( 'error' );
274
+ /**
275
+ * @var $extra_data WRIO_CF_Image_Extra_Data
276
+ */
277
+ $extra_data = $optimization_data->get_extra_data();
278
+ $extra_data->set_error( 'deferred' );
279
+ $extra_data->set_error_msg( 'server not support deferred optimization' );
280
+ $optimization_data->set_extra_data( $extra_data );
281
+ $optimization_data->save();
282
+
283
+ WRIO_Logger::error( sprintf( 'Server %s does not support deferred optimization', get_class( $image_processor ) ) );
284
+
285
+ return false;
286
+ }
287
+ $optimized_data = $cf_image->deferredOptimization();
288
+ if ( $optimized_data ) {
289
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
290
+ $image_statistics->addToField( 'folders_optimized_size', $optimized_data['optimized_size'] );
291
+ $image_statistics->addToField( 'folders_original_size', $optimized_data['original_size'] );
292
+ $image_statistics->save();
293
+ }
294
+
295
+ return $optimized_data;
296
+ }
297
+
298
+ /**
299
+ * Обработка неоптимизированных изображений
300
+ *
301
+ * @param int $max_process_per_request кол-во аттачментов за 1 запуск
302
+ *
303
+ * @return array|\WP_Error
304
+ */
305
+ public function processUnoptimizedImages( $max_process_per_request = 5 ) {
306
+
307
+ $backup = WRIOP_Backup::get_instance();
308
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
309
+
310
+ if ( $backup_origin_images && ! $backup->isBackupWritable() ) {
311
+ return new WP_Error( 'unwritable_backup_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
312
+ }
313
+
314
+ if ( ! $backup->isUploadWritable() ) {
315
+ return new WP_Error( 'unwritable_upload_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
316
+ }
317
+
318
+ if ( empty( $this->folders ) ) {
319
+ return new WP_Error( 'folders_not_found', __( 'You need to add an custom folder to start optimization!', 'robin-image-optimizer' ) );
320
+ }
321
+
322
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
323
+
324
+ $folder_images = $image_statistics->getUnoptimized( $max_process_per_request ); // тут будет выборка
325
+ $total = $image_statistics->getUnoptimizedCount(); // тут общее кол-во неоптимизированных
326
+
327
+ $folder_images_count = count( $folder_images );
328
+ $optimized_count = 0;
329
+ $optimized_items = [];
330
+
331
+ // обработка
332
+ if ( $folder_images_count ) {
333
+ foreach ( $folder_images as $folder_image ) {
334
+ $this->optimizeImage( $folder_image->id );
335
+ $optimized_count ++;
336
+ $optimized_items[ $folder_image->id ] = $folder_image;
337
+ }
338
+ }
339
+
340
+ $remain = $total - $folder_images_count;
341
+
342
+ // проверяем, есть ли аттачменты в очереди на отложенную оптимизацию
343
+ $optimized_data = $this->processDeferredOptimization();
344
+
345
+ if ( $optimized_data ) {
346
+ $optimized_count = $optimized_data['optimized_count'];
347
+ $remain = $total - $optimized_count;
348
+ }
349
+
350
+ if ( $remain <= 0 ) {
351
+ $remain = 0;
352
+ }
353
+
354
+ $last_optimized = end( $optimized_items );
355
+
356
+ $responce = [
357
+ 'remain' => $remain,
358
+ 'end' => false,
359
+ 'optimized_count' => $optimized_count,
360
+ 'last_optimized' => $image_statistics->get_last_optimized_image( $last_optimized->id ),
361
+ 'statistic' => $image_statistics->load(),
362
+ ];
363
+
364
+ return $responce;
365
+ }
366
+
367
+ /**
368
+ * Отложенная оптимизация. Этап 2: получение данных с сервера потимизации
369
+ *
370
+ * @return bool|array
371
+ */
372
+ protected function processDeferredOptimization() {
373
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
374
+ $limit = 1;
375
+ $image_data = $image_statistics->getDeferredUnoptimized( $limit );
376
+
377
+ if ( ! $image_data || ! isset( $image_data[0] ) ) {
378
+ return false;
379
+ }
380
+
381
+ $image_data = $image_data[0];
382
+ $cf_image = $this->getImage( $image_data->id, $image_data );
383
+ $optimized_data = $cf_image->deferredOptimization();
384
+
385
+ if ( $optimized_data ) {
386
+ $image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
387
+ $image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
388
+ $image_statistics->save();
389
+
390
+ $folder = $this->getFolder( $cf_image->get( 'folder_uid' ) );
391
+ $folder->reCountOptimizedFiles();
392
+ $this->saveFolders();
393
+
394
+ return $optimized_data;
395
+ }
396
+
397
+ return false;
398
+ }
399
+
400
+ /**
401
+ * Восстановление из резервной копии
402
+ *
403
+ * @param int $folder_uid Folder id.
404
+ * @param int $max_process_per_request кол-во аттачментов за 1 запуск
405
+ *
406
+ * @return array
407
+ */
408
+ public function restoreFolderFromBackup( $folder_uid, $max_process_per_request = 5 ) {
409
+ WRIO_Plugin::app()->updatePopulateOption( 'cron_running', false ); // останавливаем крон
410
+
411
+ global $wpdb;
412
+
413
+ $db_table = RIO_Process_Queue::table_name();
414
+ $optimized_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = 'cf_image' AND item_hash_alternative = %s AND result_status = 'success' LIMIT 1;", $folder_uid ) );
415
+ $optimized_images = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_type = 'cf_image' AND item_hash_alternative = %s AND result_status = 'success' LIMIT %d", $folder_uid, $max_process_per_request ) );
416
+
417
+ $images_count = 0;
418
+ if ( $optimized_images ) {
419
+ $images_count = count( $optimized_images );
420
+ }
421
+
422
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
423
+
424
+ // обработка
425
+ if ( $images_count ) {
426
+ foreach ( $optimized_images as $row ) {
427
+ $image_id = intval( $row->id );
428
+ $cf_image = $this->getImage( $image_id );
429
+ if ( $cf_image->isOptimized() ) {
430
+ $restored = $cf_image->restore();
431
+
432
+ if ( ! is_wp_error( $restored ) ) {
433
+ $optimization_data = $cf_image->getOptimizationData();
434
+ $optimized_size = $optimization_data->get_final_size();
435
+ $original_size = $optimization_data->get_original_size();
436
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
437
+ $image_statistics->deductFromField( 'original_size', $original_size );
438
+
439
+ $folder = $this->getFolder( $cf_image->get( 'folder_uid' ) );
440
+ $folder->reCountOptimizedFiles();
441
+
442
+ WRIO_Logger::error( sprintf( 'Failed to restore custom folder. Object info: %s', wp_json_encode( $cf_image ) ) );
443
+ }
444
+ }
445
+ }
446
+ $this->saveFolders();
447
+ $image_statistics->save();
448
+ }
449
+ $remain = $optimized_count - $images_count;
450
+
451
+ return [
452
+ 'remain' => $remain,
453
+ ];
454
+ }
455
+
456
+ /**
457
+ * Сбрасывает текущие ошибки оптимизации
458
+ * Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
459
+ *
460
+ * @return void
461
+ */
462
+ public function resetCurrentErrors() {
463
+ //do_action( 'wbcr/rio/multisite_current_blog' );
464
+ global $wpdb;
465
+
466
+ $db_table = RIO_Process_Queue::table_name();
467
+
468
+ $wpdb->update( $db_table, [ 'result_status' => 'unoptimized' ], [
469
+ 'item_type' => 'cf_image',
470
+ 'result_status' => 'error'
471
+ ] );
472
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
473
+ }
474
+
475
+ /**
476
+ * Хук возвращает ajax action для кнопки оптимизации.
477
+ *
478
+ * @param string $action
479
+ * @param string $type
480
+ *
481
+ * @return string
482
+ */
483
+ public function optimizeAjaxAction( $action, $type ) {
484
+ if ( $type == 'custom-folders' ) {
485
+ return 'wriop_process_cf_images';
486
+ }
487
+
488
+ return $action;
489
+ }
490
+
491
+ /**
492
+ * Хук возвращает ajax action для кнопки переоптимизации.
493
+ *
494
+ * @param string $action
495
+ * @param string $type
496
+ *
497
+ * @return string
498
+ */
499
+ public function reoptimizeAjaxAction( $action, $type ) {
500
+ if ( $type == 'custom-folders' ) {
501
+ return 'wio_cf_reoptimize_image';
502
+ }
503
+
504
+ return $action;
505
+ }
506
+
507
+ /**
508
+ * Хук возвращает ajax action для кнопки восстановления.
509
+ *
510
+ * @param string $action
511
+ * @param string $type
512
+ *
513
+ * @return string
514
+ */
515
+ public function restoreAjaxAction( $action, $type ) {
516
+ if ( $type == 'custom-folders' ) {
517
+ return 'wio_cf_restore_image';
518
+ }
519
+
520
+ return $action;
521
+ }
522
+
523
+ /**
524
+ * Возвращает блок оптимизации.
525
+ *
526
+ * @param int $image_id
527
+ *
528
+ * @return string
529
+ */
530
+ public function getMediaColumnContent( $image_id ) {
531
+ $media_library = WRIO_Media_Library::get_instance();
532
+ $params = $this->calculateParams( $image_id );
533
+
534
+ return $media_library->getMediaColumnTemplate( $params, 'custom-folders' );
535
+ }
536
+
537
+ /**
538
+ * Просчитывает параметры блока оптимизации
539
+ *
540
+ * @param int $image_id номер изображения nextgen
541
+ *
542
+ * @return array $params
543
+ */
544
+ public function calculateParams( $image_id ) {
545
+ $cf_image = new WRIO_Folder_Image( $image_id );
546
+ $isOptimized = $cf_image->isOptimized();
547
+ $diff_percent = 0;
548
+ $diff_percent_all = 0;
549
+ $original_main_size = 0;
550
+ $attachment_file_size = 0;
551
+ $optimized_size = 0;
552
+ $original_size = 0;
553
+ $optimization_level = '';
554
+ $error_msg = '';
555
+ $backuped = '';
556
+ if ( $isOptimized ) {
557
+ $optimization_data = $cf_image->getOptimizationData();
558
+ /**
559
+ * @var WRIO_CF_Image_Extra_Data $extra_data
560
+ */
561
+ $extra_data = $optimization_data->get_extra_data();
562
+ $optimization_level = $optimization_data->get_processing_level();
563
+ $original_main_size = $optimization_data->get_original_size();
564
+ $attachment_file_size = $optimization_data->get_final_size();
565
+ $optimized_size = $optimization_data->get_final_size();
566
+ $original_size = $optimization_data->get_original_size();
567
+ $backuped = $optimization_data->get_is_backed_up();
568
+ $error_msg = $extra_data->get_error_msg();
569
+ if ( $attachment_file_size and $original_main_size ) {
570
+ $diff_percent = round( ( $original_main_size - $attachment_file_size ) * 100 / $original_main_size );
571
+ }
572
+ if ( $optimized_size and $original_size ) {
573
+ $diff_percent_all = round( ( $original_size - $optimized_size ) * 100 / $original_size );
574
+ }
575
+ }
576
+ $params = [
577
+ 'attachment_id' => $image_id,
578
+ 'is_optimized' => $isOptimized,
579
+ 'attach_dimensions' => 0,
580
+ 'attachment_file_size' => $attachment_file_size,
581
+ 'optimized_size' => $optimized_size,
582
+ 'original_size' => $original_size,
583
+ 'original_main_size' => $original_main_size,
584
+ 'thumbnails_optimized' => 0,
585
+ 'optimization_level' => $optimization_level,
586
+ 'error_msg' => $error_msg,
587
+ 'backuped' => $backuped,
588
+ 'diff_percent' => $diff_percent,
589
+ 'diff_percent_all' => $diff_percent_all,
590
+ 'is_skipped' => false,
591
+ ];
592
+
593
+ return $params;
594
+ }
595
+
596
+ /**
597
+ * Возвращает процент оптимизации
598
+ * Фильтр wbcr/rio/optimize_template/optimized_percent
599
+ *
600
+ * @param int $percent процент оптимизации
601
+ * @param string $type тип страницы
602
+ *
603
+ * @return int процент оптимизации
604
+ */
605
+ public function optimizedPercent( $percent, $type ) {
606
+ if ( 'custom-folders' == $type ) {
607
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
608
+
609
+ return $image_statistics->getOptimizedPercent();
610
+ }
611
+
612
+ return $percent;
613
+ }
614
+
615
+ /**
616
+ * Сохраняет WebP размер для cf_image
617
+ *
618
+ * @param RIO_Process_Queue $queue_model
619
+ *
620
+ * @return bool
621
+ */
622
+ public function webpSuccess( $queue_model ) {
623
+ if ( ! class_exists( 'WRIO\WEBP\Listener' ) ) {
624
+ return false; // если не установлена премиум версия, то WebP не активен
625
+ }
626
+ if ( $queue_model->get_item_type() !== WRIO\WEBP\Listener::DEFAULT_TYPE ) {
627
+ return false;
628
+ }
629
+ if ( $queue_model->get_result_status() !== RIO_Process_Queue::STATUS_SUCCESS ) {
630
+ return false;
631
+ }
632
+ /**
633
+ * @var RIOP_WebP_Extra_Data $extra_data
634
+ */
635
+ $extra_data = $queue_model->get_extra_data();
636
+ $item_type = $extra_data->get_convert_from();
637
+ if ( $item_type != 'cf_image' ) {
638
+ return false;
639
+ }
640
+ $optimization_data = RIO_Process_Queue::find_by_hash( $queue_model->get_item_hash_alternative() );
641
+ if ( ! $optimization_data ) {
642
+ return false;
643
+ }
644
+ /**
645
+ * @var WRIO_CF_Image_Extra_Data $extra_data
646
+ */
647
+ $extra_data = $optimization_data->get_extra_data();
648
+ if ( ! $extra_data ) {
649
+ return false;
650
+ }
651
+ $extra_data->set_webp_main_size( $queue_model->get_final_size() );
652
+ $optimization_data->set_extra_data( $extra_data );
653
+ add_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
654
+ $optimization_data->save();
655
+ remove_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
656
+
657
+ return true;
658
+ }
659
+ }
libs/addons/includes/classes/class.folder-image.php ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с custom folder image.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Folder_Image {
16
+
17
+ /**
18
+ * @var int номер картинки в таблице
19
+ */
20
+ private $id;
21
+
22
+ /**
23
+ * @var string путь к картинке относительно папки
24
+ */
25
+ private $path;
26
+
27
+ /**
28
+ * @var string УРЛ
29
+ */
30
+ private $url;
31
+
32
+ /**
33
+ * @var string уникальный идентификатор директории
34
+ */
35
+ private $folder_uid;
36
+
37
+ /**
38
+ * @var RIO_Process_Queue данные по оптимизации
39
+ */
40
+ private $optimization_data;
41
+
42
+ /**
43
+ * Инициализация картинки из custom folder
44
+ *
45
+ * @param int $image_id номер картинки в таблице folders
46
+ * @param array|false $image_data метаданные картинки
47
+ */
48
+ public function __construct( $image_id, $image_data = false ) {
49
+ $this->id = $image_id;
50
+
51
+ if ( $image_data instanceof RIO_Process_Queue ) {
52
+ $this->optimization_data = $image_data;
53
+ } else {
54
+ $this->optimization_data = $this->createOptimizationData();
55
+
56
+ if ( $image_data ) {
57
+ $this->optimization_data->configure( (array) $image_data );
58
+ } else {
59
+ $this->loadOptimizationData();
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @var WRIO_CF_Image_Extra_Data $extra_data
65
+ */
66
+ $extra_data = $this->optimization_data->get_extra_data();
67
+ $this->path = wp_normalize_path( ABSPATH . $extra_data->get_file_path() );
68
+ $this->url = home_url( wp_normalize_path( $extra_data->get_file_path() ) );
69
+ $this->folder_uid = $this->optimization_data->get_item_hash_alternative();
70
+ }
71
+
72
+ /**
73
+ * Возвращает свойство аттачмента
74
+ *
75
+ * @param string $property имя свойства
76
+ *
77
+ * @return mixed
78
+ */
79
+ public function get( $property ) {
80
+ if ( isset( $this->$property ) ) {
81
+ return $this->$property;
82
+ }
83
+
84
+ return false;
85
+ }
86
+
87
+ /**
88
+ * Возвращает данные по оптимизации
89
+ *
90
+ * @return RIO_Process_Queue
91
+ */
92
+ public function getOptimizationData() {
93
+ if ( empty( $this->optimization_data ) ) {
94
+ $this->optimization_data = $this->createOptimizationData();
95
+ $this->optimization_data->load();
96
+ }
97
+
98
+ return $this->optimization_data;
99
+ }
100
+
101
+ /**
102
+ * Создаёт новый объект RIO_Process_Queue
103
+ *
104
+ * @return RIO_Process_Queue
105
+ */
106
+ public function createOptimizationData() {
107
+ return new RIO_Process_Queue( [
108
+ 'id' => $this->id,
109
+ 'item_type' => 'cf_image',
110
+ ] );
111
+ }
112
+
113
+
114
+ protected function loadOptimizationData() {
115
+ global $wpdb;
116
+
117
+ if ( empty( $this->optimization_data ) ) {
118
+ $this->optimization_data = $this->createOptimizationData();
119
+ }
120
+
121
+ $table_name = RIO_Process_Queue::table_name();
122
+ $sql = $wpdb->prepare( "SELECT * FROM {$table_name} WHERE id = %d AND item_type = %s LIMIT 1;", [
123
+ $this->id,
124
+ 'cf_image',
125
+ ] );
126
+
127
+ $row = $wpdb->get_row( $sql );
128
+
129
+ if ( ! empty( $row ) ) {
130
+ $this->optimization_data->configure( $row );
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Проверка на оптимизацию изображения
136
+ *
137
+ * @return bool
138
+ */
139
+ public function isOptimized() {
140
+ $optimization_data = $this->getOptimizationData();
141
+ if ( empty( $optimization_data ) ) {
142
+ return false;
143
+ }
144
+ if ( $optimization_data->is_optimized() ) {
145
+ return true;
146
+ }
147
+
148
+ return false;
149
+ }
150
+
151
+ /**
152
+ * Check whether file exists or not.
153
+ *
154
+ * @return bool
155
+ */
156
+ public function isFileExists() {
157
+ if ( file_exists( $this->path ) ) {
158
+ return true;
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ /**
165
+ * Optimize folder image.
166
+ *
167
+ * @param string $optimization_level Level of optimization.
168
+ *
169
+ * @return array
170
+ */
171
+ public function optimize( $optimization_level = '' ) {
172
+ $is_image_backuped = $this->backup();
173
+
174
+ if ( is_wp_error( $is_image_backuped ) ) {
175
+
176
+ WRIO_Logger::error( sprintf( 'Failed to backup with message: %s. Skipping optimization of custom folder', $is_image_backuped->get_error_message() ) );
177
+
178
+ return [
179
+ 'errors_count' => 1,
180
+ 'original_size' => 0,
181
+ 'optimized_size' => 0,
182
+ 'optimized_count' => 0,
183
+ ];
184
+ }
185
+ // делаем рисайз
186
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
187
+ if ( ! $optimization_level ) {
188
+ $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
189
+ }
190
+ if ( $optimization_level == 'custom' ) {
191
+ $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
192
+ $optimization_level = intval( $custom_quality );
193
+ }
194
+ $optimization_data = $this->getOptimizationData();
195
+ $results = [];
196
+ $results['processing_level'] = $optimization_level;
197
+ $main_file_path = $this->path;
198
+ $main_file_url = $this->url;
199
+ clearstatcache(); // на всякий случай очистим кеш файловой статистики
200
+ $original_main_size = filesize( $main_file_path ); // оптимизированный размер только главной картинки
201
+
202
+ $optimized_img_data = $image_processor->process( [
203
+ 'image_url' => $main_file_url,
204
+ 'image_path' => $main_file_path,
205
+ 'quality' => $image_processor->quality( $optimization_level ),
206
+ 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
207
+ ] );
208
+
209
+ if ( is_wp_error( $optimized_img_data ) ) {
210
+ $results['result_status'] = 'error';
211
+ /**
212
+ * @var $extra_data WRIO_CF_Image_Extra_Data
213
+ */
214
+ $extra_data = $optimization_data->get_extra_data();
215
+ $extra_data->set_error( 'optimization' );
216
+ $extra_data->set_error_msg( $optimized_img_data->get_error_message() );
217
+ $results['extra_data'] = $extra_data;
218
+ $optimization_data->configure( $results );
219
+ $optimization_data->save();
220
+
221
+ return [
222
+ 'errors_count' => 1,
223
+ 'original_size' => 0,
224
+ 'optimized_size' => 0,
225
+ 'optimized_count' => 0,
226
+ ];
227
+ }
228
+
229
+ // отложенная оптимизация
230
+ if ( isset( $optimized_img_data['status'] ) && $optimized_img_data['status'] == 'processing' ) {
231
+ $results['result_status'] = 'processing';
232
+ $results['is_backed_up'] = $is_image_backuped;
233
+ $results['original_size'] = 0;
234
+ $results['final_size'] = 0;
235
+
236
+ /**
237
+ * @var $extra_data WRIO_CF_Image_Extra_Data
238
+ */
239
+ $extra_data = $optimization_data->get_extra_data();
240
+ $extra_data->set_main_optimized_data( $optimized_img_data );
241
+ $results['extra_data'] = $extra_data;
242
+ $optimization_data->configure( $results );
243
+ $optimization_data->save();
244
+
245
+ return [
246
+ 'processing' => 1,
247
+ 'original_size' => 0,
248
+ 'optimized_size' => 0,
249
+ ];
250
+ }
251
+
252
+ $this->replaceOriginalFile( $optimized_img_data );
253
+
254
+ // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
255
+ if ( ! $optimized_img_data['optimized_size'] ) {
256
+ clearstatcache();
257
+ $optimized_img_data['optimized_size'] = filesize( $main_file_path );
258
+ }
259
+ // при отрицательной оптимизации ставим значение оригинала
260
+ if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
261
+ $optimized_img_data['optimized_size'] = $original_main_size;
262
+ }
263
+
264
+ $original_size = $original_main_size;
265
+ $optimized_size = $optimized_img_data['optimized_size'];
266
+
267
+ $results['result_status'] = 'success';
268
+ $results['final_size'] = $optimized_size;
269
+ $results['original_size'] = $original_size;
270
+ $results['is_backed_up'] = $is_image_backuped;
271
+
272
+ /**
273
+ * @var $extra_data WRIO_CF_Image_Extra_Data
274
+ */
275
+ $extra_data = $optimization_data->get_extra_data();
276
+ $extra_data->set_main_optimized_data( null );
277
+ $extra_data->set_error( null );
278
+ $extra_data->set_error_msg( null );
279
+ $results['extra_data'] = $extra_data;
280
+ $mime_type = '';
281
+ if ( function_exists( 'wp_get_image_mime' ) ) {
282
+ $mime_type = wp_get_image_mime( $main_file_path );
283
+ }
284
+ $results['original_mime_type'] = $mime_type;
285
+ $results['final_mime_type'] = $mime_type;
286
+ $optimization_data->configure( $results );
287
+ $optimization_data->save();
288
+
289
+ return [
290
+ 'errors_count' => 0,
291
+ 'original_size' => $original_size,
292
+ 'optimized_size' => $optimized_size,
293
+ 'optimized_count' => 1,
294
+ ];
295
+ }
296
+
297
+ /**
298
+ * Отложенная оптимизация аттачмента
299
+ *
300
+ * @return bool|array
301
+ */
302
+ public function deferredOptimization() {
303
+ $results = [
304
+ 'original_size' => 0,
305
+ 'optimized_size' => 0,
306
+ 'optimized_count' => 0,
307
+ 'processing' => 1,
308
+ ];
309
+
310
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
311
+ $optimization_data = $this->getOptimizationData();
312
+
313
+ if ( $optimization_data->get_result_status() != 'processing' ) {
314
+ return false;
315
+ }
316
+ // проверяем главную картинку
317
+ /**
318
+ * @var $extra_data WRIO_CF_Image_Extra_Data
319
+ */
320
+ $extra_data = $optimization_data->get_extra_data();
321
+ $main_optimized_data = $extra_data->get_main_optimized_data();
322
+ $main_image_url = '';
323
+ if ( ! $main_optimized_data['optimized_img_url'] ) {
324
+ $main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
325
+ if ( $main_image_url ) {
326
+ $main_optimized_data['optimized_img_url'] = $main_image_url;
327
+ $extra_data->set_main_optimized_data( $main_optimized_data );
328
+ }
329
+ }
330
+
331
+ $thumbnails_processed = true; // для кастомных папок нет превьюшек, поэтому всегда true
332
+
333
+ // когда все файлы получены - сохраняем и возвращаем результат
334
+ if ( $main_image_url && $thumbnails_processed ) {
335
+ $original_size = 0;
336
+ $optimized_size = 0;
337
+ $original_main_size = filesize( $this->get( 'path' ) );
338
+ $original_size = $original_size + $original_main_size;
339
+ $this->replaceOriginalFile( [
340
+ 'optimized_img_url' => $main_image_url,
341
+ ] );
342
+ clearstatcache();
343
+ $optimized_main_size = filesize( $this->get( 'path' ) );
344
+
345
+ // при отрицательной оптимизации ставим значение оригинала
346
+ if ( $optimized_main_size > $original_main_size ) {
347
+ $optimized_main_size = $original_main_size;
348
+ }
349
+
350
+ $optimized_size = $optimized_size + $optimized_main_size;
351
+ clearstatcache();
352
+ $mime_type = '';
353
+
354
+ if ( function_exists( 'wp_get_image_mime' ) ) {
355
+ $mime_type = wp_get_image_mime( $this->get( 'path' ) );
356
+ }
357
+
358
+ $optimization_data->configure( [
359
+ 'final_size' => $optimized_size,
360
+ 'original_size' => $original_size,
361
+ 'result_status' => 'success',
362
+ 'original_mime_type' => $mime_type,
363
+ 'final_mime_type' => $mime_type,
364
+ ] );
365
+ $extra_data->set_original_main_size( $original_main_size );
366
+
367
+ // удаляем промежуточные данные
368
+ $extra_data->set_main_optimized_data( null );
369
+ $extra_data->set_error( null );
370
+ $extra_data->set_error_msg( null );
371
+
372
+ $results['optimized_count'] = 1;
373
+ $results['original_size'] = $original_size;
374
+ $results['optimized_size'] = $optimized_size;
375
+ unset( $results['processing'] );
376
+ }
377
+
378
+ $optimization_data->set_extra_data( $extra_data );
379
+ $optimization_data->save();
380
+
381
+ return $results;
382
+ }
383
+
384
+ /**
385
+ * Заменяет оригинальный файл на оптимизированный
386
+ *
387
+ * @param array $optimized_img_data результат оптимизации ввиде массива данных
388
+ */
389
+ public function replaceOriginalFile( $optimized_img_data ) {
390
+ $optimized_img_url = $optimized_img_data['optimized_img_url'];
391
+ if ( isset( $optimized_img_data['not_need_download'] ) and $optimized_img_data['not_need_download'] ) {
392
+ $optimized_file = $optimized_img_url;
393
+ } else {
394
+ $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
395
+ }
396
+ if ( isset( $optimized_img_data['not_need_replace'] ) and $optimized_img_data['not_need_replace'] ) {
397
+ // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
398
+ return true;
399
+ }
400
+ if ( ! $optimized_file ) {
401
+ return false;
402
+ }
403
+ $path = $this->path;
404
+
405
+ if ( ! is_file( $path ) ) {
406
+ return false;
407
+ }
408
+
409
+ file_put_contents( $path, $optimized_file );
410
+
411
+ return true;
412
+ }
413
+
414
+ /**
415
+ * Загрузка картинки с удалённого сервера
416
+ *
417
+ * todo: RIO-18 можем ли мы создать универсальный метод для всех внешних запросов, чтобы не дублировать код?
418
+ *
419
+ * @param string $url
420
+ *
421
+ * @return string
422
+ */
423
+ protected function remoteDownloadImage( $url ) {
424
+ if ( ! function_exists( 'curl_version' ) ) {
425
+ return file_get_contents( $url );
426
+ }
427
+
428
+ $ch = curl_init();
429
+ curl_setopt( $ch, CURLOPT_HEADER, 0 );
430
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
431
+ curl_setopt( $ch, CURLOPT_URL, $url );
432
+
433
+ $image_body = curl_exec( $ch );
434
+ $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
435
+ if ( $http_code != '200' ) {
436
+ $image_body = false;
437
+ }
438
+ curl_close( $ch );
439
+
440
+ return $image_body;
441
+ }
442
+
443
+ /**
444
+ * Делает резервную копию изображения
445
+ */
446
+ public function backup() {
447
+ $backup = WRIOP_Backup::get_instance();
448
+
449
+ return $backup->backupCFImage( $this );
450
+ }
451
+
452
+ /**
453
+ * Восстанавливает из резервной копии
454
+ */
455
+ public function restore() {
456
+ $backup = WRIOP_Backup::get_instance();
457
+ $restored = $backup->restoreCFImage( $this );
458
+
459
+ if ( is_wp_error( $restored ) ) {
460
+ return $restored;
461
+ }
462
+
463
+ $optimization_data = $this->getOptimizationData();
464
+ $optimization_data->set_result_status( 'unoptimized' );
465
+ $optimization_data->save();
466
+
467
+ /**
468
+ * Хук срабатывает после восстановления cf_image
469
+ *
470
+ * @since 1.2.0
471
+ *
472
+ * @param RIO_Process_Queue $optimization_data
473
+ *
474
+ */
475
+ do_action( 'wbcr/rio/cf_image_restored', $this->optimization_data );
476
+
477
+ return true;
478
+ }
479
+ }
libs/addons/includes/classes/class.folder.php ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с кастомными папками.
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Folder {
16
+
17
+ /**
18
+ * @var string Folder path.
19
+ */
20
+ protected $path = '';
21
+
22
+ /**
23
+ * @var string SHA256 folder's path hash.
24
+ */
25
+ protected $uid = '';
26
+
27
+ /**
28
+ * @var int Number of files in current folder.
29
+ */
30
+ protected $files_count = 0;
31
+
32
+ /**
33
+ * @var int Number of optimized files in current folder.
34
+ */
35
+ protected $optimized_count = 0;
36
+
37
+ /**
38
+ * @var int Number of errors in current folder.
39
+ */
40
+ protected $errors_count = 0;
41
+
42
+ /**
43
+ * WRIO_Folder constructor.
44
+ *
45
+ * @param array $params List of params set on the model.
46
+ */
47
+ public function __construct( $params = [] ) {
48
+ // нужна проверка пути
49
+ foreach ( $params as $key => $value ) {
50
+ $this->set( $key, $value );
51
+ }
52
+ if ( ! $this->uid ) {
53
+ $this->uid = hash( 'sha256', $this->path );
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Get specified object's property.
59
+ *
60
+ * @param string $property_name Property name.
61
+ *
62
+ * @return bool
63
+ */
64
+ public function get( $property_name ) {
65
+ if ( isset( $this->$property_name ) ) {
66
+ return $this->$property_name;
67
+ }
68
+
69
+ return false;
70
+ }
71
+
72
+ /**
73
+ * Set specified object's property.
74
+ *
75
+ * @param string $property_name Property name.
76
+ * @param mixed $value Value to set.
77
+ */
78
+ public function set( $property_name, $value ) {
79
+ if ( isset( $this->$property_name ) ) {
80
+ $this->$property_name = $value;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Convert object to associative array form.
86
+ *
87
+ * @return array
88
+ */
89
+ public function toArray() {
90
+ return [
91
+ 'path' => $this->path,
92
+ 'files_count' => $this->files_count,
93
+ 'uid' => $this->uid,
94
+ 'optimized_count' => $this->optimized_count,
95
+ 'errors_count' => $this->errors_count,
96
+ ];
97
+ }
98
+
99
+ /**
100
+ * Обходит каталог и добавляет в индекс файлы
101
+ *
102
+ * @param mixed $offset отступ от начала
103
+ * @param mixed $max_process_elements кол-во элементов за итерацию
104
+ *
105
+ * @return int Кол-во элементов, обработанных при индексировании
106
+ */
107
+ public function indexing( $offset = 0, $max_process_elements = 100 ) {
108
+ global $wpdb;
109
+
110
+ $iterator = $this->getRecursiveIterator();
111
+ $allowed = $this->getAllowedFilesExt();
112
+ $files = [];
113
+ $db_table = RIO_Process_Queue::table_name();
114
+
115
+ foreach ( $iterator as $file ) {
116
+ $ext = substr( $file, strrpos( strtolower( $file ), '.' ) + 1 ); // получаем расширение файла
117
+ if ( in_array( strtolower( $ext ), $allowed ) ) {
118
+ // сделать путь относительно корня
119
+ $files[] = $this->realPathToRelative( $file->getPathname() );
120
+ }
121
+ }
122
+
123
+ $files = array_slice( $files, $offset, $max_process_elements );
124
+ foreach ( $files as $file ) {
125
+ $file = wp_normalize_path( $file );
126
+ //$file_path = str_replace( $this->path, '', $file );
127
+ $file_uid = hash( 'sha256', str_replace( wp_normalize_path( ABSPATH ), '', $file ) );
128
+ $sql = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_hash_alternative = %s AND item_hash = %s;", $this->uid, $file_uid );
129
+ $row = $wpdb->get_row( $sql );
130
+
131
+ if ( empty( $row ) ) {
132
+ // если файла нет в индексе - добавляем
133
+ $extra_data = new WRIO_CF_Image_Extra_Data( [
134
+ 'file_path' => $file,
135
+ 'folder_relative_path' => $this->path,
136
+ ] );
137
+ $optimization_data = new RIO_Process_Queue( [
138
+ 'item_type' => 'cf_image',
139
+ 'item_hash' => $file_uid, // хэш пути к файлу
140
+ 'item_hash_alternative' => $this->uid, // хэш директории будет сделан сеттером
141
+ 'original_size' => 0,
142
+ 'final_size' => 0,
143
+ 'original_mime_type' => '',
144
+ 'final_mime_type' => '',
145
+ 'result_status' => 'unoptimized',
146
+ 'processing_level' => '',
147
+ 'extra_data' => $extra_data,
148
+ ] );
149
+ $optimization_data->save();
150
+ } else {
151
+ // делаем апдейт и выставляем ласт индекс дату. Потом у кого в индексе ласт индекс дата меньше заданной, того уже нет на диске
152
+ }
153
+ }
154
+
155
+ return count( $files ); // сколько элементов обработано при индексировании.
156
+ }
157
+
158
+ /**
159
+ * Проверяет индекс на наличие несуществующих файлов.
160
+ *
161
+ * Находит файлы, которые пользователь удалил из папки, но в индексе они ещё есть.
162
+ * Удаляет из из индекса, вычитает из статистики
163
+ *
164
+ * @param mixed $offset отсутп от начала
165
+ * @param mixed $max_process_elements кол-во элементов за итерацию
166
+ *
167
+ * @return int $processed Кол-во обработанных записей. Используется для расчёта отступа
168
+ */
169
+ public function syncIndex( $offset = 0, $max_process_elements = 100 ) {
170
+ global $wpdb;
171
+ $db_table = RIO_Process_Queue::table_name();
172
+ $sql = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_hash_alternative = %s LIMIT %d OFFSET %d;", $this->uid, $max_process_elements, $offset );
173
+ $rows = $wpdb->get_results( $sql );
174
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
175
+ $processed = 0;
176
+ $deleted = 0;
177
+
178
+ if ( ! empty( $rows ) ) {
179
+ foreach ( $rows as $row ) {
180
+ $processed ++;
181
+ $cf_image = new WRIO_Folder_Image( $row->id, $row );
182
+ if ( ! $cf_image->isFileExists() ) {
183
+ if ( $cf_image->isOptimized() ) {
184
+ // если файл оптимизирован - вычитаем из статистики
185
+ $optimization_data = $cf_image->getOptimizationData();
186
+ $optimized_size = $optimization_data->get_final_size();
187
+ $original_size = $optimization_data->get_original_size();
188
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
189
+ $image_statistics->deductFromField( 'original_size', $original_size );
190
+ }
191
+ // если файла нет на диске - удаляем из индекса
192
+ $wpdb->delete( $db_table, [
193
+ 'id' => $cf_image->get( 'id' ),
194
+ ], [ '%d' ] );
195
+ $deleted ++;
196
+ }
197
+ }
198
+ $image_statistics->save();
199
+ $this->reCountOptimizedFiles();
200
+ }
201
+ $processed = $processed - $deleted;
202
+
203
+ return $processed;
204
+ }
205
+
206
+ /**
207
+ * Get allowed list of extensions.
208
+ *
209
+ * @return array
210
+ */
211
+ public function getAllowedFilesExt() {
212
+ $allowed = [ 'jpg', 'jpeg', 'png' ];
213
+
214
+ return $allowed;
215
+ }
216
+
217
+ /**
218
+ * Count number of files in a folder.
219
+ *
220
+ * @return int
221
+ */
222
+ public function countFiles() {
223
+ $iterator = $this->getRecursiveIterator();
224
+ $allowed = $this->getAllowedFilesExt();
225
+ $count = 0;
226
+ foreach ( $iterator as $file ) {
227
+ $ext = substr( $file, strrpos( strtolower( $file ), '.' ) + 1 ); // получаем расширение файла
228
+ if ( in_array( strtolower( $ext ), $allowed ) ) {
229
+ $count ++;
230
+ }
231
+ }
232
+
233
+ return $count;
234
+ }
235
+
236
+ /**
237
+ * Count number of indexes files.
238
+ *
239
+ * @return null|string
240
+ */
241
+ public function countIndexedFiles() {
242
+ global $wpdb;
243
+ $db_table = RIO_Process_Queue::table_name();
244
+ $sql_files = $wpdb->prepare( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = %s AND item_hash_alternative = %s;", 'cf_image', $this->uid );
245
+ $files_count = $wpdb->get_var( $sql_files );
246
+
247
+ return $files_count;
248
+ }
249
+
250
+ public function reCountFiles() {
251
+ $this->files_count = $this->countFiles();
252
+
253
+ return $this->files_count;
254
+ }
255
+
256
+ /**
257
+ * Recount optimized files.
258
+ *
259
+ * @return int|null|string
260
+ */
261
+ public function reCountOptimizedFiles() {
262
+ global $wpdb;
263
+ $db_table = RIO_Process_Queue::table_name();
264
+ $sql = "SELECT COUNT(*) FROM {$db_table} WHERE item_type = %s AND result_status = %s AND item_hash_alternative = %s;";
265
+ $sql_prepared = $wpdb->prepare( $sql, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS, $this->uid );
266
+ $optimized_count = $wpdb->get_var( $sql_prepared );
267
+ $this->optimized_count = $optimized_count;
268
+
269
+ return $this->optimized_count;
270
+ }
271
+
272
+ /**
273
+ * Remove folder and deduct its size from optimized and original size.
274
+ */
275
+ public function remove() {
276
+ // удаляем файлы из индекса и переситываем стату
277
+ global $wpdb;
278
+ $db_table = RIO_Process_Queue::table_name();
279
+ $sql = "SELECT SUM(original_size) AS original_size, SUM(final_size) AS optimized_size FROM {$db_table} WHERE item_type = %s AND result_status = %s AND item_hash_alternative = %s";
280
+ $prepared_sql = $wpdb->prepare( $sql, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS, $this->uid );
281
+ $sum = $wpdb->get_row( $prepared_sql );
282
+
283
+ // Deduct from statistics
284
+ $image_statistics = WRIO_Image_Statistic_Folders::get_instance();
285
+ $image_statistics->deductFromField( 'optimized_size', $sum->optimized_size );
286
+ $image_statistics->deductFromField( 'original_size', $sum->original_size );
287
+ $image_statistics->save();
288
+
289
+ // Delete from db
290
+ $wpdb->delete( $db_table, [
291
+ 'item_hash_alternative' => $this->uid,
292
+ 'item_type' => 'cf_image',
293
+ ], [ '%s', '%s' ] );
294
+ }
295
+
296
+ /**
297
+ * Get absolute path from relative.
298
+ *
299
+ * @return bool|string
300
+ */
301
+ public function realPath() {
302
+ return realpath( ABSPATH . $this->path );
303
+ }
304
+
305
+ /**
306
+ * Возвращает путь к директории относительно корня сайта
307
+ * На входе: /home/user/test/wp.com/wp-content/uploads/custom-folder/
308
+ * На выходе: /wp-content/uploads/custom-folder/
309
+ *
310
+ * @param string $path Путь к директории. Может быть абсолютным.
311
+ *
312
+ * @return string $relative_path относительный путь
313
+ */
314
+ public function realPathToRelative( $path ) {
315
+ $relative_path = str_replace( untrailingslashit( ABSPATH ), '', $path );
316
+
317
+ return $relative_path;
318
+ }
319
+
320
+ /**
321
+ * Get iterator to go scan files in directory.
322
+ *
323
+ * @return RecursiveIteratorIterator
324
+ */
325
+ public function getRecursiveIterator() {
326
+ $iterator = new RecursiveDirectoryIterator( $this->realPath() );
327
+
328
+ return new RecursiveIteratorIterator( $iterator );
329
+ }
330
+ }
libs/addons/includes/classes/class.folders-list-table.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class WRIO_Folders_List_Table
5
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
6
+ */
7
+ class WRIO_Folders_List_Table extends WP_List_Table {
8
+
9
+ public function __construct() {
10
+ // Set parent defaults.
11
+ parent::__construct( array(
12
+ 'singular' => 'media', // Singular name of the listed records.
13
+ 'plural' => 'media', // Plural name of the listed records.
14
+ 'ajax' => false, // Does this table support ajax?
15
+ ) );
16
+ }
17
+
18
+ public function get_columns() {
19
+ $columns = array(
20
+ 'cb' => '<input type="checkbox" />', // Render a checkbox instead of text.
21
+ 'preview' => _x( 'Preview', 'Column label', 'robin-image-optimizer' ),
22
+ 'file' => _x( 'File path', 'Column label', 'robin-image-optimizer' ),
23
+ 'folder' => _x( 'Folder', 'Column label', 'robin-image-optimizer' ),
24
+ 'status' => _x( 'Status', 'Column label', 'robin-image-optimizer' ),
25
+ 'optimization' => _x( 'Optimization', 'Column label', 'robin-image-optimizer' )
26
+ );
27
+
28
+ return $columns;
29
+ }
30
+
31
+ public function prepare_items() {
32
+ global $wpdb;
33
+
34
+ $per_page = 20;
35
+
36
+ $hidden = array();
37
+ $columns = $this->get_columns();
38
+ $sortable = $this->get_sortable_columns();
39
+ $this->_column_headers = array( $columns, $hidden, $sortable );
40
+
41
+ $current_page = $this->get_pagenum();
42
+ $offset = ( $current_page - 1 ) * $per_page;
43
+
44
+ $folder_uid = WRIO_Plugin::app()->request->get( 'folder-filter', null, true );
45
+ $status = WRIO_Plugin::app()->request->get( 'status-filter', null, true );
46
+
47
+ if ( ! in_array( $status, array( 'success', 'unoptimized', 'error' ) ) ) {
48
+ $status = null;
49
+ }
50
+
51
+ $db_table = RIO_Process_Queue::table_name();
52
+
53
+ $sql_filter = '';
54
+
55
+ if ( ! empty( $status ) ) {
56
+ $sql_filter .= " AND result_status = '" . esc_sql( $status ) . "' ";
57
+ }
58
+
59
+ if ( ! empty( $folder_uid ) ) {
60
+ $sql_filter .= " AND item_hash_alternative = '" . esc_sql( $folder_uid ) . "' ";
61
+ }
62
+
63
+ $total_items = $wpdb->get_var( "
64
+ SELECT COUNT(*)
65
+ FROM {$db_table}
66
+ WHERE item_type = 'cf_image' {$sql_filter}" );
67
+
68
+ $rows = $wpdb->get_results( $wpdb->prepare( "
69
+ SELECT *
70
+ FROM {$db_table}
71
+ WHERE item_type = 'cf_image' {$sql_filter}
72
+ ORDER BY id DESC
73
+ LIMIT %d
74
+ OFFSET %d", $per_page, $offset ) );
75
+
76
+ if ( empty( $rows ) ) {
77
+ $this->items = array();
78
+
79
+ return;
80
+ }
81
+
82
+ foreach ( (array) $rows as $key => $row ) {
83
+ $rows[ $key ] = new RIO_Process_Queue( $row );
84
+ }
85
+
86
+ $this->items = $rows;
87
+
88
+ $this->set_pagination_args( array(
89
+ 'total_items' => $total_items, // WE have to calculate the total number of items.
90
+ 'per_page' => $per_page, // WE have to determine how many items to show on a page.
91
+ 'total_pages' => ceil( $total_items / $per_page ), // WE have to calculate the total number of pages.
92
+ ) );
93
+ }
94
+
95
+
96
+ public function display() {
97
+ $cf = WRIO_Custom_Folders::get_instance();
98
+ $folders = $cf->getFolders();
99
+
100
+ $folder_uid = WRIO_Plugin::app()->request->get( 'folder-filter', null, true );
101
+ $status = WRIO_Plugin::app()->request->get( 'status-filter', null, true );
102
+
103
+ if ( ! in_array( $status, array( 'success', 'unoptimized', 'error' ) ) ) {
104
+ $status = null;
105
+ }
106
+
107
+ $optimized_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'success' );
108
+ $unoptimized_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'unoptimized' );
109
+ $error_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'error' );
110
+
111
+ ?>
112
+ <div class="wrap wriop-files-list">
113
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
114
+ <form method="get" id="wriop-files-list-form" action="<?php echo admin_url( 'upload.php?page=rio-custom-media' ); ?>">
115
+ <input type="hidden" name="page" value="rio-custom-media"/>
116
+ <div class="wp-filter">
117
+ <div class="filter-items">
118
+ <label for="folder-filter" class="screen-reader-text"><?php _e( 'Filter by folder', 'robin-image-optimizer' ); ?></label>
119
+ <select class="folder-filters" name="folder-filter" id="folder-filter">
120
+ <option value="" selected="selected"><?php _e( 'All Folders', 'robin-image-optimizer' ); ?></option>
121
+ <?php if ( ! empty( $folders ) ): ?>
122
+ <?php foreach ( (array) $folders as $folder ): ?>
123
+ <option <?php selected( $folder_uid, $folder->get( 'uid' ) ); ?> value="<?php echo esc_attr( $folder->get( 'uid' ) ); ?>"><?php echo esc_attr( $folder->get( 'path' ) ); ?>
124
+ (<?php echo esc_attr( $folder->get( 'files_count' ) ); ?>)
125
+ </option>
126
+ <?php endforeach; ?>
127
+ <?php endif; ?>
128
+ </select>
129
+ <label for="status-filter" class="screen-reader-text"><?php _e( 'Filter by status', 'robin-image-optimizer' ); ?></label>
130
+ <select class="folder-filters" name="status-filter" id="status-filter">
131
+ <option value="" selected="selected"><?php _e( 'All Media Files', 'robin-image-optimizer' ); ?></option>
132
+ <option <?php selected( $status, 'success' ); ?> value="success"><?php _e( 'Optimized', 'robin-image-optimizer' ); ?>
133
+ (<?php echo esc_attr( $optimized_count ); ?>)
134
+ </option>
135
+ <option <?php selected( $status, 'unoptimized' ); ?> value="unoptimized"><?php _e( 'Unoptimized', 'robin-image-optimizer' ); ?>
136
+ (<?php echo esc_attr( $unoptimized_count ); ?>)
137
+ </option>
138
+ <option <?php selected( $status, 'error' ); ?> value="error"><?php _e( 'Errors', 'robin-image-optimizer' ); ?>
139
+ (<?php echo esc_attr( $error_count ); ?>)
140
+ </option>
141
+ </select>
142
+ <input type="submit" id="folders-query-submit" class="button" value="Filter">
143
+ </div>
144
+ </div>
145
+ <?php parent::display(); ?>
146
+ </form>
147
+ </div>
148
+ <?php
149
+ }
150
+
151
+ protected function get_sortable_columns() {
152
+ $sortable_columns = array();
153
+
154
+ return $sortable_columns;
155
+ }
156
+
157
+ protected function get_bulk_actions() {
158
+ $actions = array();
159
+
160
+ return $actions;
161
+ }
162
+
163
+ /**
164
+ * @param RIO_Process_Queue $item
165
+ *
166
+ * @return string
167
+ */
168
+ protected function column_cb( $item ) {
169
+ return sprintf( '<input type="checkbox" name="%1$s[]" value="%2$s" />', $this->_args['singular'], // Let's simply repurpose the table's singular label ("movie").
170
+ $item->get_id() // The value of the checkbox should be the record's ID.
171
+ );
172
+ }
173
+
174
+ /**
175
+ * @param RIO_Process_Queue $item
176
+ */
177
+ protected function column_preview( $item ) {
178
+
179
+ /** @var WRIO_CF_Image_Extra_Data $extra_data */
180
+ $extra_data = $item->get_extra_data();
181
+
182
+ if ( ! empty( $extra_data ) ) {
183
+ $file_relative_path = wp_normalize_path( $extra_data->get_file_path() );
184
+ $image_url = home_url( $file_relative_path );
185
+
186
+ printf( '
187
+ <span class="media-icon image-icon">
188
+ <a href="%s"><img src="%s" class="attachment-60x60 size-60x60" alt="" width="60" height="60"></a>
189
+ </span>', esc_url( $image_url ), esc_url( $image_url ) );
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @param RIO_Process_Queue $item
195
+ */
196
+ protected function column_file( $item ) {
197
+
198
+ /** @var WRIO_CF_Image_Extra_Data $extra_data */
199
+ $extra_data = $item->get_extra_data();
200
+
201
+ if ( ! empty( $extra_data ) ) {
202
+ $file_relative_path = wp_normalize_path( $extra_data->get_file_path() );
203
+ $file_name = wp_basename( $file_relative_path );
204
+ $image_url = home_url( $file_relative_path );
205
+
206
+ printf( '
207
+ <p class="filename">
208
+ <a href="%s">%s</a>
209
+ </p>', esc_url( $image_url ), $file_name );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * @param RIO_Process_Queue $item
215
+ */
216
+ protected function column_folder( $item ) {
217
+
218
+ /** @var WRIO_CF_Image_Extra_Data $extra_data */
219
+ $extra_data = $item->get_extra_data();
220
+
221
+ if ( ! empty( $extra_data ) ) {
222
+ $file_relative_path = wp_normalize_path( $extra_data->get_file_path() );
223
+ $file_name = wp_basename( $file_relative_path );
224
+ $folder = str_replace( $file_name, '', $file_relative_path );
225
+
226
+ printf( '<code>%s</code > ', $folder );
227
+ }
228
+ }
229
+
230
+ /**
231
+ * @param RIO_Process_Queue $item
232
+ */
233
+ protected function column_status( $item ) {
234
+ $statuses = array(
235
+ 'success' => __( 'Success', 'robin-image-optimizer' ),
236
+ 'error' => __( 'Error', 'robin-image-optimizer' ),
237
+ 'processing' => __( 'Processing', 'robin-image-optimizer' ),
238
+ 'unoptimized' => __( 'Unoptimized', 'robin-image-optimizer' ),
239
+ 'skip' => __( 'Skipped', 'robin-image-optimizer' ),
240
+ );
241
+ if ( isset( $statuses[ $item->get_result_status() ] ) ) {
242
+ echo esc_attr( $statuses[ $item->get_result_status() ] );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * @param RIO_Process_Queue $item
248
+ */
249
+ protected function column_optimization( $item ) {
250
+ $cf = WRIO_Custom_Folders::get_instance();
251
+ echo $cf->getMediaColumnContent( $item->get_id() );
252
+ }
253
+
254
+ protected function process_bulk_action() {
255
+ // Detect when a bulk action is being triggered.
256
+ if ( 'delete' === $this->current_action() ) {
257
+ wp_die( 'Items deleted(or they would be if we had items to delete)! ' );
258
+ }
259
+ }
260
+ }
libs/addons/includes/classes/class.gallery-nextgen.php ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с галереей nextgen
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Nextgen_Gallery {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * @var array $nextgen_images контейнер для хранения nextgen_images
28
+ */
29
+ private $nextgen_images = [];
30
+
31
+ /**
32
+ * Инициализация функционала nextgen галереи. Установка хуков
33
+ */
34
+ public function __construct() {
35
+ if ( ! wrio_is_active_nextgen_gallery() ) {
36
+ return;
37
+ }
38
+
39
+ require_once( WRIOP_PLUGIN_DIR . '/includes/classes/models/class.nextgen-extra-data.php' );
40
+ require_once( WRIOP_PLUGIN_DIR . '/includes/classes/class.image-nextgen.php' );
41
+ require_once( WRIOP_PLUGIN_DIR . '/admin/ajax/optimization.php' );
42
+
43
+ add_filter( 'ngg_manage_images_number_of_columns', [ $this, 'addColumns' ] );
44
+
45
+ add_action( 'wp_ajax_wio_ng_reoptimize_image', 'wbcr_riop_reoptimizeImage' );
46
+ add_action( 'wp_ajax_wio_ng_restore_image', 'wbcr_riop_restoreImage' );
47
+ add_action( 'wp_ajax_wio_process_ng_images', 'wbcr_riop_optimizeImages' );
48
+
49
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueueMeadiaScripts' ], 10 );
50
+ add_action( 'ngg_delete_picture', [ $this, 'deleteImageHook' ], 10, 2 );
51
+ add_action( 'ngg_recovered_image', [ $this, 'recoverImageHook' ], 10, 1 );
52
+
53
+ add_filter( 'wbcr/rio/optimize_template/reoptimize_ajax_action', [
54
+ $this,
55
+ 'reoptimizeAjaxAction',
56
+ ], 10, 2 );
57
+
58
+ add_filter( 'wbcr/rio/optimize_template/restore_ajax_action', [ $this, 'restoreAjaxAction' ], 10, 2 );
59
+ //add_filter( 'wbcr/rio/multisite_blogs', [ $this, 'multisiteBlogs' ], 10, 2 );
60
+ add_action( 'wbcr/rio/optimize_template/optimized_percent', [ $this, 'optimizedPercent' ], 10, 2 );
61
+ add_action( 'wbcr/riop/queue_item_saved', [ $this, 'webpSuccess' ] );
62
+ }
63
+
64
+ /**
65
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
66
+ * @since 1.3.0
67
+ *
68
+ * @return object|\WRIO_Nextgen_Gallery object Main instance.
69
+ */
70
+ public static function get_instance() {
71
+ if ( ! isset( static::$_instance ) ) {
72
+ static::$_instance = new static();
73
+ }
74
+
75
+ return static::$_instance;
76
+ }
77
+
78
+ /**
79
+ * Возвращает сайты, на которых установлен NextGen gallery
80
+ * Используется в мультисайт режиме
81
+ *
82
+ * @param array $blogs сайты
83
+ * @param string $type тип
84
+ *
85
+ * @return array сайты, на которых установлен nextgen
86
+ */
87
+ /*public function multisiteBlogs( $blogs, $type ) {
88
+ if ( 'nextgen' == $type ) {
89
+ $nextgen_basename = 'nextgen-gallery/nggallery.php';
90
+ if ( is_plugin_active_for_network( $nextgen_basename ) ) {
91
+ return $blogs;
92
+ } else {
93
+ $nextgen_blogs = [];
94
+ foreach ( $blogs as $blog ) {
95
+ switch_to_blog( intval( $blog->blog_id ) );
96
+ if ( is_plugin_active( $nextgen_basename ) ) {
97
+ $nextgen_blogs[] = $blog;
98
+ }
99
+ restore_current_blog();
100
+ }
101
+
102
+ return $nextgen_blogs;
103
+ }
104
+ }
105
+
106
+ return $blogs;
107
+ }*/
108
+
109
+ /**
110
+ * Хук возвращает ajax action для кнопки переоптимизации
111
+ */
112
+ public function reoptimizeAjaxAction( $action, $type ) {
113
+ if ( $type == 'nextgen' ) {
114
+ return 'wio_ng_reoptimize_image';
115
+ }
116
+
117
+ return $action;
118
+ }
119
+
120
+ /**
121
+ * Хук возвращает ajax action для кнопки восстановления
122
+ */
123
+ public function restoreAjaxAction( $action, $type ) {
124
+ if ( $type == 'nextgen' ) {
125
+ return 'wio_ng_restore_image';
126
+ }
127
+
128
+ return $action;
129
+ }
130
+
131
+ /**
132
+ * Добавляем стили и скрипты в медиабиблиотеку
133
+ */
134
+ public function enqueueMeadiaScripts( $hook ) {
135
+ if ( strpos( $hook, 'page_nggallery-manage-gallery' ) === false ) {
136
+ return;
137
+ }
138
+ wp_enqueue_style( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/css/media.css', [], WRIO_Plugin::app()->getPluginVersion() );
139
+ wp_enqueue_script( 'wio-install-addons', WRIO_PLUGIN_URL . '/admin/assets/js/single-optimization.js', [ 'jquery' ], WRIO_Plugin::app()->getPluginVersion() );
140
+ }
141
+
142
+ /**
143
+ * Добавляет колонку оптимизации в галерею nextgen
144
+ */
145
+ public function addColumns( $count ) {
146
+ $count ++;
147
+ add_filter( 'ngg_manage_images_column_' . $count . '_header', [ $this, 'columnTitle' ] );
148
+ add_filter( 'ngg_manage_images_column_' . $count . '_content', [ $this, 'columnContent' ], 10, 2 );
149
+
150
+ return $count;
151
+ }
152
+
153
+ /**
154
+ * Название колонки оптимизации в галерее
155
+ */
156
+ public function columnTitle() {
157
+ return __( 'Image optimizer', 'image optimizer' );
158
+ }
159
+
160
+ /**
161
+ * Возвращает содержимое блока оптимизации
162
+ */
163
+ public function columnContent( $output, $image ) {
164
+ $output = $this->getMediaColumnContent( $image->pid );
165
+
166
+ return $output;
167
+ }
168
+
169
+ /**
170
+ * Возвращает блок оптимизации
171
+ */
172
+ public function getMediaColumnContent( $image_id ) {
173
+
174
+ $media_library = WRIO_Media_Library::get_instance();
175
+ $params = $this->calculateParams( $image_id );
176
+
177
+ return $media_library->getMediaColumnTemplate( $params, 'nextgen' );
178
+ }
179
+
180
+ /**
181
+ * Просчитывает параметры блока оптимизации
182
+ *
183
+ * @param int $image_id номер изображения nextgen
184
+ *
185
+ * @return array $params
186
+ */
187
+ public function calculateParams( $image_id ) {
188
+ $nextgen_image = new WRIO_Image_Nextgen( $image_id );
189
+ $isOptimized = $nextgen_image->isOptimized();
190
+ $diff_percent = 0;
191
+ $diff_percent_all = 0;
192
+ $original_main_size = 0;
193
+ $attachment_file_size = 0;
194
+ $optimized_size = 0;
195
+ $original_size = 0;
196
+ $optimization_level = '';
197
+ $error_msg = '';
198
+ $backuped = '';
199
+ if ( $isOptimized ) {
200
+ $optimization_data = $nextgen_image->getOptimizationData();
201
+ /**
202
+ * @var WRIO_Nextgen_Extra_Data $extra_data
203
+ */
204
+ $extra_data = $optimization_data->get_extra_data();
205
+ $optimization_level = $optimization_data->get_processing_level();
206
+ $original_main_size = $extra_data->get_original_main_size();
207
+ $attachment_file_size = $optimization_data->get_final_size();
208
+ $optimized_size = $optimization_data->get_final_size();
209
+ $original_size = $optimization_data->get_original_size();
210
+ $backuped = $optimization_data->get_is_backed_up();
211
+ $error_msg = $extra_data->get_error_msg();
212
+ if ( $attachment_file_size and $original_main_size ) {
213
+ $diff_percent = round( ( $original_main_size - $attachment_file_size ) * 100 / $original_main_size );
214
+ }
215
+ if ( $optimized_size and $original_size ) {
216
+ $diff_percent_all = round( ( $original_size - $optimized_size ) * 100 / $original_size );
217
+ }
218
+ }
219
+ $params = [
220
+ 'attachment_id' => $image_id,
221
+ 'is_optimized' => $isOptimized,
222
+ 'attach_dimensions' => 0,
223
+ 'attachment_file_size' => $attachment_file_size,
224
+ 'optimized_size' => $optimized_size,
225
+ 'original_size' => $original_size,
226
+ 'original_main_size' => $original_main_size,
227
+ 'thumbnails_optimized' => 1,
228
+ 'optimization_level' => $optimization_level,
229
+ 'error_msg' => $error_msg,
230
+ 'backuped' => $backuped,
231
+ 'diff_percent' => $diff_percent,
232
+ 'diff_percent_all' => $diff_percent_all,
233
+ 'is_skipped' => false,
234
+ ];
235
+
236
+ return $params;
237
+ }
238
+
239
+ /**
240
+ * Возвращает объект nextgen_image
241
+ *
242
+ * @param int $image_id
243
+ * @param array $image_meta
244
+ *
245
+ * @return WRIO_Image_Nextgen
246
+ */
247
+ public function getNextgenImage( $image_id, $image_meta = false ) {
248
+ if ( ! isset( $this->nextgen_images[ $image_id ] ) ) {
249
+ $this->nextgen_images[ $image_id ] = new WRIO_Image_Nextgen( $image_id, $image_meta );
250
+ }
251
+
252
+ return $this->nextgen_images[ $image_id ];
253
+ }
254
+
255
+ /**
256
+ * Оптимизирует nextgen_image
257
+ *
258
+ * @param int $image_id номер картинки в таблице nextgen
259
+ * @param string $level качество
260
+ *
261
+ * @return array
262
+ */
263
+ public function optimizeNextgenImage( $image_id, $level = '' ) {
264
+ $nextgen_image = $this->getNextgenImage( $image_id );
265
+ $optimization_data = $nextgen_image->getOptimizationData();
266
+ if ( 'processing' == $optimization_data->get_result_status() ) {
267
+ return $this->deferredOptimizeNextgenImage( $image_id );
268
+ }
269
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
270
+ if ( $nextgen_image->isOptimized() ) {
271
+ $optimized_size = $optimization_data->get_final_size();
272
+ $original_size = $optimization_data->get_original_size();
273
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
274
+ $image_statistics->deductFromField( 'original_size', $original_size );
275
+ $nextgen_image->restore();
276
+ }
277
+ $image_optimized_data = $nextgen_image->optimize( $level );
278
+ $original_size = $image_optimized_data['original_size'];
279
+ $optimized_size = $image_optimized_data['optimized_size'];
280
+ $image_statistics->addToField( 'optimized_size', $optimized_size );
281
+ $image_statistics->addToField( 'original_size', $original_size );
282
+ $image_statistics->save();
283
+
284
+ return $image_optimized_data;
285
+ }
286
+
287
+ /**
288
+ * Отложенная оптимизация nextgen_image.
289
+ *
290
+ * @param int $image_id
291
+ *
292
+ * @return array|bool array on success, false on failure.
293
+ */
294
+ protected function deferredOptimizeNextgenImage( $image_id ) {
295
+ $nextgen_image = $this->getNextgenImage( $image_id );
296
+ $optimization_data = $nextgen_image->getOptimizationData();
297
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
298
+
299
+ // если текущий сервер оптимизации не поддерживает отложенную оптимизацию, а в очереди есть аттачменты - ставим им ошибку
300
+ if ( ! $image_processor->isDeferred() ) {
301
+ $optimization_data->set_result_status( 'error' );
302
+
303
+ /**
304
+ * @var WRIO_Nextgen_Extra_Data $extra_data
305
+ */
306
+ $extra_data = $optimization_data->get_extra_data();
307
+ $extra_data->set_error( 'deferred' );
308
+ $extra_data->set_error_msg( 'server not support deferred optimization' );
309
+ $optimization_data->set_extra_data( $extra_data );
310
+ $optimization_data->save();
311
+
312
+ WRIO_Logger::error( sprintf( 'Server %s does not support deferred optimization', get_class( $image_processor ) ) );
313
+
314
+ return false;
315
+ }
316
+ $optimized_data = $nextgen_image->deferredOptimization();
317
+ if ( $optimized_data ) {
318
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
319
+ $image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
320
+ $image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
321
+ $image_statistics->save();
322
+ }
323
+
324
+ return $optimized_data;
325
+ }
326
+
327
+
328
+ /**
329
+ * Обработка неоптимизированных изображений
330
+ *
331
+ * @param int $max_process_per_request кол-во аттачментов за 1 запуск
332
+ *
333
+ * @return array|\WP_Error
334
+ */
335
+ public function processUnoptimizedImages( $max_process_per_request = 5 ) {
336
+ $backup = WRIOP_Backup::get_instance();
337
+ $backup_origin_images = WRIO_Plugin::app()->getPopulateOption( 'backup_origin_images', false );
338
+ if ( $backup_origin_images and ! $backup->isBackupWritable() ) {
339
+ return new WP_Error( 'unwritable_backup_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
340
+ }
341
+ if ( ! $backup->isUploadWritable() ) {
342
+ return new WP_Error( 'unwritable_upload_dir', __( 'No access for writing backups.', 'robin-image-optimizer' ) );
343
+ }
344
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
345
+
346
+ //выборка неоптимизированных изображений
347
+ $gallery_images = $image_statistics->getUnoptimized( $max_process_per_request ); // тут будет выборка
348
+ $total_unoptimized = $image_statistics->getUnoptimizedCount(); // тут общее кол-во неоптимизированных
349
+ $gallery_images_count = 0;
350
+ if ( isset( $gallery_images ) ) {
351
+ $gallery_images_count = count( $gallery_images );
352
+ }
353
+
354
+ $optimized_count = 0;
355
+
356
+ // обработка
357
+ if ( $gallery_images_count ) {
358
+ foreach ( $gallery_images as $gallery_image ) {
359
+ $this->optimizeNextgenImage( $gallery_image->pid );
360
+ }
361
+ }
362
+
363
+ $remain = $total_unoptimized - $gallery_images_count;
364
+ // проверяем, есть ли аттачменты в очереди на отложенную оптимизацию
365
+ $optimized_data = $this->processDeferredOptimization();
366
+ if ( $optimized_data ) {
367
+ $optimized_count = $optimized_data['optimized_count'];
368
+ $remain = $total_unoptimized - $optimized_count;
369
+ }
370
+ if ( $remain <= 0 ) {
371
+ $remain = 0;
372
+ }
373
+ $responce = [
374
+ 'remain' => $remain,
375
+ 'end' => false,
376
+ 'last_optimized' => $image_statistics->get_last_optimized_images( $max_process_per_request ),
377
+ 'statistic' => $image_statistics->load(),
378
+ 'optimized_count' => $optimized_count,
379
+ ];
380
+
381
+ return $responce;
382
+ }
383
+
384
+ /**
385
+ * Отложенная оптимизация
386
+ *
387
+ * @return bool|array
388
+ */
389
+ protected function processDeferredOptimization() {
390
+ global $wpdb;
391
+ $db_table = RIO_Process_Queue::table_name();
392
+ $image_id = $wpdb->get_var( "SELECT object_id FROM {$db_table} WHERE item_type = 'nextgen' and result_status = 'processing' LIMIT 1;" );
393
+ if ( ! $image_id ) {
394
+ return false;
395
+ }
396
+ $nextgen_image = $this->getNextgenImage( $image_id );
397
+ $optimized_data = $nextgen_image->deferredOptimization();
398
+ if ( $optimized_data ) {
399
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
400
+ $image_statistics->addToField( 'optimized_size', $optimized_data['optimized_size'] );
401
+ $image_statistics->addToField( 'original_size', $optimized_data['original_size'] );
402
+ $image_statistics->save();
403
+
404
+ return $optimized_data;
405
+ }
406
+
407
+ return false;
408
+ }
409
+
410
+ /**
411
+ * Сбрасывает текущие ошибки оптимизации
412
+ * Позволяет изображениям, которые оптимизированы с ошибкой, заново пройти оптимизацию.
413
+ *
414
+ * @return void
415
+ */
416
+ public function resetCurrentErrors() {
417
+ //do_action( 'wbcr/rio/multisite_current_blog' );
418
+
419
+ global $wpdb;
420
+
421
+ $db_table = RIO_Process_Queue::table_name();
422
+
423
+ $wpdb->delete( $db_table, [
424
+ 'item_type' => 'nextgen',
425
+ 'result_status' => 'error',
426
+ ], [ '%s', '%s' ] );
427
+ //do_action( 'wbcr/rio/multisite_restore_blog' );
428
+ }
429
+
430
+ /**
431
+ * Хук срабатывает при восстановлении картинок через стандартный интерфейс nextgen
432
+ */
433
+ public function recoverImageHook( $image ) {
434
+ $image_id = isset( $image->pid ) ? $image->pid : 0;
435
+ $nextgen_image = new WRIO_Image_Nextgen( $image_id );
436
+ if ( $nextgen_image->isOptimized() ) {
437
+ $optimization_data = $nextgen_image->getOptimizationData();
438
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
439
+ $optimized_size = $optimization_data->get_final_size();
440
+ $original_size = $optimization_data->get_original_size();
441
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
442
+ $image_statistics->deductFromField( 'original_size', $original_size );
443
+ $image_statistics->save();
444
+
445
+ if ( ! $nextgen_image->restore() ) {
446
+ WRIO_Logger::error( sprintf( 'Failed to restore Nextgen image. Object info: %s', wp_json_encode( $nextgen_image ) ) );
447
+ }
448
+
449
+ $optimization_data->delete();
450
+ }
451
+ $this->deleteNextgenOptimizationData( $image_id );
452
+ }
453
+
454
+ /**
455
+ * Хук срабатывает при удалении картинки
456
+ */
457
+ public function deleteImageHook( $image_id, $image ) {
458
+ $nextgen_image = new WRIO_Image_Nextgen( $image_id );
459
+
460
+ if ( $nextgen_image->isOptimized() ) {
461
+ $restored = $nextgen_image->restore();
462
+
463
+ if ( ! is_wp_error( $restored ) ) {
464
+ $optimization_data = $nextgen_image->getOptimizationData();
465
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
466
+ $optimized_size = $optimization_data->get_final_size();
467
+ $original_size = $optimization_data->get_original_size();
468
+ $image_statistics->deductFromField( 'optimized_size', $optimized_size );
469
+ $image_statistics->deductFromField( 'original_size', $original_size );
470
+ $image_statistics->save();
471
+
472
+ WRIO_Logger::error( sprintf( 'Failed to restore Nextgen image. Object info: %s', wp_json_encode( $nextgen_image ) ) );
473
+
474
+ $optimization_data->delete();
475
+ }
476
+ }
477
+ $this->deleteNextgenOptimizationData( $image_id );
478
+ }
479
+
480
+ /**
481
+ * Удаляет данные по оптимизации из таблицы в базе данных
482
+ *
483
+ * @param int $image_id
484
+ *
485
+ * @return void
486
+ */
487
+ protected function deleteNextgenOptimizationData( $image_id = 0 ) {
488
+ global $wpdb;
489
+
490
+ $db_table = RIO_Process_Queue::table_name();
491
+
492
+ $wpdb->delete( $db_table, [
493
+ 'object_id' => $image_id,
494
+ 'item_type' => 'nextgen',
495
+ ] );
496
+ }
497
+
498
+ /**
499
+ * Возвращает процент оптимизации
500
+ * Фильтр wbcr/rio/optimize_template/optimized_percent
501
+ *
502
+ * @param int $percent процент оптимизации
503
+ * @param string $type тип страницы
504
+ *
505
+ * @return int процент оптимизации
506
+ */
507
+ public function optimizedPercent( $percent, $type ) {
508
+ if ( 'nextgen' == $type ) {
509
+ $image_statistics = WRIO_Image_Statistic_Nextgen::get_instance();
510
+
511
+ return $image_statistics->getOptimizedPercent();
512
+ }
513
+
514
+ return $percent;
515
+ }
516
+
517
+ /**
518
+ * Сохраняет WebP размер
519
+ *
520
+ * @param RIO_Process_Queue $queue_model
521
+ *
522
+ * @return bool
523
+ */
524
+ public function webpSuccess( $queue_model ) {
525
+ if ( ! class_exists( 'WRIO\WEBP\Listener' ) ) {
526
+ return false; // если не установлена премиум версия, то WebP не активен
527
+ }
528
+ if ( $queue_model->get_item_type() !== WRIO\WEBP\Listener::DEFAULT_TYPE ) {
529
+ return false;
530
+ }
531
+ if ( $queue_model->get_result_status() !== RIO_Process_Queue::STATUS_SUCCESS ) {
532
+ return false;
533
+ }
534
+
535
+ /**
536
+ * @var RIOP_WebP_Extra_Data $extra_data
537
+ */
538
+ $extra_data = $queue_model->get_extra_data();
539
+ $item_type = $extra_data->get_convert_from();
540
+
541
+ if ( $item_type != 'nextgen' ) {
542
+ return false;
543
+ }
544
+
545
+ if ( ! $queue_model->object_id ) {
546
+ return false;
547
+ }
548
+
549
+ $optimization_data = new RIO_Process_Queue( [
550
+ 'object_id' => $queue_model->object_id,
551
+ 'item_type' => 'nextgen',
552
+ ] );
553
+
554
+ $optimization_data->load();
555
+
556
+ /**
557
+ * @var WRIO_Nextgen_Extra_Data $extra_data
558
+ */
559
+ $extra_data = $optimization_data->get_extra_data();
560
+ if ( ! $extra_data ) {
561
+ return false;
562
+ }
563
+
564
+ $extra_data->set_webp_main_size( $queue_model->get_final_size() );
565
+ $optimization_data->set_extra_data( $extra_data );
566
+ add_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
567
+ $optimization_data->save();
568
+ remove_filter( 'wbcr/riop/queue_item_save_execute_hook', '__return_false' );
569
+
570
+ return true;
571
+ }
572
+
573
+ }
libs/addons/includes/classes/class.image-nextgen.php ADDED
@@ -0,0 +1,532 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы с nextgen image
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Image_Nextgen {
16
+
17
+ /**
18
+ * @var int номер картинки в таблице nextgen
19
+ */
20
+ private $id;
21
+
22
+ /**
23
+ * @var string путь к картинке
24
+ */
25
+ private $path;
26
+
27
+ /**
28
+ * @var string путь к миниатюре
29
+ */
30
+ private $thumbnail_path;
31
+
32
+ /**
33
+ * @var string имя файла
34
+ */
35
+ private $file;
36
+
37
+ /**
38
+ * @var string имя файла миниатюры
39
+ */
40
+ private $thumbnail_file;
41
+
42
+ /**
43
+ * @var string УРЛ
44
+ */
45
+ private $url;
46
+
47
+ /**
48
+ * @var string УРЛ миниатюры
49
+ */
50
+ private $thumbnail_url;
51
+
52
+ /**
53
+ * @var array мета данные картинки
54
+ */
55
+ private $meta;
56
+
57
+ /**
58
+ * @var array мета данные галереи
59
+ */
60
+ private $gallery_meta;
61
+
62
+ /**
63
+ * @var RIO_Process_Queue данные по оптимизации
64
+ */
65
+ private $optimization_data = null;
66
+
67
+ /**
68
+ * Инициализация картинки из nextgen gallery
69
+ *
70
+ * @param int $image_id номер картинки в таблице nextgen
71
+ * @param array $image_data метаданные картинки
72
+ */
73
+ public function __construct( $image_id, $image_data = false ) {
74
+ global $wpdb;
75
+
76
+ $this->id = $image_id;
77
+
78
+ if ( $image_data ) {
79
+ $this->meta = $image_data;
80
+ } else {
81
+ $this->meta = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}ngg_pictures WHERE pid = " . intval( $image_id ) );
82
+ }
83
+
84
+ $this->file = $this->meta->filename;
85
+ $this->thumbnail_file = 'thumbs_' . $this->meta->filename;
86
+
87
+ global $wpdb;
88
+ $this->gallery_meta = $wpdb->get_row( "SELECT * FROM {$wpdb->prefix}ngg_gallery WHERE gid = " . intval( $this->meta->galleryid ) );
89
+
90
+ $this->path = wp_normalize_path( ABSPATH . $this->gallery_meta->path . $this->meta->filename );
91
+ $this->thumbnail_path = wp_normalize_path( ABSPATH . $this->gallery_meta->path . 'thumbs/' . $this->thumbnail_file );
92
+ $this->url = home_url( $this->gallery_meta->path . $this->meta->filename );
93
+ $this->thumbnail_url = home_url( $this->gallery_meta->path . 'thumbs/' . $this->thumbnail_file );
94
+ }
95
+
96
+ /**
97
+ * Возвращает свойство аттачмента
98
+ *
99
+ * @param string $property имя свойства
100
+ *
101
+ * @return mixed
102
+ */
103
+ public function get( $property ) {
104
+ if ( isset( $this->$property ) ) {
105
+ return $this->$property;
106
+ }
107
+
108
+ return false;
109
+ }
110
+
111
+ /**
112
+ * Проверка на оптимизацию изображения
113
+ *
114
+ * @return bool
115
+ */
116
+ public function isOptimized() {
117
+ $optimization_data = $this->getOptimizationData();
118
+ if ( empty( $optimization_data ) ) {
119
+ return false;
120
+ }
121
+ if ( $optimization_data->is_optimized() ) {
122
+ return true;
123
+ }
124
+
125
+ return false;
126
+ }
127
+
128
+ /**
129
+ * Возвращает данные по оптимизации
130
+ *
131
+ * @return RIO_Process_Queue
132
+ */
133
+ public function getOptimizationData() {
134
+ if ( empty( $this->optimization_data ) ) {
135
+ $this->optimization_data = $this->createOptimizationData();
136
+ $this->optimization_data->load();
137
+ }
138
+
139
+ return $this->optimization_data;
140
+ }
141
+
142
+ /**
143
+ * Создаёт новый объект RIO_Process_Queue
144
+ *
145
+ * @return RIO_Process_Queue
146
+ */
147
+ public function createOptimizationData() {
148
+ return new RIO_Process_Queue( [
149
+ 'object_id' => $this->id,
150
+ 'item_type' => 'nextgen',
151
+ ] );
152
+ }
153
+
154
+ /**
155
+ * Оптимизирует изображения
156
+ *
157
+ * @param string $optimization_level качество
158
+ *
159
+ * @return array
160
+ */
161
+ public function optimize( $optimization_level = '' ) {
162
+ $is_image_backuped = $this->backup();
163
+
164
+ if ( is_wp_error( $is_image_backuped ) ) {
165
+ $error_msg = $is_image_backuped->get_error_message() . PHP_EOL;
166
+
167
+ return [
168
+ 'errors_count' => 1,
169
+ 'original_size' => 0,
170
+ 'optimized_size' => 0,
171
+ 'optimized_count' => 0,
172
+ ];
173
+ }
174
+
175
+ // делаем рисайз
176
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
177
+
178
+ if ( ! $optimization_level ) {
179
+ $optimization_level = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', 'normal' );
180
+ }
181
+
182
+ if ( $optimization_level == 'custom' ) {
183
+ $custom_quality = WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level_custom', 100 );
184
+ $optimization_level = intval( $custom_quality );
185
+ }
186
+
187
+ $optimization_data = $this->createOptimizationData();
188
+ $results = [];
189
+ $results['processing_level'] = $optimization_level;
190
+ $results['original_mime_type'] = '';
191
+ $results['final_mime_type'] = '';
192
+
193
+ $main_file_path = wp_normalize_path( ABSPATH . $this->gallery_meta->path . $this->meta->filename );
194
+ $main_file_url = home_url( $this->gallery_meta->path . $this->meta->filename );
195
+
196
+ clearstatcache(); // на всякий случай очистим кеш файловой статистики
197
+ if ( ! file_exists( $main_file_path ) ) {
198
+ $results['result_status'] = 'error';
199
+ $results['original_size'] = 0;
200
+ $results['final_size'] = 0;
201
+ $extra_data = [
202
+ 'error' => 'file',
203
+ 'error_msg' => __( 'File not found', 'robin-image-optimizer' ),
204
+ ];
205
+ $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
206
+ $optimization_data->configure( $results );
207
+ $optimization_data->save();
208
+
209
+ return [
210
+ 'errors_count' => 1,
211
+ 'original_size' => 0,
212
+ 'optimized_size' => 0,
213
+ 'optimized_count' => 0,
214
+ ];
215
+ }
216
+ $original_main_size = filesize( $main_file_path ); // оптимизированный размер только главной картинки
217
+
218
+ $optimized_img_data = $image_processor->process( [
219
+ 'image_url' => $main_file_url,
220
+ 'image_path' => $main_file_path,
221
+ 'quality' => $image_processor->quality( $optimization_level ),
222
+ 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
223
+ ] );
224
+
225
+ if ( is_wp_error( $optimized_img_data ) ) {
226
+ $results['result_status'] = 'error';
227
+ $results['original_size'] = 0;
228
+ $results['final_size'] = 0;
229
+ $extra_data = [
230
+ 'error' => 'optimization',
231
+ 'error_msg' => $optimized_img_data->get_error_message(),
232
+ ];
233
+ $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
234
+ $optimization_data->configure( $results );
235
+ $optimization_data->save();
236
+
237
+ return [
238
+ 'errors_count' => 1,
239
+ 'original_size' => 0,
240
+ 'optimized_size' => 0,
241
+ 'optimized_count' => 0,
242
+ ];
243
+ }
244
+
245
+ // оптимизируем thumbnail
246
+ $optimized_thumbnail_data = $image_processor->process( [
247
+ 'image_url' => $this->thumbnail_url,
248
+ 'image_path' => $this->thumbnail_path,
249
+ 'quality' => $image_processor->quality( $optimization_level ),
250
+ 'save_exif' => WRIO_Plugin::app()->getPopulateOption( 'save_exif_data', false ),
251
+ ] );
252
+
253
+ // отложенная оптимизация
254
+ if ( isset( $optimized_img_data['status'] ) && $optimized_img_data['status'] == 'processing' ) {
255
+ $results['result_status'] = 'processing';
256
+ $results['is_backed_up'] = $is_image_backuped;
257
+ $results['original_size'] = 0;
258
+ $results['final_size'] = 0;
259
+ $extra_data = [
260
+ 'main_optimized_data' => $optimized_img_data,
261
+ 'thumbnails_optimized_data' => $optimized_thumbnail_data,
262
+ 'image_relative_path' => str_replace( untrailingslashit( ABSPATH ), '', $this->path ),
263
+ ];
264
+ $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
265
+ $optimization_data->configure( $results );
266
+ $optimization_data->save();
267
+
268
+ return [
269
+ 'processing' => 1,
270
+ 'original_size' => 0,
271
+ 'optimized_size' => 0,
272
+ ];
273
+ }
274
+
275
+ $this->replaceOriginalFile( $optimized_img_data );
276
+
277
+ // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
278
+ if ( ! $optimized_img_data['optimized_size'] ) {
279
+ clearstatcache();
280
+ $optimized_img_data['optimized_size'] = filesize( $main_file_path );
281
+ }
282
+
283
+ // при отрицательной оптимизации ставим значение оригинала
284
+ if ( $optimized_img_data['optimized_size'] > $original_main_size ) {
285
+ $optimized_img_data['optimized_size'] = $original_main_size;
286
+ }
287
+
288
+ $original_size = $original_main_size;
289
+ $optimized_size = $optimized_img_data['optimized_size'];
290
+ $optimized_main_size = $optimized_img_data['optimized_size'];
291
+
292
+ if ( ! is_wp_error( $optimized_thumbnail_data ) ) {
293
+ $original_size += filesize( $this->thumbnail_path );
294
+ $this->replaceOriginalFile( $optimized_thumbnail_data, 'thumbnail' );
295
+ // некоторые провайдеры не отдают оптимизированный размер, поэтому после замены файла получаем его сами
296
+ if ( ! $optimized_thumbnail_data['optimized_size'] ) {
297
+ clearstatcache();
298
+ $optimized_thumbnail_data['optimized_size'] = filesize( $this->thumbnail_path );
299
+ }
300
+ $optimized_size += $optimized_thumbnail_data['optimized_size'];
301
+ }
302
+ $results['result_status'] = 'success';
303
+ $results['final_size'] = $optimized_size;
304
+ $results['original_size'] = $original_size;
305
+ $results['is_backed_up'] = $is_image_backuped;
306
+ $extra_data = [
307
+ 'original_main_size' => $original_main_size,
308
+ 'optimized_main_size' => $optimized_main_size,
309
+ 'image_relative_path' => str_replace( wp_normalize_path( untrailingslashit( ABSPATH ) ), '', $this->path ),
310
+ ];
311
+ $results['extra_data'] = new WRIO_Nextgen_Extra_Data( $extra_data );
312
+ $mime_type = '';
313
+ if ( function_exists( 'wp_get_image_mime' ) ) {
314
+ $mime_type = wp_get_image_mime( $main_file_path );
315
+ }
316
+ $results['original_mime_type'] = $mime_type;
317
+ $results['final_mime_type'] = $mime_type;
318
+ $optimization_data->configure( $results );
319
+ $optimization_data->save();
320
+
321
+ return [
322
+ 'errors_count' => 0,
323
+ 'original_size' => $original_size,
324
+ 'optimized_size' => $optimized_size,
325
+ 'optimized_count' => 1,
326
+ ];
327
+ }
328
+
329
+ /**
330
+ * Отложенная оптимизация аттачмента
331
+ *
332
+ * @return bool|array
333
+ */
334
+ public function deferredOptimization() {
335
+ $results = [
336
+ 'original_size' => 0,
337
+ 'optimized_size' => 0,
338
+ 'optimized_count' => 0,
339
+ 'processing' => 1,
340
+ ];
341
+ $image_processor = WIO_OptimizationTools::getImageProcessor();
342
+ $optimization_data = $this->getOptimizationData();
343
+ if ( $optimization_data->get_result_status() != 'processing' ) {
344
+ return false;
345
+ }
346
+ // проверяем главную картинку
347
+ /**
348
+ * @var WRIO_Nextgen_Extra_Data $extra_data
349
+ */
350
+ $extra_data = $optimization_data->get_extra_data();
351
+ $main_optimized_data = $extra_data->get_main_optimized_data();
352
+ $main_image_url = '';
353
+ if ( ! $main_optimized_data['optimized_img_url'] ) {
354
+ $main_image_url = $image_processor->checkDeferredOptimization( $main_optimized_data );
355
+ if ( $main_image_url ) {
356
+ $main_optimized_data['optimized_img_url'] = $main_image_url;
357
+ $extra_data->set_main_optimized_data( $main_optimized_data );
358
+ }
359
+ }
360
+
361
+ $thumbnails_processed = true;
362
+ $thumbnail_optimized_data = $extra_data->get_thumbnails_optimized_data();
363
+ if ( ! $thumbnail_optimized_data['optimized_img_url'] ) {
364
+ $thumbnail_image_url = $image_processor->checkDeferredOptimization( $thumbnail_optimized_data );
365
+ if ( $thumbnail_image_url ) {
366
+ $thumbnail_optimized_data['optimized_img_url'] = $thumbnail_image_url;
367
+ } else {
368
+ $thumbnails_processed = false;
369
+ }
370
+ }
371
+ $extra_data->set_thumbnails_optimized_data( $thumbnail_optimized_data );
372
+
373
+ // когда все файлы получены - сохраняем и возвращаем результат
374
+ if ( $main_image_url && $thumbnails_processed ) {
375
+ $original_size = 0;
376
+ $optimized_size = 0;
377
+ $thumbnails_count = 0;
378
+ $original_main_size = filesize( $this->get( 'path' ) );
379
+ $original_size = $original_size + $original_main_size;
380
+ $this->replaceOriginalFile( [
381
+ 'optimized_img_url' => $main_image_url,
382
+ ] );
383
+ clearstatcache();
384
+ $optimized_main_size = filesize( $this->get( 'path' ) );
385
+ // при отрицательной оптимизации ставим значение оригинала
386
+ if ( $optimized_main_size > $original_main_size ) {
387
+ $optimized_main_size = $original_main_size;
388
+ }
389
+ $optimized_size = $optimized_size + $optimized_main_size;
390
+ $thumbnail_file = $this->get( 'thumbnail_path' );
391
+ $original_size = $original_size + filesize( $thumbnail_file );
392
+ $this->replaceOriginalFile( [
393
+ 'optimized_img_url' => $thumbnail_optimized_data['optimized_img_url'],
394
+ ], 'thumbnail' );
395
+ clearstatcache();
396
+ $optimized_size = $optimized_size + filesize( $thumbnail_file );
397
+ $thumbnails_count ++;
398
+ $mime_type = '';
399
+ if ( function_exists( 'wp_get_image_mime' ) ) {
400
+ $mime_type = wp_get_image_mime( $this->get( 'path' ) );
401
+ }
402
+ $optimization_data->configure( [
403
+ 'final_size' => $optimized_size,
404
+ 'original_size' => $original_size,
405
+ 'result_status' => 'success',
406
+ 'original_mime_type' => $mime_type,
407
+ 'final_mime_type' => $mime_type,
408
+ ] );
409
+ $extra_data->set_original_main_size( $original_main_size );
410
+ // удаляем промежуточные данные
411
+ $extra_data->set_main_optimized_data( null );
412
+ $extra_data->set_thumbnails_optimized_data( null );
413
+
414
+ $results['optimized_count'] = 1;
415
+ $results['original_size'] = $original_size;
416
+ $results['optimized_size'] = $optimized_size;
417
+ unset( $results['processing'] );
418
+ }
419
+ $optimization_data->set_extra_data( $extra_data );
420
+ $optimization_data->save();
421
+
422
+ return $results;
423
+ }
424
+
425
+ /**
426
+ * Заменяет оригинальный файл на оптимизированный
427
+ *
428
+ * @param array $optimized_img_data результат оптимизации ввиде массива данных
429
+ * @param string $image_size Размер(thumbnail, medium ... )
430
+ */
431
+ public function replaceOriginalFile( $optimized_img_data, $image_size = '' ) {
432
+ $optimized_img_url = $optimized_img_data['optimized_img_url'];
433
+ if ( isset( $optimized_img_data['not_need_download'] ) and $optimized_img_data['not_need_download'] ) {
434
+ $optimized_file = $optimized_img_url;
435
+ } else {
436
+ $optimized_file = $this->remoteDownloadImage( $optimized_img_url );
437
+ }
438
+ if ( isset( $optimized_img_data['not_need_replace'] ) and $optimized_img_data['not_need_replace'] ) {
439
+ // если картинка уже оптимизирована и провайдер её не может уменьшить - он может вернуть положительный ответ, но без самой картинки. В таком случае ничего заменять не надо
440
+ return true;
441
+ }
442
+ if ( ! $optimized_file ) {
443
+ return false;
444
+ }
445
+ $path = $this->path;
446
+ if ( $image_size == 'thumbnail' ) {
447
+ $path = $this->thumbnail_path;
448
+ }
449
+
450
+ if ( ! is_file( $path ) ) {
451
+ return false;
452
+ }
453
+
454
+ file_put_contents( $path, $optimized_file );
455
+
456
+ return true;
457
+ }
458
+
459
+ /**
460
+ * Загрузка картинки с удалённого сервера
461
+ *
462
+ * todo: RIO-18 можем ли мы создать универсальный метод для всех внешних запросов, чтобы не дублировать код?
463
+ *
464
+ * @param string $url
465
+ *
466
+ * @return string
467
+ */
468
+ protected function remoteDownloadImage( $url ) {
469
+ if ( ! function_exists( 'curl_version' ) ) {
470
+ return file_get_contents( $url );
471
+ }
472
+
473
+ $ch = curl_init();
474
+ curl_setopt( $ch, CURLOPT_HEADER, 0 );
475
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
476
+ curl_setopt( $ch, CURLOPT_URL, $url );
477
+
478
+ $image_body = curl_exec( $ch );
479
+ $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
480
+ if ( $http_code != '200' ) {
481
+ $image_body = false;
482
+ }
483
+ curl_close( $ch );
484
+
485
+ return $image_body;
486
+ }
487
+
488
+ /**
489
+ * Делает резервную копию изображения
490
+ */
491
+ public function backup() {
492
+ $backup = WRIOP_Backup::get_instance();
493
+
494
+ return $backup->backupNextgen( $this );
495
+ }
496
+
497
+ /**
498
+ * Восстанавливает из резервной копии.
499
+ *
500
+ * @return bool|WP_Error
501
+ */
502
+ public function restore() {
503
+
504
+ $backup = WRIOP_Backup::get_instance();
505
+ $restored = $backup->restoreNextgen( $this );
506
+
507
+ if ( is_wp_error( $restored ) ) {
508
+ return $restored;
509
+ }
510
+
511
+ global $wpdb;
512
+
513
+ $io_db_table = RIO_Process_Queue::table_name();
514
+
515
+ $wpdb->delete( $io_db_table, [
516
+ 'object_id' => $this->id,
517
+ 'item_type' => 'nextgen',
518
+ ] );
519
+
520
+ /**
521
+ * Хук срабатывает после восстановления nextgen image
522
+ *
523
+ * @since 1.2.0
524
+ *
525
+ * @param RIO_Process_Queue $optimization_data
526
+ *
527
+ */
528
+ do_action( 'wbcr/rio/nextgen_image_restored', $this->optimization_data );
529
+
530
+ return true;
531
+ }
532
+ }
libs/addons/includes/classes/class.image-statistic-folders.php ADDED
@@ -0,0 +1,332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы со статистическими данными по оптимизации изображений
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Image_Statistic_Folders extends WRIO_Image_Statistic {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * Сохранение статистики
28
+ */
29
+ public function save() {
30
+ WRIO_Plugin::app()->updateOption( 'folders_original_size', $this->statistic['original_size'] );
31
+ WRIO_Plugin::app()->updateOption( 'folders_optimized_size', $this->statistic['optimized_size'] );
32
+ }
33
+
34
+ /**
35
+ * Загрузка статистики и расчёт некоторых параметров
36
+ *
37
+ * @return array
38
+ */
39
+ public function load() {
40
+ $original_size = WRIO_Plugin::app()->getOption( 'folders_original_size', 0 );
41
+ $optimized_size = WRIO_Plugin::app()->getOption( 'folders_optimized_size', 0 );
42
+ $total_images = $this->getTotalCount();
43
+ $error_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'error' );
44
+ $skipped_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'skip' );
45
+ $optimized_count = $this->getOptimizedCount();
46
+
47
+ if ( ! $total_images ) {
48
+ $total_images = 0;
49
+ if ( $original_size || $optimized_size ) {
50
+ // если нет картинок, то и размеров не должно быть
51
+ $original_size = $this->statistic['original_size'] = 0;
52
+ $optimized_size = $this->statistic['optimized_size'] = 0;
53
+ $this->save();
54
+ }
55
+ }
56
+ if ( ! $error_count ) {
57
+ $error_count = 0;
58
+ }
59
+ if ( ! $skipped_count ) {
60
+ $skipped_count = 0;
61
+ }
62
+ if ( ! $optimized_count ) {
63
+ $optimized_count = 0;
64
+ }
65
+ // unoptimized count: all - optimized - error - skip
66
+ $unoptimized_count = $total_images - $optimized_count - $error_count - $skipped_count;
67
+ if ( $optimized_size and $original_size ) {
68
+ $percent_diff = round( ( $original_size - $optimized_size ) * 100 / $original_size, 1 );
69
+ $percent_diff_line = round( $optimized_size * 100 / $original_size, 0 );
70
+ } else {
71
+ $percent_diff = 0;
72
+ $percent_diff_line = 100;
73
+ }
74
+ if ( $total_images ) {
75
+ $optimized_images_percent = round( $optimized_count * 100 / $total_images );
76
+ } else {
77
+ $optimized_images_percent = 0;
78
+ }
79
+
80
+ return [
81
+ 'original' => $total_images,
82
+ 'optimized' => $optimized_count,
83
+ 'optimized_percent' => $optimized_images_percent,
84
+ 'percent_line' => $percent_diff_line,
85
+ 'unoptimized' => $unoptimized_count,
86
+ 'optimized_size' => $optimized_size,
87
+ 'original_size' => $original_size,
88
+ 'save_size_percent' => $percent_diff,
89
+ 'error' => $error_count,
90
+ ];
91
+ }
92
+
93
+ /**
94
+ * Общее кол-во изображений
95
+ */
96
+ public function getTotalCount() {
97
+ $cf = WRIO_Custom_Folders::get_instance();
98
+ $current_folder = apply_filters( 'wriop_cf_current_folder', false );
99
+ if ( $current_folder ) {
100
+ // если нужна конкретная папка
101
+ $folder = $cf->getFolder( $current_folder );
102
+ if ( ! $folder ) {
103
+ return 0;
104
+ }
105
+
106
+ return $folder->get( 'files_count' );
107
+ }
108
+ $folders = $cf->getFolders();
109
+ $total_images = 0;
110
+ if ( ! $folders ) {
111
+ return $total_images;
112
+ }
113
+ foreach ( $folders as $folder ) {
114
+ $total_images += $folder->get( 'files_count' );
115
+ }
116
+
117
+ return $total_images;
118
+ }
119
+
120
+ public function getOptimizedCount() {
121
+ $current_folder = apply_filters( 'wriop_cf_current_folder', false );
122
+ if ( $current_folder ) {
123
+ global $wpdb;
124
+ $db_table = RIO_Process_Queue::table_name();
125
+ $sql_optimized = $wpdb->prepare( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = 'cf_image' AND item_hash_alternative = %s AND result_status = 'success';", $current_folder );
126
+ $optimized_count = $wpdb->get_var( $sql_optimized );
127
+ } else {
128
+ $optimized_count = RIO_Process_Queue::count_by_type_status( 'cf_image', 'success' );
129
+ }
130
+
131
+ return $optimized_count;
132
+ }
133
+
134
+ /**
135
+ * Кол-во неоптимизированных изображений
136
+ */
137
+ public function getUnoptimizedCount() {
138
+ global $wpdb;
139
+
140
+ $db_table = RIO_Process_Queue::table_name();
141
+ $current_folder = apply_filters( 'wriop_cf_current_folder', false );
142
+
143
+ if ( $current_folder ) {
144
+ $sql_unoptimized = $wpdb->prepare( "
145
+ SELECT COUNT(*)
146
+ FROM {$db_table}
147
+ WHERE item_type = 'cf_image'
148
+ AND item_hash_alternative = %s
149
+ AND result_status IN (%s,%s);", $current_folder, RIO_Process_Queue::STATUS_UNOPTIMIZED, RIO_Process_Queue::STATUS_PROCESSING );
150
+ } else {
151
+ $sql_unoptimized = $wpdb->prepare( "
152
+ SELECT COUNT(*)
153
+ FROM {$db_table} WHERE
154
+ item_type = 'cf_image' AND result_status IN (%s,%s);", RIO_Process_Queue::STATUS_UNOPTIMIZED, RIO_Process_Queue::STATUS_PROCESSING );
155
+ }
156
+
157
+ $unoptimized = $wpdb->get_var( $sql_unoptimized );
158
+
159
+ return $unoptimized;
160
+ }
161
+
162
+ /**
163
+ * Возвращает неоптимизированные изображения
164
+ *
165
+ * @param int $limit ограничение выборки
166
+ *
167
+ * @return RIO_Process_Queue[]|array
168
+ */
169
+ public function getUnoptimized( $limit = 10 ) {
170
+ // переделать
171
+ global $wpdb;
172
+
173
+ $unoptimized_items = [];
174
+ $db_table = RIO_Process_Queue::table_name();
175
+ $current_folder = apply_filters( 'wriop_cf_current_folder', false );
176
+
177
+ if ( $current_folder ) {
178
+ $sql_unoptimized = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_type = 'cf_image' AND item_hash_alternative = %s AND result_status = 'unoptimized' LIMIT %d", $current_folder, $limit );
179
+ } else {
180
+ $sql_unoptimized = "SELECT * FROM {$db_table} WHERE item_type = 'cf_image' AND result_status = 'unoptimized' LIMIT " . intval( $limit );
181
+ }
182
+
183
+ $result = $wpdb->get_results( $sql_unoptimized );
184
+
185
+ if ( ! empty( $result ) ) {
186
+ foreach ( $result as $key => $data ) {
187
+ $unoptimized_items[ $key ] = new RIO_Process_Queue( $data );
188
+ }
189
+ }
190
+
191
+ return $unoptimized_items;
192
+ }
193
+
194
+ public function getDeferredUnoptimized( $limit = 10 ) {
195
+ global $wpdb;
196
+
197
+ $db_table = RIO_Process_Queue::table_name();
198
+ $current_folder = apply_filters( 'wriop_cf_current_folder', false );
199
+ if ( $current_folder ) {
200
+ $sql_unoptimized = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_type = 'cf_image' AND item_hash_alternative = %s AND result_status = 'processing' LIMIT %d", $current_folder, $limit );
201
+ } else {
202
+ $sql_unoptimized = "SELECT * FROM {$db_table} WHERE item_type = 'cf_image' and result_status = 'processing' LIMIT " . intval( $limit );
203
+ }
204
+ $unoptimized = $wpdb->get_results( $sql_unoptimized );
205
+
206
+ return $unoptimized;
207
+ }
208
+
209
+ /**
210
+ * Возвращает результат последних оптимизаций изображений
211
+ *
212
+ * @param int $limit лимит
213
+ *
214
+ * @return array {
215
+ * Параметры
216
+ * @type string $id id
217
+ * @type string $file_name Имя файла
218
+ * @type string $url URL
219
+ * @type string $thumbnail_url URL превьюшки
220
+ * @type string $original_size Размер до оптимизации
221
+ * @type string $optimized_size Размер после оптимизации
222
+ * @type string $webp_size webP размер
223
+ * @type string $original_saving На сколько процентов изменился главный файл
224
+ * @type string $thumbnails_count Сколько превьюшек оптимизировано
225
+ * @type string $total_saving Процент оптимизации главного файла и превьюшек
226
+ * }
227
+ */
228
+ public function get_last_optimized_images( $limit = 100 ) {
229
+ global $wpdb;
230
+
231
+ $items = [];
232
+ $db_table = RIO_Process_Queue::table_name();
233
+
234
+ $sql = $wpdb->prepare( "SELECT *
235
+ FROM {$db_table} as t1
236
+ WHERE t1.item_type = 'cf_image'
237
+ AND t1.result_status
238
+ IN (%s, %s)
239
+ ORDER BY id DESC
240
+ LIMIT %d;", RIO_Process_Queue::STATUS_SUCCESS, RIO_Process_Queue::STATUS_ERROR, $limit );
241
+
242
+ $optimized_images = $wpdb->get_results( $sql, ARRAY_A );
243
+
244
+ foreach ( $optimized_images as $row ) {
245
+ $items[] = $this->format_for_log( new RIO_Process_Queue( $row ) );
246
+ }
247
+
248
+ return $items;
249
+ }
250
+
251
+ /**
252
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
253
+ * @since 1.1
254
+ *
255
+ * @param $id
256
+ */
257
+ public function get_last_optimized_image( $model ) {
258
+ $items[] = $this->format_for_log( $model );
259
+
260
+ return $items;
261
+ }
262
+
263
+ /**
264
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
265
+ * @since 1.0.4
266
+ *
267
+ * @param int|RIO_Process_Queue $queue_model
268
+ */
269
+ protected function format_for_log( $queue_model ) {
270
+ if ( $queue_model instanceof RIO_Process_Queue ) {
271
+ $cf_image = new WRIO_Folder_Image( $queue_model->id, $queue_model );
272
+ } else {
273
+ // todo: Temporarily fix
274
+ $cf_image = new WRIO_Folder_Image( $queue_model );
275
+ }
276
+
277
+ $optimization_data = $cf_image->getOptimizationData();
278
+
279
+ $main_file = $cf_image->get( 'path' );
280
+ $main_saving = $total_saving = 0;
281
+
282
+ if ( $optimization_data->original_size ) {
283
+ $total_saving = ( $optimization_data->original_size - $optimization_data->final_size ) * 100 / $optimization_data->original_size;
284
+ $main_saving = $total_saving;
285
+ }
286
+
287
+ $image_url = $cf_image->get( 'url' );
288
+ $thumbnail_url = $image_url;
289
+
290
+ $formated_data = [
291
+ 'id' => $optimization_data->id,
292
+ 'url' => $image_url,
293
+ 'thumbnail_url' => $thumbnail_url,
294
+ 'file_name' => wp_basename( $main_file ),
295
+ 'original_size' => size_format( $optimization_data->original_size, 2 ),
296
+ 'optimized_size' => size_format( $optimization_data->final_size, 2 ),
297
+ 'original_saving' => round( $main_saving ) . '%',
298
+ 'thumbnails_count' => 0,
299
+ 'type' => 'success',
300
+ 'total_saving' => round( $total_saving ) . '%',
301
+ ];
302
+
303
+ /**
304
+ * @var WRIO_CF_Image_Extra_Data $extra_data
305
+ */
306
+ $extra_data = $optimization_data->get_extra_data();
307
+
308
+ if ( ! empty( $extra_data ) ) {
309
+ $webp_size = $extra_data->get_webp_main_size();
310
+ if ( $webp_size ) {
311
+ $webp_size = size_format( $webp_size, 2 );
312
+ } else {
313
+ $webp_size = '-';
314
+ }
315
+
316
+ $formated_data['webp_size'] = $webp_size;
317
+
318
+ $error = $extra_data->get_error();
319
+
320
+ if ( $optimization_data->result_status === RIO_Process_Queue::STATUS_ERROR || ! empty( $error ) ) {
321
+ $formated_data['type'] = 'error';
322
+
323
+ $error_message = $extra_data->get_error_msg();
324
+
325
+ $formated_data['error_msg'] = ! empty( $error_message ) ? $error_message : esc_html__( 'Unknown error', 'robin-image-optimizer' );
326
+ }
327
+ }
328
+
329
+ return $formated_data;
330
+ }
331
+
332
+ }
libs/addons/includes/classes/class.image-statistic-nextgen.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Класс для работы со статистическими данными по оптимизации изображений
10
+ *
11
+ * @author Eugene Jokerov <jokerov@gmail.com>
12
+ * @copyright (c) 2018, Webcraftic
13
+ * @version 1.0
14
+ */
15
+ class WRIO_Image_Statistic_Nextgen extends WRIO_Image_Statistic {
16
+
17
+ /**
18
+ * The single instance of the class.
19
+ *
20
+ * @since 1.3.0
21
+ * @access protected
22
+ * @var object
23
+ */
24
+ protected static $_instance;
25
+
26
+ /**
27
+ * Сохранение статистики
28
+ */
29
+ public function save() {
30
+ WRIO_Plugin::app()->updateOption( 'nextgen_original_size', $this->statistic['original_size'] );
31
+ WRIO_Plugin::app()->updateOption( 'nextgen_optimized_size', $this->statistic['optimized_size'] );
32
+ }
33
+
34
+ /**
35
+ * Загрузка статистики и расчёт некоторых параметров
36
+ *
37
+ * @return array
38
+ */
39
+ public function load() {
40
+ $original_size = WRIO_Plugin::app()->getOption( 'nextgen_original_size', 0 );
41
+ $optimized_size = WRIO_Plugin::app()->getOption( 'nextgen_optimized_size', 0 );
42
+ global $wpdb;
43
+ $io_db_table = RIO_Process_Queue::table_name();
44
+
45
+ $sql_unoptimized = "SELECT COUNT(*)
46
+ FROM {$wpdb->prefix}ngg_pictures as t1 LEFT JOIN {$io_db_table} as t2
47
+ ON t2.item_type = 'nextgen' and t1.pid = t2.object_id
48
+ WHERE t2.result_status Is Null or ( t2.result_status != 'success' and t2.result_status != 'error' )";
49
+
50
+ $error_count = RIO_Process_Queue::count_by_type_status( 'nextgen', 'error' );
51
+ $total_images = $this->getTotalCount();
52
+ if ( ! $total_images ) {
53
+ $total_images = 0;
54
+ if ( $original_size || $optimized_size ) {
55
+ // если нет картинок, то и размеров не должно быть
56
+ $original_size = $this->statistic['original_size'] = 0;
57
+ $optimized_size = $this->statistic['optimized_size'] = 0;
58
+ $this->save();
59
+ }
60
+ }
61
+ $unoptimized = $wpdb->get_var( $sql_unoptimized );
62
+ if ( $optimized_size and $original_size ) {
63
+ $percent_diff = round( ( $original_size - $optimized_size ) * 100 / $original_size, 1 );
64
+ $percent_diff_line = round( $optimized_size * 100 / $original_size, 0 );
65
+ } else {
66
+ $percent_diff = 0;
67
+ $percent_diff_line = 100;
68
+ }
69
+ $optimized_exists_images_count = $total_images - $unoptimized - $error_count; // оптимизированные картинки, которые сейчас есть в медиабиблиотеке
70
+ if ( $total_images ) {
71
+ $optimized_images_percent = round( $optimized_exists_images_count * 100 / $total_images );
72
+ } else {
73
+ $optimized_images_percent = 0;
74
+ }
75
+
76
+ return [
77
+ 'original' => $total_images,
78
+ 'optimized' => $optimized_exists_images_count,
79
+ 'optimized_percent' => $optimized_images_percent,
80
+ 'percent_line' => $percent_diff_line,
81
+ 'unoptimized' => $unoptimized,
82
+ 'optimized_size' => $optimized_size,
83
+ 'original_size' => $original_size,
84
+ 'save_size_percent' => $percent_diff,
85
+ 'error' => $error_count,
86
+ ];
87
+ }
88
+
89
+ /**
90
+ * Общее кол-во изображений nextgen
91
+ */
92
+ public function getTotalCount() {
93
+ global $wpdb;
94
+ $sql_total = "SELECT COUNT(*) FROM {$wpdb->prefix}ngg_pictures";
95
+ $total_images = $wpdb->get_var( $sql_total );
96
+
97
+ return $total_images;
98
+ }
99
+
100
+ /**
101
+ * Кол-во неоптимизированных изображений
102
+ */
103
+ public function getUnoptimizedCount() {
104
+ global $wpdb;
105
+ $io_db_table = RIO_Process_Queue::table_name();
106
+ $sql_unoptimized = "SELECT COUNT(*)
107
+ FROM {$wpdb->prefix}ngg_pictures as t1 LEFT JOIN {$io_db_table} as t2
108
+ ON t2.item_type = 'nextgen' and t1.pid = t2.object_id
109
+ WHERE t2.result_status Is Null or ( t2.result_status != 'success' and t2.result_status != 'error' )";
110
+ $total_images = $wpdb->get_var( $sql_unoptimized );
111
+
112
+ return $total_images;
113
+ }
114
+
115
+ /**
116
+ * Возвращает неоптимизированные изображения
117
+ *
118
+ * @param string $limit ограничение выборки
119
+ *
120
+ * @return array
121
+ */
122
+ public function getUnoptimized( $limit = 10 ) {
123
+ global $wpdb;
124
+ $io_db_table = RIO_Process_Queue::table_name();
125
+ $sql_unoptimized = "SELECT *
126
+ FROM {$wpdb->prefix}ngg_pictures as t1 LEFT JOIN {$io_db_table} as t2
127
+ ON t2.item_type = 'nextgen' and t1.pid = t2.object_id
128
+ WHERE t2.result_status Is Null or ( t2.result_status != 'success' and t2.result_status != 'error' )
129
+ LIMIT " . intval( $limit );
130
+ $unoptimized = $wpdb->get_results( $sql_unoptimized );
131
+
132
+ return $unoptimized;
133
+ }
134
+
135
+ /**
136
+ * Возвращает результат последних оптимизаций изображений
137
+ *
138
+ * @param int $limit лимит
139
+ *
140
+ * @return array {
141
+ * Параметры
142
+ * @type string $id id
143
+ * @type string $file_name Имя файла
144
+ * @type string $url URL
145
+ * @type string $thumbnail_url URL превьюшки
146
+ * @type string $original_size Размер до оптимизации
147
+ * @type string $optimized_size Размер после оптимизации
148
+ * @type string $webp_size webP размер
149
+ * @type string $original_saving На сколько процентов изменился главный файл
150
+ * @type string $thumbnails_count Сколько превьюшек оптимизировано
151
+ * @type string $total_saving Процент оптимизации главного файла и превьюшек
152
+ * }
153
+ */
154
+ public function get_last_optimized_images( $limit = 100 ) {
155
+ global $wpdb;
156
+ $logs = [];
157
+ $db_table = RIO_Process_Queue::table_name();
158
+ $sql = $wpdb->prepare( "SELECT t1.*,t2.filename as file_name, t3.path as gallery_path
159
+ FROM {$db_table} as t1
160
+ LEFT JOIN {$wpdb->prefix}ngg_pictures as t2 ON t1.object_id = t2.pid
161
+ LEFT JOIN {$wpdb->prefix}ngg_gallery as t3 ON t2.galleryid = t3.gid
162
+ WHERE t1.item_type = 'nextgen' AND t1.result_status IN (%s, %s)
163
+ ORDER BY id DESC
164
+ LIMIT %d ;", RIO_Process_Queue::STATUS_SUCCESS, RIO_Process_Queue::STATUS_ERROR, $limit );
165
+ $optimized_images = $wpdb->get_results( $sql );
166
+
167
+ if ( empty( $optimized_images ) ) {
168
+ return [];
169
+ }
170
+
171
+ foreach ( $optimized_images as $row ) {
172
+ $log = [];
173
+
174
+ $optimization_data = new RIO_Process_Queue( $row );
175
+
176
+ /**
177
+ * @var WRIO_Nextgen_Extra_Data $extra_data
178
+ */
179
+ $extra_data = $optimization_data->get_extra_data();
180
+
181
+ $original_main_size = 0;
182
+
183
+ if ( ! empty( $extra_data ) ) {
184
+ $original_main_size = $extra_data->get_original_main_size();
185
+ $webp_size = $extra_data->get_webp_main_size();
186
+ if ( ! empty( $webp_size ) ) {
187
+ $log['webp_size'] = size_format( $webp_size, 2 );
188
+ } else {
189
+ $log['webp_size'] = '-';
190
+ }
191
+
192
+ $error = $extra_data->get_error();
193
+
194
+ if ( $row->result_status == RIO_Process_Queue::STATUS_ERROR || ! empty( $error ) ) {
195
+ $log['type'] = 'error';
196
+
197
+ $error_message = $extra_data->get_error_msg();
198
+
199
+ $log['error_msg'] = ! empty( $error_message ) ? $error_message : esc_html__( 'Unknown error', 'robin-image-optimizer' );
200
+ }
201
+ }
202
+
203
+ $main_file = trailingslashit( ABSPATH ) . $row->gallery_path . $row->file_name;
204
+ $main_file_optimized_size = $main_saving = $total_saving = 0;
205
+
206
+ if ( file_exists( $main_file ) ) {
207
+ $main_file_optimized_size = filesize( $main_file );
208
+ }
209
+
210
+ if ( $original_main_size ) {
211
+ $main_saving = ( $original_main_size - $main_file_optimized_size ) * 100 / $original_main_size;
212
+ }
213
+
214
+ if ( $row->original_size ) {
215
+ $total_saving = ( $row->original_size - $row->final_size ) * 100 / $row->original_size;
216
+ }
217
+
218
+ $image_url = home_url( $row->gallery_path . $row->file_name );
219
+ $thumbnail_url = home_url( $row->gallery_path . 'thumbs/thumbs_' . $row->file_name );
220
+
221
+ $logs[] = array_merge( $log, [
222
+ 'id' => $row->id,
223
+ 'url' => $image_url,
224
+ 'thumbnail_url' => $thumbnail_url,
225
+ 'file_name' => preg_replace( '/^.+[\\\\\\/]/', '', $main_file ),
226
+ 'original_size' => size_format( $row->original_size, 2 ),
227
+ 'optimized_size' => size_format( $row->final_size, 2 ),
228
+ 'original_saving' => round( $main_saving ) . '%',
229
+ 'thumbnails_count' => 1,
230
+ 'total_saving' => round( $total_saving ) . '%',
231
+ ] );
232
+ }
233
+
234
+ return $logs;
235
+ }
236
+
237
+ }
libs/addons/includes/classes/helpers/class.url.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class WRIO_Webp_Hash_Src holds hash data about type, source src and normalized src which is mainly used to compare
10
+ * or replace data.
11
+ *
12
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
13
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
14
+ * @copyright (c) 22.09.2018, Webcraftic
15
+ * @version 1.0
16
+ */
17
+ class WRIO_Url {
18
+ /**
19
+ * Check whether URI is valid or not.
20
+ *
21
+ * @param string $src Image path.
22
+ * @param bool $decode Whether to decode src.
23
+ *
24
+ * @return null|string NULL on failure to get valid uri.
25
+ */
26
+ public static function normalize ( $src, $decode = true ) {
27
+ $url_parts = wp_parse_url( $src );
28
+
29
+ // Unsupported scheme.
30
+ if ( isset( $url_parts['scheme'] ) && 'http' !== $url_parts['scheme'] && 'https' !== $url_parts['scheme'] ) {
31
+ return null;
32
+ }
33
+
34
+ // This is a relative path, try to get the URL.
35
+ if ( ! isset( $url_parts['host'] ) && ! isset( $url_parts['scheme'] ) ) {
36
+ $src = site_url( $src );
37
+ }
38
+
39
+ if ( false === strpos( $src, content_url() ) ) {
40
+ return null;
41
+ }
42
+
43
+ if($decode) {
44
+ return urldecode($src);
45
+ }
46
+
47
+ return $src;
48
+ }
49
+ }
libs/addons/includes/classes/index.php ADDED
File without changes
libs/addons/includes/classes/models/class.folders-extra-data.php ADDED
@@ -0,0 +1,259 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class WRIO_Nextgen_Extra_Data is a DTO model for `nextgen` post type used for `extra_data`
10
+ * property in RIO_Process_Queue.
11
+ *
12
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
13
+ * @see RIO_Process_Queue::$extra_data for further information
14
+ *
15
+ */
16
+ class WRIO_CF_Image_Extra_Data extends RIO_Base_Extra_Data {
17
+
18
+ /**
19
+ * @var string путь к файлу относительно папки
20
+ */
21
+ protected $file_path = null;
22
+
23
+ /**
24
+ * @var string тип ошибки
25
+ */
26
+ protected $error = null;
27
+
28
+ /**
29
+ * @var string текст сообщения об ошибке
30
+ */
31
+ protected $error_msg = null;
32
+
33
+ /**
34
+ * @var int оригинальный размер основного файла
35
+ */
36
+ protected $original_main_size = null;
37
+
38
+ /**
39
+ * @var int оптимизированный размер основного файла
40
+ */
41
+ protected $optimized_main_size = null;
42
+
43
+ /**
44
+ * @var array ответ от сервера оптимизации по основному файлу
45
+ */
46
+ protected $main_optimized_data = null;
47
+
48
+ /**
49
+ * @var string путь к папке относительно корня WP
50
+ */
51
+ protected $folder_relative_path = null;
52
+
53
+ /**
54
+ * @var int размер основного изображения
55
+ */
56
+ protected $webp_main_size = null;
57
+
58
+ /**
59
+ * get_file_path
60
+ *
61
+ * @return string
62
+ */
63
+ public function get_file_path() {
64
+ return $this->file_path;
65
+ }
66
+
67
+ /**
68
+ * set_file_path
69
+ *
70
+ * @param string $file_path
71
+ *
72
+ * @return void
73
+ */
74
+ public function set_file_path( $file_path ) {
75
+ $this->file_path = $file_path;
76
+ }
77
+
78
+ /**
79
+ * get_error
80
+ *
81
+ * @return string
82
+ */
83
+ public function get_error() {
84
+ return $this->error;
85
+ }
86
+
87
+ /**
88
+ * set_error
89
+ *
90
+ * @param string $error
91
+ *
92
+ * @return void
93
+ */
94
+ public function set_error( $error ) {
95
+ $this->error = $error;
96
+ }
97
+
98
+ /**
99
+ * get_error_msg
100
+ *
101
+ * @return string
102
+ */
103
+ public function get_error_msg() {
104
+ return $this->error_msg;
105
+ }
106
+
107
+ /**
108
+ * set_error_msg
109
+ *
110
+ * @param string $error_msg
111
+ *
112
+ * @return void
113
+ */
114
+ public function set_error_msg( $error_msg ) {
115
+ $this->error_msg = $error_msg;
116
+ }
117
+
118
+ /**
119
+ * get_original_main_size
120
+ *
121
+ * @return int
122
+ */
123
+ public function get_original_main_size() {
124
+ return $this->original_main_size;
125
+ }
126
+
127
+ /**
128
+ * set_original_main_size
129
+ *
130
+ * @param int $original_main_size
131
+ *
132
+ * @return void
133
+ */
134
+ public function set_original_main_size( $original_main_size ) {
135
+ $this->original_main_size = $original_main_size;
136
+ }
137
+
138
+ /**
139
+ * get_optimized_main_size
140
+ *
141
+ * @return int
142
+ */
143
+ public function get_optimized_main_size() {
144
+ return $this->optimized_main_size;
145
+ }
146
+
147
+ /**
148
+ * set_optimized_main_size
149
+ *
150
+ * @param int $optimized_main_size
151
+ *
152
+ * @return void
153
+ */
154
+ public function set_optimized_main_size( $optimized_main_size ) {
155
+ $this->optimized_main_size = $optimized_main_size;
156
+ }
157
+
158
+ /**
159
+ * get_main_optimized_data
160
+ *
161
+ * @return array
162
+ */
163
+ public function get_main_optimized_data() {
164
+ return (array) $this->main_optimized_data;
165
+ }
166
+
167
+ /**
168
+ * set_main_optimized_data
169
+ *
170
+ * @param array $main_optimized_data
171
+ *
172
+ * @return void
173
+ */
174
+ public function set_main_optimized_data( $main_optimized_data ) {
175
+ $this->main_optimized_data = $main_optimized_data;
176
+ }
177
+
178
+ /**
179
+ * get_folder_relative_path
180
+ *
181
+ * @return string
182
+ */
183
+ public function get_folder_relative_path() {
184
+ return $this->folder_relative_path;
185
+ }
186
+
187
+ /**
188
+ * set_folder_relative_path
189
+ *
190
+ * @param string $folder_relative_path
191
+ *
192
+ * @return void
193
+ */
194
+ public function set_folder_relative_path( $folder_relative_path ) {
195
+ $this->folder_relative_path = $folder_relative_path;
196
+ }
197
+
198
+ /**
199
+ * Возвращает абсолютный путь к папке
200
+ *
201
+ * @return string
202
+ */
203
+ public function get_folder_absolute_path() {
204
+ $relative_path = $this->get_folder_relative_path();
205
+
206
+ return wp_normalize_path( ABSPATH . $relative_path );
207
+ }
208
+
209
+ /**
210
+ * Возвращает путь к изображению относительно корня WP
211
+ *
212
+ * @return string
213
+ */
214
+ public function get_image_relative_path() {
215
+ return wp_normalize_path( $this->get_file_path() );
216
+ }
217
+
218
+ /**
219
+ * Возвращает абсолютный путь к изображению
220
+ *
221
+ * @return string
222
+ */
223
+ public function get_image_absolute_path() {
224
+ $relative_path = $this->get_image_relative_path();
225
+
226
+ return wp_normalize_path( ABSPATH . $relative_path );
227
+ }
228
+
229
+ /**
230
+ * Возвращает URL изображения
231
+ *
232
+ * @return string
233
+ */
234
+ public function get_image_url() {
235
+ $relative_path = $this->get_image_relative_path();
236
+
237
+ return home_url( $relative_path );
238
+ }
239
+
240
+ /**
241
+ * Возвращает WebP размер основного изображения
242
+ *
243
+ * @return int
244
+ */
245
+ public function get_webp_main_size() {
246
+ return $this->webp_main_size;
247
+ }
248
+
249
+ /**
250
+ * Устанавливает WebP размер основного изображения
251
+ *
252
+ * @param int $webp_main_size
253
+ *
254
+ * @return void
255
+ */
256
+ public function set_webp_main_size( $webp_main_size ) {
257
+ $this->webp_main_size = $webp_main_size;
258
+ }
259
+ }
libs/addons/includes/classes/models/class.nextgen-extra-data.php ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Class WRIO_Nextgen_Extra_Data is a DTO model for `nextgen` post type used for `extra_data`
10
+ * property in RIO_Process_Queue.
11
+ *
12
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
13
+ * @see RIO_Process_Queue::$extra_data for further information
14
+ *
15
+ */
16
+ class WRIO_Nextgen_Extra_Data extends RIO_Base_Extra_Data {
17
+
18
+ /**
19
+ * @var string тип ошибки
20
+ */
21
+ protected $error = null;
22
+
23
+ /**
24
+ * @var string текст сообщения об ошибке
25
+ */
26
+ protected $error_msg = null;
27
+
28
+ /**
29
+ * @var int оригинальный размер основного файла
30
+ */
31
+ protected $original_main_size = null;
32
+
33
+ /**
34
+ * @var int оптимизированный размер основного файла
35
+ */
36
+ protected $optimized_main_size = null;
37
+
38
+ /**
39
+ * @var array ответ от сервера оптимизации по основному файлу
40
+ */
41
+ protected $main_optimized_data = null;
42
+
43
+ /**
44
+ * @var array ответ от сервера оптимизации по превьюшке
45
+ */
46
+ protected $thumbnails_optimized_data = null;
47
+
48
+ /**
49
+ * @var string относительный путь к изображению
50
+ */
51
+ protected $image_relative_path = null;
52
+
53
+ /**
54
+ * @var int размер основного изображения
55
+ */
56
+ protected $webp_main_size = null;
57
+
58
+ /**
59
+ * get_error
60
+ *
61
+ * @return string
62
+ */
63
+ public function get_error() {
64
+ return $this->error;
65
+ }
66
+
67
+ /**
68
+ * set_error
69
+ *
70
+ * @param string $error
71
+ *
72
+ * @return void
73
+ */
74
+ public function set_error( $error ) {
75
+ $this->error = $error;
76
+ }
77
+
78
+ /**
79
+ * get_error_msg
80
+ *
81
+ * @return string
82
+ */
83
+ public function get_error_msg() {
84
+ return $this->error_msg;
85
+ }
86
+
87
+ /**
88
+ * set_error_msg
89
+ *
90
+ * @param string $error_msg
91
+ *
92
+ * @return void
93
+ */
94
+ public function set_error_msg( $error_msg ) {
95
+ $this->error_msg = $error_msg;
96
+ }
97
+
98
+ /**
99
+ * get_original_main_size
100
+ *
101
+ * @return int
102
+ */
103
+ public function get_original_main_size() {
104
+ return $this->original_main_size;
105
+ }
106
+
107
+ /**
108
+ * set_original_main_size
109
+ *
110
+ * @param int $original_main_size
111
+ *
112
+ * @return void
113
+ */
114
+ public function set_original_main_size( $original_main_size ) {
115
+ $this->original_main_size = $original_main_size;
116
+ }
117
+
118
+ /**
119
+ * get_optimized_main_size
120
+ *
121
+ * @return int
122
+ */
123
+ public function get_optimized_main_size() {
124
+ return $this->optimized_main_size;
125
+ }
126
+
127
+ /**
128
+ * set_optimized_main_size
129
+ *
130
+ * @param int $optimized_main_size
131
+ *
132
+ * @return void
133
+ */
134
+ public function set_optimized_main_size( $optimized_main_size ) {
135
+ $this->optimized_main_size = $optimized_main_size;
136
+ }
137
+
138
+ /**
139
+ * get_main_optimized_data
140
+ *
141
+ * @return array
142
+ */
143
+ public function get_main_optimized_data() {
144
+ return (array) $this->main_optimized_data;
145
+ }
146
+
147
+ /**
148
+ * set_main_optimized_data
149
+ *
150
+ * @param array $main_optimized_data
151
+ *
152
+ * @return void
153
+ */
154
+ public function set_main_optimized_data( $main_optimized_data ) {
155
+ $this->main_optimized_data = $main_optimized_data;
156
+ }
157
+
158
+ /**
159
+ * get_thumbnails_optimized_data
160
+ *
161
+ * @return array
162
+ */
163
+ public function get_thumbnails_optimized_data() {
164
+ return (array) $this->thumbnails_optimized_data;
165
+ }
166
+
167
+ /**
168
+ * set_thumbnails_optimized_data
169
+ *
170
+ * @param array $thumbnails_optimized_data
171
+ *
172
+ * @return void
173
+ */
174
+ public function set_thumbnails_optimized_data( $thumbnails_optimized_data ) {
175
+ $this->thumbnails_optimized_data = $thumbnails_optimized_data;
176
+ }
177
+
178
+ /**
179
+ * get_image_relative_path
180
+ *
181
+ * @return string
182
+ */
183
+ public function get_image_relative_path() {
184
+ return $this->image_relative_path;
185
+ }
186
+
187
+ /**
188
+ * set_image_relative_path
189
+ *
190
+ * @param string $image_relative_path
191
+ *
192
+ * @return void
193
+ */
194
+ public function set_image_relative_path( $image_relative_path ) {
195
+ $this->image_relative_path = $image_relative_path;
196
+ }
197
+
198
+ /**
199
+ * Возвращает путь к превьюшке относительно корня WP
200
+ *
201
+ * @return string
202
+ */
203
+ public function get_image_thumbnail_relative_path() {
204
+ $basename = wp_basename( $this->image_relative_path );
205
+ $dir = dirname( $this->image_relative_path );
206
+
207
+ return $dir . '/thumbs/thumbs_' . $basename;
208
+ }
209
+
210
+ /**
211
+ * Возвращает абсолютный путь к изображению
212
+ *
213
+ * @return string
214
+ */
215
+ public function get_image_absolute_path() {
216
+ $relative_path = $this->get_image_relative_path();
217
+
218
+ return wp_normalize_path( untrailingslashit( ABSPATH ) . $relative_path );
219
+ }
220
+
221
+ /**
222
+ * Возвращает абсолютный путь к превьюшке
223
+ *
224
+ * @return string
225
+ */
226
+ public function get_image_thumbnail_absolute_path() {
227
+ $relative_path = $this->get_image_thumbnail_relative_path();
228
+
229
+ return wp_normalize_path( untrailingslashit( ABSPATH ) . $relative_path );
230
+ }
231
+
232
+ /**
233
+ * Возвращает URL изображения
234
+ *
235
+ * @return string
236
+ */
237
+ public function get_image_url() {
238
+ $relative_path = $this->get_image_relative_path();
239
+
240
+ return home_url( $relative_path );
241
+ }
242
+
243
+ /**
244
+ * Возвращает URL изображения-превьюшки
245
+ *
246
+ * @return string
247
+ */
248
+ public function get_image_thumbnail_url() {
249
+ $relative_path = $this->get_image_thumbnail_relative_path();
250
+
251
+ return home_url( $relative_path );
252
+ }
253
+
254
+ /**
255
+ * Возвращает WebP размер основного изображения
256
+ *
257
+ * @return int
258
+ */
259
+ public function get_webp_main_size() {
260
+ return $this->webp_main_size;
261
+ }
262
+
263
+ /**
264
+ * Устанавливает WebP размер основного изображения
265
+ *
266
+ * @param int $webp_main_size
267
+ *
268
+ * @return void
269
+ */
270
+ public function set_webp_main_size( $webp_main_size ) {
271
+ $this->webp_main_size = $webp_main_size;
272
+ }
273
+ }
libs/addons/includes/classes/models/class.webp-extra-data.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class RIOP_WebP_Extra_Data.
5
+ *
6
+ * @property string $source_src
7
+ *
8
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
9
+ */
10
+ class RIOP_WebP_Extra_Data extends RIO_Base_Extra_Data {
11
+ /**
12
+ * @var null|string E.g. attachment, nextgen, etc
13
+ */
14
+ protected $convert_from = null;
15
+
16
+ /**
17
+ * @var null|string|int
18
+ */
19
+ protected $converted_from_size = null;
20
+
21
+ /**
22
+ * @var string|null Image source src.
23
+ */
24
+ protected $source_src = null;
25
+
26
+ /**
27
+ * @var string|null Image absolute path.
28
+ */
29
+ protected $source_path = null;
30
+
31
+ /**
32
+ * @var string|null Converted WebP image src.
33
+ */
34
+ protected $converted_src = null;
35
+
36
+ /**
37
+ * @var string|null Converted WebP absolute path.
38
+ */
39
+ protected $converted_path = null;
40
+
41
+ /**
42
+ * @var int|null Post ID.
43
+ */
44
+ protected $post_id = null;
45
+
46
+ /**
47
+ * @param string $source_src
48
+ */
49
+ public function set_source_src ( $source_src ) {
50
+ $this->source_src = trim( $source_src );
51
+ }
52
+
53
+ /**
54
+ * Get source property.
55
+ *
56
+ * @param bool $decoded Whether to decode src or not.
57
+ *
58
+ * @return string
59
+ */
60
+ public function get_source_src ( $decoded = true ) {
61
+ $src = $this->source_src;
62
+
63
+ if ( $decoded ) {
64
+ return urldecode( $src );
65
+ }
66
+
67
+ return $src;
68
+ }
69
+
70
+ /**
71
+ * @return null|string
72
+ */
73
+ public function get_source_path () {
74
+ return $this->source_path;
75
+ }
76
+
77
+ /**
78
+ * @param null|string $source_path
79
+ */
80
+ public function set_source_path ( $source_path ) {
81
+ $this->source_path = $source_path;
82
+ }
83
+
84
+ /**
85
+ * @return null|string
86
+ */
87
+ public function get_convert_from () {
88
+ return $this->convert_from;
89
+ }
90
+
91
+ /**
92
+ * @param null|string $convert_from
93
+ */
94
+ public function set_convert_from ( $convert_from ) {
95
+ $this->convert_from = $convert_from;
96
+ }
97
+
98
+ /**
99
+ * @return int|null|string
100
+ */
101
+ public function get_converted_from_size () {
102
+ return $this->converted_from_size;
103
+ }
104
+
105
+ /**
106
+ * @param int|null|string $converted_from_size
107
+ */
108
+ public function set_converted_from_size ( $converted_from_size ) {
109
+ $this->converted_from_size = $converted_from_size;
110
+ }
111
+
112
+ /**
113
+ * @return string
114
+ */
115
+ public function get_converted_path () {
116
+ return $this->converted_path;
117
+ }
118
+
119
+ /**
120
+ * @param string $converted_path
121
+ */
122
+ public function set_converted_path ( $converted_path ) {
123
+ $this->converted_path = $converted_path;
124
+ }
125
+
126
+ /**
127
+ * @return string
128
+ */
129
+ public function get_converted_src () {
130
+ return $this->converted_src;
131
+ }
132
+
133
+ /**
134
+ * @param string $converted_src
135
+ */
136
+ public function set_converted_src ( $converted_src ) {
137
+ $this->converted_src = $converted_src;
138
+ }
139
+
140
+ /**
141
+ * @return int
142
+ */
143
+ public function get_post_id () {
144
+ return $this->post_id;
145
+ }
146
+
147
+ /**
148
+ * @param int $post_id
149
+ *
150
+ * @return RIOP_WebP_Extra_Data
151
+ */
152
+ public function set_post_id ( $post_id ) {
153
+ $this->post_id = $post_id;
154
+
155
+ return $this;
156
+ }
157
+ }
libs/addons/includes/classes/webp/class-webp-api.php ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class WRIO_WebP_Api processing images from processing queue, sends them to API and saves locally.
5
+ *
6
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
7
+ */
8
+ class WRIO_WebP_Api {
9
+
10
+ /**
11
+ * @var string API url.
12
+ */
13
+ private $_api_url = 'http://142.93.91.206/';
14
+
15
+ /**
16
+ * @var int|null Attachment ID.
17
+ */
18
+ private $_models = null;
19
+
20
+ /**
21
+ * @var null|int UNIX epoch when last request was processed.
22
+ */
23
+ private $_last_request_tick = null;
24
+
25
+
26
+ /**
27
+ * WRIO_WebP_Api constructor.
28
+ *
29
+ * @param RIO_Process_Queue[] $model Item to be converted to WebP.
30
+ */
31
+ public function __construct( $model ) {
32
+ $this->_models = $model;
33
+ }
34
+
35
+ /**
36
+ * Process image queue based on provided attachment ID.
37
+ *
38
+ * When attachment has multiple thumbnails, all of them would be converted one after another.
39
+ *
40
+ * Notice: when there are no items queried for provided data, false would be returned.
41
+ *
42
+ * @return bool true on success execution, false on failure or missing item in queue.
43
+ */
44
+ public function process_image_queue() {
45
+
46
+ foreach ( $this->_models as $model ) {
47
+ /**
48
+ * @var RIOP_WebP_Extra_Data $extra_data
49
+ */
50
+ $extra_data = $model->get_extra_data();
51
+
52
+ if ( $extra_data === null ) {
53
+ continue;
54
+ }
55
+
56
+ $response = $this->request( $model );
57
+
58
+ if ( $this->can_save( $response ) && $this->save_file( $response, $model ) ) {
59
+ $this->update( $model );
60
+ }
61
+ }
62
+
63
+ return true;
64
+ }
65
+
66
+ /**
67
+ * Request API
68
+ *
69
+ * @param RIO_Process_Queue $model Queue model.
70
+ *
71
+ * @return array|bool|WP_Error
72
+ */
73
+ public function request( $model ) {
74
+
75
+ if ( $this->_last_request_tick === null ) {
76
+ $this->_last_request_tick = time();
77
+ } else {
78
+ if ( is_int( $this->_last_request_tick ) && ( time() - $this->_last_request_tick ) < 1 ) {
79
+ // Need to have some rest before calling REST :D to comply with API request limit
80
+ sleep( 2 );
81
+ }
82
+
83
+ $this->_last_request_tick = time();
84
+ }
85
+
86
+ if ( ! wrio_is_license_activate() ) {
87
+ WRIO_Logger::error( "Unable to get license to make proper request to the API" );
88
+
89
+ return false;
90
+ }
91
+
92
+ $transient_string = md5( WRIO_Plugin::app()->getPrefix() . '_processing_image' . $model->get_item_hash() );
93
+
94
+ $transient_value = get_transient( $transient_string );
95
+
96
+ if ( is_numeric( $transient_value ) && (int) $transient_value === 1 ) {
97
+ WRIO_Logger::info( sprintf( 'Skipping to wp_remote_get() as transient "%s" already exist. Usually it means that no request was returned yet', $transient_string ) );
98
+
99
+ return false;
100
+ }
101
+
102
+ set_transient( $transient_string, 1 );
103
+
104
+ $url = $this->_api_url . 'v1/image/convert?';
105
+
106
+ $url .= http_build_query( [ 'format' => 'webp' ] );
107
+
108
+ /**
109
+ * @var RIOP_WebP_Extra_Data $extra_data
110
+ */
111
+ $extra_data = $model->get_extra_data();
112
+
113
+ $multipartBoundary = '--------------------------' . microtime( true );
114
+
115
+ $file_contents = file_get_contents( $extra_data->get_source_path() );
116
+
117
+ $body = "--" . $multipartBoundary . "\r\n" . "Content-Disposition: form-data; name=\"file\"; filename=\"" . basename( $extra_data->get_source_path() ) . "\"\r\n" . "Content-Type: " . $model->get_original_mime_type() . "\r\n\r\n" . $file_contents . "\r\n";
118
+
119
+ $body .= "--" . $multipartBoundary . "--\r\n";
120
+
121
+ $headers = [
122
+ // should be base64 encoded, otherwise API would fail authentication
123
+ 'Authorization' => 'Bearer ' . base64_encode( wrio_get_license_key() ),
124
+ 'PluginId' => wrio_get_freemius_plugin_id(),
125
+ 'Content-Type' => 'multipart/form-data; boundary=' . $multipartBoundary,
126
+ ];
127
+
128
+ $response = wp_remote_post( $url, [
129
+ 'timeout' => 60,
130
+ 'headers' => $headers,
131
+ 'body' => $body,
132
+ ] );
133
+
134
+ delete_transient( $transient_string );
135
+
136
+ return $response;
137
+ }
138
+
139
+ /**
140
+ * Process response from API.
141
+ *
142
+ * @param array|WP_Error|false $response
143
+ *
144
+ * @return bool True means response image was successfully saved, false on failure.
145
+ */
146
+ public function can_save( $response ) {
147
+ \WRIO_Logger::info( 'WebP convertation: Checks to save a webp by response.' );
148
+ //\WRIO_Logger::debug( var_export( $response, true ) );
149
+
150
+ if ( is_wp_error( $response ) ) {
151
+ WRIO_Logger::error( sprintf( 'Error response from API. Code: %s, error: %s', $response->get_error_code(), $response->get_error_message() ) );
152
+
153
+ return false;
154
+ }
155
+
156
+ if ( false === $response ) {
157
+ WRIO_Logger::error( 'Unknown response returned from API or it was not requested, failing to process response' );
158
+
159
+ return false;
160
+ }
161
+
162
+ $content_disposition = wp_remote_retrieve_header( $response, 'content-disposition' );
163
+
164
+ if ( 0 === strpos( $content_disposition, 'attachment;' ) ) {
165
+
166
+ $body = wp_remote_retrieve_body( $response );
167
+
168
+ if ( empty( $body ) ) {
169
+ WRIO_Logger::error( 'Response returned content-disposition header as "attachment;", but empty body returned, failing to proceed' );
170
+
171
+ return false;
172
+ }
173
+
174
+ \WRIO_Logger::info( 'WebP convertation: Image can be saved. ' );
175
+
176
+ return true;
177
+ }
178
+
179
+ $response_text = wp_remote_retrieve_body( $response );
180
+
181
+ if ( ! empty( $response_text ) ) {
182
+ $response_json = json_decode( $response_text );
183
+
184
+ if ( ! empty( $response_json ) ) {
185
+ if ( isset( $response_json->error ) && ! empty( $response_json->error ) ) {
186
+ WRIO_Logger::error( sprintf( 'Unable to convert attachment as API returned error: "%s"', $response_json->error ) );
187
+ }
188
+
189
+ if ( isset( $response_json->status ) && 401 === (int) $response_json->status ) {
190
+ WRIO_Logger::error( sprintf( 'Error response from API. Code: %s, error: %s', $response_json->message, $response_json->code ) );
191
+ }
192
+ }
193
+ }
194
+
195
+ return false;
196
+ }
197
+
198
+ /**
199
+ * Save file from response.
200
+ *
201
+ * It is assumed that it was checked by can_save() method.
202
+ *
203
+ * @param array|WP_Error|false $response
204
+ * @param RIO_Process_Queue $queue_model
205
+ *
206
+ * @return bool
207
+ * @see can_save() for further information.
208
+ *
209
+ */
210
+ public function save_file( $response, $queue_model ) {
211
+
212
+ try {
213
+ $save_path = static::get_save_path( $queue_model );
214
+ } catch( \Exception $exception ) {
215
+ WRIO_Logger::error( sprintf( 'Unable to process response failed to get save path: "%s"', $exception->getMessage() ) );
216
+
217
+ return false;
218
+ }
219
+
220
+ \WRIO_Logger::info( sprintf( 'WebP convertation: Try to save webp image in %s.', $save_path ) );
221
+
222
+ $body = wp_remote_retrieve_body( $response );
223
+
224
+ $file_saved = @file_put_contents( $save_path, $body );
225
+
226
+ if ( ! $file_saved ) {
227
+ /**
228
+ * @var $http_response WP_HTTP_Requests_Response
229
+ */
230
+ $http_response = $response['http_response'];
231
+ WRIO_Logger::error( sprintf( 'Failed to save file "%s" under %s with file_put_contents()', $save_path, $http_response->get_response_object()->url ) );
232
+
233
+ return false;
234
+ }
235
+
236
+ \WRIO_Logger::info( 'WebP convertation: Image saved successfully!' );
237
+
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * Update processing item data to finish its cycle.
243
+ *
244
+ * @param RIO_Process_Queue $queue_model Queue model to be update.
245
+ *
246
+ * @return bool
247
+ */
248
+ public function update( $queue_model ) {
249
+
250
+ try {
251
+ $save_path = static::get_save_path( $queue_model );
252
+ } catch( \Exception $exception ) {
253
+ WRIO_Logger::error( sprintf( 'Unable to update queue model #%s as of exception: %s', $queue_model->get_id(), $exception->getMessage() ) );
254
+
255
+ return false;
256
+ }
257
+
258
+ $queue_model->result_status = RIO_Process_Queue::STATUS_SUCCESS;
259
+ $queue_model->final_size = filesize( $save_path );
260
+
261
+ /**
262
+ * @var RIOP_WebP_Extra_Data $updated_extra_data
263
+ */
264
+ $updated_extra_data = $queue_model->get_extra_data();
265
+ $updated_extra_data->set_converted_src( $this->get_save_url( $queue_model ) );
266
+ $updated_extra_data->set_converted_path( $save_path );
267
+
268
+ $queue_model->extra_data = $updated_extra_data;
269
+
270
+ /**
271
+ * Хук срабатывает после успешной конвертации в WebP
272
+ *
273
+ * @since 1.2.0
274
+ *
275
+ * @param RIO_Process_Queue $queue_model
276
+ *
277
+ */
278
+ do_action( 'wbcr/rio/webp_success', $queue_model );
279
+
280
+ return $queue_model->save();
281
+ }
282
+
283
+ /**
284
+ * Get complete save url.
285
+ *
286
+ * @param RIO_Process_Queue $queue_model Instance of queue item.
287
+ *
288
+ * @return string
289
+ */
290
+ public function get_save_url( $queue_model ) {
291
+ /**
292
+ * @var $extra_data RIOP_WebP_Extra_Data
293
+ */
294
+ $extra_data = $queue_model->get_extra_data();
295
+
296
+ if ( empty( $extra_data ) ) {
297
+ WRIO_Logger::error( sprintf( 'Unable to get extra data for queue item #%s', $queue_model->get_id() ) );
298
+
299
+ return null;
300
+ }
301
+
302
+ $origin_file_name = wp_basename( $extra_data->get_source_src() );
303
+ $webp_file_name = trim( wp_basename( $extra_data->get_source_path() ) ) . '.webp';
304
+
305
+ return str_replace( $origin_file_name, $webp_file_name, $extra_data->get_source_src() );
306
+ }
307
+
308
+ /**
309
+ * Get absolute save path.
310
+ *
311
+ * @param \RIO_Process_Queue $queue_model
312
+ *
313
+ * @return bool
314
+ * @throws Exception on failure to create missing directory
315
+ */
316
+ public static function get_save_path( $queue_model ) {
317
+ /**
318
+ * @var $extra_data RIOP_WebP_Extra_Data
319
+ */
320
+ $extra_data = $queue_model->get_extra_data();
321
+
322
+ if ( empty( $extra_data ) ) {
323
+ WRIO_Logger::error( sprintf( 'Unable to get extra data for queue item #%s', $queue_model->get_id() ) );
324
+
325
+ return null;
326
+ }
327
+
328
+ $path = dirname( $extra_data->get_source_path() );
329
+
330
+ // Create DIR when does not exist
331
+ if ( ! file_exists( $path ) ) {
332
+ $message = sprintf( 'Failed to create directory %s with mode %s recursively', $path, 0755 );
333
+ WRIO_Logger::error( $message );
334
+ throw new \Exception( $message );
335
+ }
336
+
337
+ return trailingslashit( $path ) . trim( wp_basename( $extra_data->get_source_path() ) ) . '.webp';
338
+ }
339
+ }
libs/addons/includes/classes/webp/class-webp-delivery.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WRIO\WEBP\HTML;
4
+
5
+ // Exit if accessed directly
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
+
10
+ /**
11
+ * Class WRIO\WEBP\HTML\Delivery converts and replace JPEG & PNG images within HTML doc.
12
+ *
13
+ * Images converted via third-party service, saved locally and then replaced based on parsed DOM <img>, or other elements.
14
+ *
15
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
16
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
17
+ * @link https://css-tricks.com/using-webp-images/
18
+ * @link https://dev.opera.com/articles/responsive-images/#different-image-types-use-case
19
+ * @link https://ru.wordpress.org/plugins/webp-express/
20
+ * @link https://github.com/rosell-dk/
21
+ * @version 1.0
22
+ */
23
+ class Delivery {
24
+
25
+ const DEFAULT_DELIVERY_MODE = 'none';
26
+ const PICTURE_DELIVERY_MODE = 'picture';
27
+ const URL_DELIVERY_MODE = 'url';
28
+ const REDIRECT_DELIVERY_MODE = 'redirect';
29
+
30
+ /**
31
+ * WRIO_Webp constructor.
32
+ */
33
+ public function __construct() {
34
+ $this->init();
35
+ }
36
+
37
+ /**
38
+ * Initiate the class.
39
+ */
40
+ public function init() {
41
+ if ( static::is_webp_enabled() ) {
42
+
43
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
44
+ \WRIO_Logger::info( sprintf( "WebP option enabled and browser \"%s\" is supported, ready to process buffer", isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '*undefined*' ) );
45
+ \WRIO_Logger::info( sprintf( "WebP delivery mode: %s", static::get_delivery_mode() ) );
46
+ }
47
+
48
+ //if ( ! is_admin() && $this->is_supported_browser() ) {
49
+ //add_action( 'template_redirect', [ $this, 'process_buffer' ], 1 );
50
+ //}
51
+
52
+ //if (get_option('webp-express-alter-html-hooks', 'ob') == 'ob') {
53
+ /* TODO:
54
+ Which hook should we use, and should we make it optional?
55
+ - Cache enabler uses 'template_redirect'
56
+ - ShortPixes uses 'init'
57
+
58
+ We go with template_redirect now, because it is the "innermost".
59
+ This lowers the risk of problems with plugins used rewriting URLs to point to CDN.
60
+ (We need to process the output *before* the other plugin has rewritten the URLs,
61
+ if the "Only for webps that exists" feature is enabled)
62
+ */
63
+
64
+ if ( ! static::is_default_delivery_mode() || ! static::is_redirect_delivery_mode() ) {
65
+ add_action( 'init', [ $this, 'process_buffer' ], 1 );
66
+ }
67
+
68
+ if ( static::is_picture_delivery_mode() ) {
69
+ add_action( 'wp_head', [ $this, 'add_picture_fill_js' ] );
70
+ }
71
+
72
+ //} else {
73
+ //add_filter( 'the_content', 'webPExpressAlterHtml', 10000 ); // priority big, so it will be executed last
74
+ //add_filter( 'the_excerpt', 'webPExpressAlterHtml', 10000 );
75
+ //add_filter( 'post_thumbnail_html', 'webPExpressAlterHtml');
76
+ //}
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Check whether WebP options enabled or not.
82
+ *
83
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
84
+ * @since 1.0.4
85
+ *
86
+ * @return bool
87
+ */
88
+ public static function is_webp_enabled() {
89
+ return (bool) \WRIO_Plugin::app()->getPopulateOption( 'convert_webp_format' );
90
+ }
91
+
92
+ /**
93
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
94
+ * @since 1.0.4
95
+ * @return bool
96
+ */
97
+ public static function is_redirect_delivery_mode() {
98
+ return self::REDIRECT_DELIVERY_MODE === static::get_delivery_mode();
99
+ }
100
+
101
+ /**
102
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
103
+ * @since 1.0.4
104
+ * @return bool
105
+ */
106
+ public static function is_picture_delivery_mode() {
107
+ return self::PICTURE_DELIVERY_MODE === static::get_delivery_mode();
108
+ }
109
+
110
+ /**
111
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
112
+ * @since 1.0.4
113
+ * @return bool
114
+ */
115
+ public static function is_url_delivery_mode() {
116
+ return self::URL_DELIVERY_MODE === static::get_delivery_mode();
117
+ }
118
+
119
+ /**
120
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
121
+ * @since 1.0.4
122
+ * @return bool
123
+ */
124
+ public static function is_default_delivery_mode() {
125
+ return self::DEFAULT_DELIVERY_MODE === static::get_delivery_mode();
126
+ }
127
+
128
+ /**
129
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
130
+ * @since 1.0.4
131
+ * @return string
132
+ */
133
+ public static function get_delivery_mode() {
134
+ $delivery_mode = \WRIO_Plugin::app()->getPopulateOption( 'webp_delivery_mode' );
135
+
136
+ if ( ! empty( $delivery_mode ) ) {
137
+ return $delivery_mode;
138
+ }
139
+
140
+ return self::DEFAULT_DELIVERY_MODE;
141
+ }
142
+
143
+ /**
144
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
145
+ * @since 1.0.4
146
+ */
147
+ public function add_picture_fill_js() {
148
+ // Don't do anything with the RSS feed.
149
+ // - and no need for PictureJs in the admin
150
+ if ( is_feed() || is_admin() ) {
151
+ return;
152
+ }
153
+
154
+ echo '<script>' . 'document.createElement( "picture" );' . 'if(!window.HTMLPictureElement && document.addEventListener) {' . 'window.addEventListener("DOMContentLoaded", function() {' . 'var s = document.createElement("script");' . 's.src = "' . WRIOP_PLUGIN_URL . '/assets/js/picturefill.min.js' . '";' . 'document.body.appendChild(s);' . '});' . '}' . '</script>';
155
+ }
156
+
157
+ /**
158
+ * Process HTML template buffer.
159
+ */
160
+ public function process_buffer() {
161
+ if ( ! is_admin() || ( function_exists( "wp_doing_ajax" ) && wp_doing_ajax() ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
162
+ ob_start( [ $this, 'process_alter_html' ] );
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Process tags to replace those elements which match converted to WebP within buffer.
168
+ *
169
+ * @param string $content HTML buffer.
170
+ *
171
+ * @return string
172
+ */
173
+ public function process_alter_html( $content ) {
174
+ $raw_content = $content;
175
+
176
+ // Don't do anything with the RSS feed.
177
+ if ( is_feed() || empty( $content ) ) {
178
+ //WRIO_Logger::info( "Buffer content is empty, skipping processing" );
179
+ return $content;
180
+ }
181
+
182
+ if ( static::is_picture_delivery_mode() ) {
183
+ if ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ) {
184
+ //for AMP pages the <picture> tag is not allowed
185
+ return $content;
186
+ }
187
+
188
+ require_once WRIOP_PLUGIN_DIR . '/includes/classes/webp/class-webp-html-picture-tags.php';
189
+ $content = Picture_Tags::replace( $content );
190
+ } else if ( static::is_url_delivery_mode() ) {
191
+ require_once( WRIOP_PLUGIN_DIR . '/includes/classes/webp/class-webp-html-image-urls-replacer.php' );
192
+ $content = Urls_Replacer::replace( $content );
193
+ }
194
+
195
+ // If the search and replacement are completed with an error, then return the raw content.
196
+ // If this is not prevented, in case of an error the user will receive a white screen.
197
+ if ( empty( $content ) ) {
198
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
199
+ \WRIO_Logger::warning( sprintf( "Failed search and replace urls. Empty result received (%s).", base64_encode( $content ) ) );
200
+ }
201
+
202
+ return $raw_content;
203
+ }
204
+
205
+ return $content;
206
+ }
207
+
208
+ /**
209
+ * Get url for webp
210
+ * returns second argument if no webp
211
+ *
212
+ * @param string $source_url (ie http://example.com/wp-content/image.jpg)
213
+ * @param string $return_value_on_fail
214
+ *
215
+ * @return string
216
+ */
217
+ public static function get_webp_url( $source_url, $return_value_on_fail ) {
218
+
219
+ // Currently we do not handle relative urls - so we skip
220
+ if ( ! preg_match( '#^https?://#', $source_url ) ) {
221
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
222
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Invalid image url\r\nSource url: %s", $source_url ) );
223
+ }
224
+
225
+ return $return_value_on_fail;
226
+ }
227
+
228
+ if ( ! preg_match( '#(jpe?g|png)$#', $source_url ) ) {
229
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
230
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Unsupported image format\r\nSource url: %s", $source_url ) );
231
+ }
232
+
233
+ return $return_value_on_fail;
234
+ }
235
+
236
+ // If the image is stored on a remote server, need to skip it
237
+ if ( strpos( $source_url, get_site_url() ) === false ) {
238
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
239
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Image is on a remote server\r\nSource url: %s", $source_url ) );
240
+ }
241
+
242
+ return $return_value_on_fail;
243
+ }
244
+
245
+ if ( static::is_url_delivery_mode() && ! static::is_supported_browser() ) {
246
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
247
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Browser does not support webp images\r\nBrowser: %s", $_SERVER['HTTP_USER_AGENT'] ) );
248
+ }
249
+
250
+ return $return_value_on_fail;
251
+ }
252
+
253
+ $relative_file_path = wrio_convert_url_to_abs_path( $source_url );
254
+
255
+ // If you could not find original image, skip it. Perhaps an error
256
+ // in absolute path formation to the directory where the
257
+ // image is stored.
258
+ if ( empty( $relative_file_path ) || ! file_exists( $relative_file_path ) ) {
259
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
260
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Unable to find origin image\r\nRelative path: (%s)\r\nSource url: (%s)", $relative_file_path, $source_url ) );
261
+ }
262
+
263
+ return $return_value_on_fail;
264
+ }
265
+
266
+ $webp_file_url = $source_url . '.webp';
267
+ $webp_file_path = $relative_file_path . '.webp';
268
+
269
+ if ( ! file_exists( $webp_file_path ) ) {
270
+ if ( \WRIO_Logger::is_keep_error_log_on_frontend() ) {
271
+ \WRIO_Logger::warning( sprintf( "Failed getting webp url. Webp image is not found\r\nSource url: %s\r\nWebP url: %s\r\nWebP path: %s", $source_url, $webp_file_url, $webp_file_path ) );
272
+ }
273
+
274
+ return $return_value_on_fail;
275
+ }
276
+
277
+ return $webp_file_url;
278
+ }
279
+
280
+ /**
281
+ * Check whether browser supports WebP or not.
282
+ *
283
+ * @return bool
284
+ */
285
+ public static function is_supported_browser() {
286
+ if ( isset( $_SERVER['HTTP_ACCEPT'] ) && strpos( $_SERVER['HTTP_ACCEPT'], 'image/webp' ) !== false || isset( $_SERVER['HTTP_USER_AGENT'] ) && strpos( $_SERVER['HTTP_USER_AGENT'], ' Chrome/' ) !== false ) {
287
+ return true;
288
+ }
289
+
290
+ return false;
291
+ }
292
+ }
libs/addons/includes/classes/webp/class-webp-html-image-urls-replacer.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WRIO\WEBP\HTML;
4
+
5
+ use DOMUtilForWebP\ImageUrlReplacer;
6
+
7
+ class Urls_Replacer extends ImageUrlReplacer {
8
+
9
+ public function replaceUrl( $url ) {
10
+ return Delivery::get_webp_url( $url, null );
11
+ }
12
+
13
+ public function attributeFilter( $attrName ) {
14
+ // Allow "src", "srcset" and data-attributes that smells like they are used for images
15
+ // The following rule matches all attributes used for lazy loading images that we know of
16
+ return preg_match( '#^(src|srcset|(data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*))$#i', $attrName );
17
+
18
+ // If you want to limit it further, only allowing attributes known to be used for lazy load,
19
+ // use the following regex instead:
20
+ //return preg_match('#^(src|srcset|data-(src|srcset|cvpsrc|cvpset|thumb|bg-url|large_image|lazyload|source-url|srcsmall|srclarge|srcfull|slide-img|lazy-original))$#i', $attrName);
21
+ }
22
+
23
+ }
libs/addons/includes/classes/webp/class-webp-html-picture-tags.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WRIO\WEBP\HTML;
4
+
5
+ /**
6
+ * Class AlterHtmlPicture - convert an <img> tag to a <picture> tag and add the webp versions of the images
7
+ * Based this code on code from the ShortPixel plugin, which used code from Responsify WP plugin
8
+ */
9
+
10
+ use DOMUtilForWebP\PictureTags;
11
+
12
+ class Picture_Tags extends PictureTags {
13
+
14
+ public function replaceUrl( $url ) {
15
+ return Delivery::get_webp_url( $url, null );
16
+ }
17
+ }
libs/addons/includes/classes/webp/class-webp-listener.php ADDED
@@ -0,0 +1,511 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WRIO\WEBP;
4
+
5
+ // Exit if accessed directly
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit;
8
+ }
9
+
10
+ use WRIO\WEBP\HTML\Delivery;
11
+
12
+ /**
13
+ * Class Listener listens to new events via hooks.
14
+ *
15
+ * For example, once attachment optimized and if WebP option enabled it will kicked and converted.
16
+ *
17
+ * Same applies for custom folder and NextGen plugin.
18
+ *
19
+ * @author Webcraftic <wordpress.webraftic@gmail.com>
20
+ * @author Alexander Teshabaev <sasha.tesh@gmail.com>
21
+ * @copyright (c) 22.09.2018, Webcraftic
22
+ * @version 1.0
23
+ */
24
+ class Listener {
25
+
26
+ /**
27
+ * Default type.
28
+ */
29
+ const DEFAULT_TYPE = 'webp';
30
+
31
+ /**
32
+ * @var null|\RIO_Process_Queue[] Saved queue items.
33
+ */
34
+ private $_saved_models = null;
35
+
36
+ /**
37
+ * WRIO_Webp constructor.
38
+ */
39
+ public function __construct() {
40
+ $this->init();
41
+ }
42
+
43
+ /**
44
+ * Init the object.
45
+ */
46
+ public function init() {
47
+ if ( Delivery::is_webp_enabled() ) {
48
+ add_action( 'wbcr/riop/queue_item_saved', function ( $model ) {
49
+
50
+ /**
51
+ * @var \RIO_Process_Queue $model
52
+ */
53
+ if ( $model->get_item_type() !== self::DEFAULT_TYPE ) { // Otherwise it can become recursive
54
+
55
+ $this->process_queue_item( $model );
56
+
57
+ if ( ! empty( $this->_saved_models ) ) {
58
+ ( new \WRIO_WebP_Api( $this->_saved_models ) )->process_image_queue();
59
+ }
60
+
61
+ $this->_saved_models = null;
62
+ }
63
+ } );
64
+ }
65
+
66
+ add_action( 'wbcr/rio/attachment_restored', [ $this, 'process_attachment_restore' ] );
67
+ add_action( 'wbcr/rio/cf_image_restored', [ $this, 'process_attachment_restore' ] );
68
+ add_action( 'wbcr/rio/nextgen_image_restored', [ $this, 'process_attachment_restore' ] );
69
+ }
70
+
71
+ /**
72
+ * Process attachment restore.
73
+ *
74
+ * @param \RIO_Process_Queue $model
75
+ *
76
+ * @return bool
77
+ */
78
+ public function process_attachment_restore( $model ) {
79
+ $item_params = [
80
+ 'object_id' => $model->get_object_id(),
81
+ 'item_type' => Listener::DEFAULT_TYPE,
82
+ ];
83
+ if ( 'cf_image' == $model->get_item_type() ) {
84
+ unset( $item_params['object_id'] ); // для custom folders не нужен номер объекта
85
+ /**
86
+ * @var $extra_data \WRIO_CF_Image_Extra_Data
87
+ */
88
+ $extra_data = $model->get_extra_data();
89
+ $item_params['item_hash'] = hash( 'sha256', $extra_data->get_image_url() );
90
+ }
91
+
92
+ $delete_items = \RIO_Process_Queue::find_all( $item_params );
93
+
94
+ if ( empty( $delete_items ) ) {
95
+ return false;
96
+ }
97
+
98
+ foreach ( $delete_items as $item ) {
99
+ /**
100
+ * @var $extra \RIOP_WebP_Extra_Data
101
+ */
102
+ $extra = $item->get_extra_data();
103
+
104
+ if ( empty( $extra ) ) {
105
+ \WRIO_Logger::warning( sprintf( 'Failed to clean-up queue item #%s as it is missing extra data', $item->get_id() ) );
106
+ continue;
107
+ }
108
+
109
+ $converted_path = $extra->get_converted_path();
110
+ if ( ! empty( $converted_path ) ) {
111
+ if ( @unlink( $converted_path ) ) {
112
+ \WRIO_Logger::info( sprintf( 'Unlinked %s from disk, ready to delete item #%s from DB', $converted_path, $item->get_id() ) );
113
+ } else {
114
+ \WRIO_Logger::error( sprintf( 'Failed to unlink %s from disk', $converted_path ) );
115
+ }
116
+ }
117
+
118
+ if ( $item->delete() ) {
119
+ \WRIO_Logger::info( sprintf( 'Deleted #%s as attachment #%s was recovered', $item->get_id(), $item->get_object_id() ) );
120
+ } else {
121
+ \WRIO_Logger::error( sprintf( 'Failed to delete queue item #%s as delete() method failed', $item->get_id() ) );
122
+ }
123
+ }
124
+
125
+ return true;
126
+ }
127
+
128
+ /**
129
+ * Process new queue item.
130
+ *
131
+ * @param \RIO_Process_Queue $model Model to process.
132
+ *
133
+ * @return bool
134
+ */
135
+ public function process_queue_item( $model ) {
136
+ if ( ! ( $model instanceof \RIO_Process_Queue ) ) {
137
+ \WRIO_Logger::info( 'Model must be instance of RIO_Process_Queue to be process by %s' . __FUNCTION__ );
138
+
139
+ return false;
140
+ }
141
+
142
+ if ( ! $model->is_optimized() ) {
143
+ \WRIO_Logger::info( sprintf( 'Skipping to process attachment #%s as it is not optimized', $model->get_id() ) );
144
+
145
+ return false;
146
+ }
147
+
148
+ switch ( $model->get_item_type() ) {
149
+ case 'attachment':
150
+ $this->process_attachment( $model );
151
+ break;
152
+ case 'cf_image':
153
+ $this->process_custom_folder( $model );
154
+ break;
155
+ case 'nextgen';
156
+ $this->process_nextgen( $model );
157
+ break;
158
+ }
159
+
160
+ return true;
161
+ }
162
+
163
+ /**
164
+ * Process attachment.
165
+ *
166
+ * Finds attachment by id, gets its src and srcset and saves them on the database.
167
+ *
168
+ * After this, images are processed by Cron or manually via admin GUI.
169
+ *
170
+ * @param \RIO_Process_Queue $model Attachment model to process.
171
+ *
172
+ * @return bool
173
+ */
174
+ public function process_attachment( $model ) {
175
+
176
+ \WRIO_Logger::info( sprintf( 'Start webp convertation proccess for attachment #%s', $model->get_id() ) );
177
+
178
+ $attachment = get_post( $model->get_object_id() );
179
+
180
+ if ( empty( $attachment ) ) {
181
+ \WRIO_Logger::warning( sprintf( 'Webp convertation: No attachment found by #%s', $model->get_object_id() ) );
182
+
183
+ return false;
184
+ }
185
+
186
+ $allowed_mimes = [ 'image/jpeg', 'image/png', 'image/gif' ];
187
+
188
+ if ( ! in_array( $attachment->post_mime_type, $allowed_mimes ) ) {
189
+ \WRIO_Logger::warning( sprintf( 'Webp convertation: Attachment #%s with MIME type %s cannot be processed as only these are allowed: %s', $attachment->ID, $attachment->post_mime_type, implode( ', ', $allowed_mimes ) ) );
190
+
191
+ return false;
192
+ }
193
+
194
+ $attachment_meta = static::get_attachment_data( $attachment );
195
+
196
+ if ( empty( $attachment_meta ) ) {
197
+ \WRIO_Logger::warning( sprintf( 'Webp convertation: Unable to get attachment #%s meta such as height, abs. path, URL, etc. Skipping WebP processing...', $attachment->ID ) );
198
+
199
+ return false;
200
+ }
201
+
202
+ /**
203
+ * @var $data array
204
+ */
205
+ foreach ( $attachment_meta as $hash => $data ) {
206
+
207
+ \WRIO_Logger::info( sprintf( 'Webp convertation: Ready to save hash "%s" (extra data: %s) as it does not exist yet', $hash, json_encode( $data ) ) );
208
+
209
+ $source_path = isset( $data['absolute_path'] ) ? $data['absolute_path'] : null;
210
+
211
+ if ( empty( $source_path ) || ! file_exists( $source_path ) ) {
212
+ \WRIO_Logger::error( sprintf( "Webp convertation: Image is not found.\r\nSource path: %s", $source_path ) );
213
+ continue;
214
+ }
215
+
216
+ $source_src = $data['url'];
217
+
218
+ $webp_exists = \RIO_Process_Queue::find_by_hash( hash( 'sha256', $source_src ) );
219
+
220
+ if ( $webp_exists ) {
221
+ \WRIO_Logger::warning( sprintf( "Webp convertation: Skipped because the webp image already exists.\r\nSource scr: %s", $source_src ) );
222
+
223
+ return false;
224
+ }
225
+
226
+ $extra_data = new \RIOP_WebP_Extra_Data( [
227
+ 'convert_from' => 'attachment',
228
+ 'converted_from_size' => $data['size'],
229
+ 'source_src' => $source_src,
230
+ 'source_path' => $source_path,
231
+ ] );
232
+
233
+ $saved = $this->save( [
234
+ 'item_hash' => $source_src,
235
+ 'object_id' => $attachment->ID,
236
+ 'original_mime_type' => $attachment->post_mime_type,
237
+ ], $extra_data );
238
+
239
+ if ( $saved instanceof \RIO_Process_Queue ) {
240
+ $this->_saved_models[] = $saved;
241
+ }
242
+ }
243
+
244
+ \WRIO_Logger::info( sprintf( 'End webp convertation proccess for attachment #%s. Saved models: %d', $model->get_id(), sizeof( $this->_saved_models ) ) );
245
+
246
+ return true;
247
+ }
248
+
249
+ /**
250
+ * Process custom folder.
251
+ *
252
+ * @param \RIO_Process_Queue $model Model to process.
253
+ *
254
+ * @return bool
255
+ */
256
+ public function process_custom_folder( $model ) {
257
+
258
+ \WRIO_Logger::info( sprintf( 'Start webp convertation proccess for Custom folder item #%s', $model->get_id() ) );
259
+
260
+ /**
261
+ * @var $model_extra_data \WRIO_CF_Image_Extra_Data
262
+ */
263
+ $model_extra_data = $model->get_extra_data();
264
+
265
+ $webp_exists = \RIO_Process_Queue::find_by_hash( hash( 'sha256', $model_extra_data->get_image_url() ) );
266
+
267
+ if ( $webp_exists ) {
268
+ \WRIO_Logger::warning( sprintf( "Webp convertation: Skipped because the webp image already exists.\r\nSource scr: %s", $model_extra_data->get_image_url() ) );
269
+
270
+ return false;
271
+ }
272
+
273
+ $extra_data = new \RIOP_WebP_Extra_Data( [
274
+ 'convert_from' => 'cf_image',
275
+ 'source_src' => $model_extra_data->get_image_url(),
276
+ 'source_path' => $model_extra_data->get_image_absolute_path(),
277
+ ] );
278
+
279
+ $saved = $this->save( [
280
+ 'item_hash' => $model_extra_data->get_image_url(),
281
+ 'item_hash_alternative' => $model_extra_data->get_image_relative_path(),
282
+ 'original_mime_type' => $model->get_original_mime_type(),
283
+ ], $extra_data );
284
+
285
+ if ( $saved instanceof \RIO_Process_Queue ) {
286
+ $this->_saved_models[] = $saved;
287
+ }
288
+
289
+ \WRIO_Logger::info( sprintf( 'End webp convertation proccess for Custom folder #%s. Saved models: %d', $model->get_id(), sizeof( $this->_saved_models ) ) );
290
+
291
+ return true;
292
+ }
293
+
294
+ /**
295
+ * Process NextGen plugin images.
296
+ *
297
+ * @link https://wordpress.org/plugins/nextgen-gallery/ Plugin link.
298
+ *
299
+ * @param \RIO_Process_Queue $model Model to process.
300
+ *
301
+ * @return bool
302
+ */
303
+ public function process_nextgen( $model ) {
304
+
305
+ \WRIO_Logger::info( sprintf( 'Start webp convertation proccess for NextGen item #%s', $model->get_id() ) );
306
+
307
+ /**
308
+ * @var $model_extra_data \WRIO_Nextgen_Extra_Data
309
+ */
310
+ $model_extra_data = $model->get_extra_data();
311
+
312
+ if ( ! ( $model_extra_data instanceof \WRIO_Nextgen_Extra_Data ) ) {
313
+ return false;
314
+ }
315
+
316
+ $webp_exists = \RIO_Process_Queue::find_by_hash( hash( 'sha256', $model_extra_data->get_image_url() ) );
317
+
318
+ if ( $webp_exists ) {
319
+ \WRIO_Logger::warning( sprintf( "Webp convertation: Skipped because the webp image already exists.\r\nSource scr: %s", $model_extra_data->get_image_url() ) );
320
+
321
+ return false;
322
+ }
323
+
324
+ // Original
325
+ $extra_data = new \RIOP_WebP_Extra_Data( [
326
+ 'convert_from' => 'nextgen',
327
+ 'converted_from_size' => null,
328
+ 'source_src' => $model_extra_data->get_image_url(),
329
+ 'source_path' => $model_extra_data->get_image_absolute_path(),
330
+ ] );
331
+
332
+ $original_saved = $this->save( [
333
+ 'item_hash' => $model_extra_data->get_image_url(),
334
+ 'original_mime_type' => $model->get_original_mime_type(),
335
+ 'object_id' => $model->get_object_id(),
336
+ ], $extra_data );
337
+
338
+ if ( $original_saved instanceof \RIO_Process_Queue ) {
339
+ $this->_saved_models[] = $original_saved;
340
+ }
341
+
342
+ // Thumbnail
343
+ $extra_data_thumbnail = new \RIOP_WebP_Extra_Data( [
344
+ 'convert_from' => 'nextgen',
345
+ 'converted_from_size' => null,
346
+ 'source_src' => $model_extra_data->get_image_thumbnail_url(),
347
+ 'source_path' => $model_extra_data->get_image_thumbnail_absolute_path(),
348
+ ] );
349
+
350
+ $thumbmail_saved = $this->save( [
351
+ 'item_hash' => $model_extra_data->get_image_thumbnail_url(),
352
+ 'original_mime_type' => $model->get_original_mime_type(),
353
+ ], $extra_data_thumbnail );
354
+
355
+ if ( $thumbmail_saved instanceof \RIO_Process_Queue ) {
356
+ $this->_saved_models[] = $thumbmail_saved;
357
+ }
358
+
359
+ \WRIO_Logger::info( sprintf( 'End webp convertation proccess for NextGen #%s. Saved models: %d', $model->get_id(), sizeof( $this->_saved_models ) ) );
360
+
361
+ return true;
362
+ }
363
+
364
+ /**
365
+ * Get attachment data such as height, width, absolute path and URL.
366
+ *
367
+ * @param WP_Post|int $attachment Attachment to get data for.
368
+ *
369
+ * @return array {
370
+ * Associative array of attachment data and its thumbnails, where key is sha256 hash or attachment URL.
371
+ *
372
+ * @type string $size Size of an image, e.g. medium.
373
+ * @type int $height Height in pixels.
374
+ * @type int $width Width in pixels.
375
+ * @type string $mime MIME type, e.g. image/jpeg.
376
+ * @type string $absolute_path Absolute path to thumbnail.
377
+ * @type string $url URL path to thumbnail.
378
+ * }
379
+ */
380
+ public static function get_attachment_data( $attachment ) {
381
+ $attachment = get_post( $attachment );
382
+ $hashmap = [];
383
+
384
+ if ( empty( $attachment ) ) {
385
+ return $hashmap;
386
+ }
387
+
388
+ $dirs = wp_upload_dir();
389
+
390
+ if ( isset( $dirs['error'] ) && $dirs['error'] !== false ) {
391
+ return $hashmap;
392
+ }
393
+
394
+ $attachment_meta = wp_get_attachment_metadata( $attachment->ID );
395
+
396
+ // Fallback to get attachment meta it can be empty when WordPress failed to create it or invocation
397
+ // of method was produced too soon
398
+ if ( empty( $attachment_meta ) ) {
399
+ $exploded_url = explode( 'wp-content/uploads/', $attachment->guid, 2 );
400
+ if ( isset( $exploded_url[1] ) ) {
401
+ $exploded_relative_path = trim( $exploded_url[1] );
402
+ $path_from_url = trailingslashit( $dirs['basedir'] ) . $exploded_relative_path;
403
+
404
+ // Need to remove this filter, as it would start recursion
405
+ remove_filter( 'wp_generate_attachment_metadata', 'wp_generate_attachment_metadata' );
406
+ remove_filter( 'wp_generate_attachment_metadata', [
407
+ \WRIO_Media_Library::get_instance(),
408
+ 'optimizeAfterUpload',
409
+ ] );
410
+
411
+ $attachment_meta = wp_generate_attachment_metadata( $attachment->ID, $path_from_url );
412
+
413
+ add_filter( 'wp_generate_attachment_metadata', [
414
+ \WRIO_Media_Library::get_instance(),
415
+ 'optimizeAfterUpload',
416
+ ], 10, 2 );
417
+ }
418
+ }
419
+
420
+ if ( isset( $dirs['basedir'] ) && isset( $attachment_meta['file'] ) ) {
421
+
422
+ $original = [
423
+ 'size' => 'original',
424
+ 'height' => $attachment_meta['height'],
425
+ 'width' => $attachment_meta['width'],
426
+ 'absolute_path' => wp_normalize_path( trailingslashit( $dirs['basedir'] ) . $attachment_meta['file'] ),
427
+ 'url' => \WRIO_Url::normalize( trailingslashit( $dirs['baseurl'] ) . $attachment_meta['file'] ),
428
+ ];
429
+
430
+ $hashmap[ hash( 'sha256', $original['url'] ) ] = $original;
431
+
432
+ foreach ( $attachment_meta['sizes'] as $size => $size_data ) {
433
+ // [2019, 01, somename.jpg]
434
+ $exploded = explode( '/', $attachment_meta['file'] );
435
+
436
+ // [2019, 01]
437
+ array_pop( $exploded );
438
+
439
+ // [2019, 01, someothername.jpg]
440
+ $exploded[] = $size_data['file'];
441
+
442
+ $new_file = implode( '/', $exploded );
443
+
444
+ $url = \WRIO_Url::normalize( trailingslashit( $dirs['baseurl'] ) . $new_file );
445
+ $absolute_path = wp_normalize_path( trailingslashit( $dirs['basedir'] ) . $new_file );
446
+ $hashed_url = hash( 'sha256', $url );
447
+
448
+ $hashmap[ $hashed_url ] = [
449
+ 'size' => $size,
450
+ 'height' => $size_data['height'],
451
+ 'width' => $size_data['width'],
452
+ 'mime' => $size_data['mime-type'],
453
+ 'absolute_path' => $absolute_path,
454
+ 'url' => $url,
455
+ ];
456
+ }
457
+ }
458
+
459
+ return $hashmap;
460
+ }
461
+
462
+ /**
463
+ * Add new image to be converted to WebP.
464
+ *
465
+ * @param array $props List of properties to be set on the model.
466
+ * @param \RIOP_WebP_Extra_Data $extra_data List of extra data params
467
+ *
468
+ * @return false|\RIO_Process_Queue
469
+ */
470
+ public function save( $props, $extra_data ) {
471
+
472
+ $model = new \RIO_Process_Queue();
473
+
474
+ if ( isset( $props['item_hash'] ) ) {
475
+ $model->set_item_hash( $props['item_hash'] );
476
+ }
477
+
478
+ if ( isset( $props['item_hash_alternative'] ) ) {
479
+ $model->set_item_hash_alternative( $props['item_hash_alternative'] );
480
+ }
481
+
482
+ if ( isset( $props['object_id'] ) ) {
483
+ $model->object_id = $props['object_id'];
484
+ }
485
+
486
+ if ( isset( $props['original_mime_type'] ) ) {
487
+ $model->original_mime_type = $props['original_mime_type'];
488
+ }
489
+
490
+ $model->item_type = self::DEFAULT_TYPE;
491
+ $model->result_status = \RIO_Process_Queue::STATUS_PROCESSING;
492
+ $model->processing_level = \WRIO_Plugin::app()->getPopulateOption( 'image_optimization_level', \RIO_Process_Queue::LEVEL_NORMAL );
493
+ $model->is_backed_up = false;
494
+ $model->original_size = @filesize( $extra_data->get_source_path() );
495
+ $model->final_size = 0; // to be known
496
+ $model->final_mime_type = 'image/webp';
497
+ $model->extra_data = $extra_data;
498
+
499
+ $is_saved = $model->save();
500
+
501
+ if ( $is_saved ) {
502
+ \WRIO_Logger::info( sprintf( 'Saved item with src "%s" and hash "%s" successfully', $extra_data->get_source_src(), $model->get_item_hash() ) );
503
+
504
+ return $model;
505
+ }
506
+
507
+ \WRIO_Logger::info( sprintf( 'Failed to save item with src "%s" and hash "%s" as save() method failed, check SQL for errors', $extra_data->get_source_src(), $model->get_item_hash() ) );
508
+
509
+ return false;
510
+ }
511
+ }
libs/addons/includes/classes/webp/class-webp-server.php ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WRIO\WEBP;
4
+
5
+ /**
6
+ * @author Webcraftic <wordpress.webraftic@gmail.com>, Alexander Kovalev <alex.kovalevv@gmail.com>
7
+ * @copyright (c) 20.04.2019, Webcraftic
8
+ * @version 1.0
9
+ */
10
+ class Server {
11
+
12
+ /**
13
+ * return the server home path
14
+ *
15
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
16
+ * @since 1.0.4
17
+ */
18
+ public static function get_home_path() {
19
+ $home = set_url_scheme( get_option( 'home' ), 'http' );
20
+ $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' );
21
+
22
+ if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
23
+ $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */
24
+ $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
25
+
26
+ if ( $pos !== false ) {
27
+ $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos );
28
+ $home_path = trim( $home_path, '/\\' ) . DIRECTORY_SEPARATOR;;
29
+ } else {
30
+ $wp_path_rel_to_home = DIRECTORY_SEPARATOR . trim( $wp_path_rel_to_home, '/\\' ) . DIRECTORY_SEPARATOR;
31
+
32
+ $real_apth = realpath( ABSPATH ) . DIRECTORY_SEPARATOR;
33
+
34
+ $pos = strpos( $real_apth, $wp_path_rel_to_home );
35
+ $home_path = substr( $real_apth, 0, $pos );
36
+ $home_path = trim( $home_path, '/\\' ) . DIRECTORY_SEPARATOR;
37
+ }
38
+ } else {
39
+ $home_path = ABSPATH;
40
+ }
41
+
42
+ $home_path = trim( $home_path, '\\/ ' );
43
+
44
+ //not for windows
45
+ if ( DIRECTORY_SEPARATOR != '\\' ) {
46
+ $home_path = DIRECTORY_SEPARATOR . $home_path;
47
+ }
48
+
49
+ return $home_path;
50
+ }
51
+
52
+ /**
53
+ * Return if the server run Apache
54
+ *
55
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
56
+ * @since 1.0.4
57
+ * @return bool
58
+ */
59
+ public static function is_apache() {
60
+ $is_apache = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false );
61
+
62
+ return $is_apache;
63
+ }
64
+
65
+ /**
66
+ * Return if the server run on nginx
67
+ *
68
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
69
+ * @since 1.0.4
70
+ * @return bool
71
+ */
72
+ public static function is_nginx() {
73
+ $is_nginx = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false );
74
+
75
+ return $is_nginx;
76
+ }
77
+
78
+ /**
79
+ * Return if the server run on IIS
80
+ *
81
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
82
+ * @since 1.0.4
83
+ * @return bool
84
+ */
85
+ public static function is_iss() {
86
+ $is_IIS = ! static::is_apache() && ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer' ) !== false );
87
+
88
+ return $is_IIS;
89
+ }
90
+
91
+ /**
92
+ * Return if the server run on IIS version 7 and up
93
+ *
94
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
95
+ * @since 1.0.4
96
+ * @return bool
97
+ */
98
+ public static function is_iis7() {
99
+ $is_iis7 = static::is_iss() && intval( substr( $_SERVER['SERVER_SOFTWARE'], strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/' ) + 14 ) ) >= 7;
100
+
101
+ return $is_iis7;
102
+ }
103
+
104
+ /**
105
+ * Is permalink enabled?
106
+ *
107
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
108
+ * @since 1.0.4
109
+ * @return bool
110
+ * @global \WP_Rewrite $wp_rewrite
111
+ */
112
+ public static function is_permalink() {
113
+ global $wp_rewrite;
114
+
115
+ if ( ! isset( $wp_rewrite ) || ! is_object( $wp_rewrite ) || ! $wp_rewrite->using_permalinks() ) {
116
+ return false;
117
+ }
118
+
119
+ return true;
120
+ }
121
+
122
+ /**
123
+ * Return whatever the htaccess config file is writable
124
+ *
125
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
126
+ * @since 1.0.4
127
+ * @return bool
128
+ */
129
+ public static function is_writable_htaccess( $htaccess_file ) {
130
+ if ( ( ! file_exists( $htaccess_file ) && static::is_permalink() ) || is_writable( $htaccess_file ) ) {
131
+ return true;
132
+ }
133
+
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Return whatever the web.config config file is writable
139
+ *
140
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
141
+ * @since 1.0.4
142
+ */
143
+ public static function is_writable_webconfig_file() {
144
+ $home_path = static::get_home_path();
145
+ $web_config_file = $home_path . 'web.config';
146
+
147
+ if ( ( ! file_exists( $web_config_file ) && self::is_permalink() ) || win_is_writable( $web_config_file ) ) {
148
+ return true;
149
+ }
150
+
151
+ return false;
152
+ }
153
+
154
+ /**
155
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
156
+ * @since 1.0.4
157
+ * @return bool
158
+ */
159
+ public static function got_mod_rewrite() {
160
+ if ( self::apache_mod_loaded( 'mod_rewrite', true ) ) {
161
+ return true;
162
+ }
163
+
164
+ return false;
165
+ }
166
+
167
+ /**
168
+ * Does the specified module exist in the Apache config?
169
+ *
170
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
171
+ * @since 1.0.4
172
+ *
173
+ * @param string $mod The module, e.g. mod_rewrite.
174
+ * @param bool $default Optional. The default return value if the module is not found. Default false.
175
+ *
176
+ * @return bool Whether the specified module is loaded.
177
+ * @global bool $is_apache
178
+ *
179
+ */
180
+ public static function apache_mod_loaded( $mod, $default = false ) {
181
+ if ( ! static::is_apache() ) {
182
+ return false;
183
+ }
184
+
185
+ if ( function_exists( 'apache_get_modules' ) ) {
186
+ $mods = apache_get_modules();
187
+ if ( in_array( $mod, $mods ) ) {
188
+ return true;
189
+ }
190
+ } else if ( getenv( 'HTTP_MOD_REWRITE' ) !== false ) {
191
+ $mod_found = getenv( 'HTTP_MOD_REWRITE' ) == 'On' ? true : false;
192
+
193
+ return $mod_found;
194
+ } else if ( function_exists( 'phpinfo' ) && false === strpos( ini_get( 'disable_functions' ), 'phpinfo' ) ) {
195
+ ob_start();
196
+ phpinfo( 8 );
197
+ $phpinfo = ob_get_clean();
198
+ if ( false !== strpos( $phpinfo, $mod ) ) {
199
+ return true;
200
+ }
201
+ }
202
+
203
+ return $default;
204
+ }
205
+
206
+ /**
207
+ * Return whatever server using the .htaccess config file
208
+ *
209
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
210
+ * @since 1.0.4
211
+ * @return bool
212
+ */
213
+ public static function server_use_htaccess() {
214
+ $home_path = static::get_home_path();
215
+ $htaccess_file = $home_path . DIRECTORY_SEPARATOR . '.htaccess';
216
+
217
+ if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) && static::is_permalink() ) || is_writable( $htaccess_file ) ) {
218
+ if ( static::got_mod_rewrite() ) {
219
+ return true;
220
+ }
221
+ }
222
+
223
+ return false;
224
+ }
225
+
226
+ /**
227
+ * Cleans webp rules from htaccess file. Use when deactivating a plugin
228
+ * or turn off webp support option.
229
+ *
230
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
231
+ * @since 1.0.4
232
+ */
233
+ public static function htaccess_clear_webp_rules() {
234
+ $wp_upload_dir = wp_upload_dir();
235
+ $root_htaccess_file_path = static::get_home_path() . DIRECTORY_SEPARATOR . '.htaccess';
236
+ $wp_content_htaccess_file_path = trailingslashit( WP_CONTENT_DIR ) . '.htaccess';
237
+
238
+ static::insert_with_markers( $root_htaccess_file_path, '' );
239
+
240
+ if ( isset( $wp_upload_dir['error'] ) && $wp_upload_dir['error'] !== false ) {
241
+ \WRIO_Logger::error( 'It is not possible to update webp rules for htaccess, because upload dir is not writable.' );
242
+ } else {
243
+
244
+ $upload_base = $wp_upload_dir['basedir'];
245
+ $uploads_htaccess_file_path = trailingslashit( $upload_base ) . '.htaccess';
246
+
247
+ static::insert_with_markers( $uploads_htaccess_file_path, '' );
248
+ }
249
+
250
+ static::insert_with_markers( $wp_content_htaccess_file_path, '' );
251
+ }
252
+
253
+ /**
254
+ * Add webp rules in htaccess file. Use when activating a plugin
255
+ * or turn on webp support option.
256
+ *
257
+ * @author Alexander Kovalev <alex.kovalevv@gmail.com>
258
+ * @since @since 1.0.4
259
+ *
260
+ * @param bool $clear
261
+ */
262
+ public static function htaccess_update_webp_rules() {
263
+ // [BS] Backward compat. 11/03/2019 - remove possible settings from root .htaccess
264
+ $wp_upload_dir = wp_upload_dir();
265
+ $root_htaccess_file_path = static::get_home_path() . DIRECTORY_SEPARATOR . '.htaccess';
266
+ $wp_content_htaccess_file_path = trailingslashit( WP_CONTENT_DIR ) . '.htaccess';
267
+
268
+ $rules = '
269
+ <IfModule mod_rewrite.c>
270
+ RewriteEngine On
271
+
272
+ ##### TRY FIRST the file appended with .webp (ex. test.jpg.webp) #####
273
+ # Does browser explicitly support webp?
274
+ RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
275
+ # OR Is request from Page Speed
276
+ RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
277
+ # OR does this browser explicitly support webp
278
+ RewriteCond %{HTTP_ACCEPT} image/webp
279
+ # AND is the request a jpg or png?
280
+ RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
281
+ # AND does a .ext.webp image exist?
282
+ RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.webp -f
283
+ # THEN send the webp image and set the env var webp
284
+ RewriteRule ^(.+)$ $1.webp [NC,T=image/webp,E=webp,L]
285
+
286
+ ##### IF NOT, try the file with replaced extension (test.webp) #####
287
+ RewriteCond %{HTTP_USER_AGENT} Chrome [OR]
288
+ RewriteCond %{HTTP_USER_AGENT} "Google Page Speed Insights" [OR]
289
+ RewriteCond %{HTTP_ACCEPT} image/webp
290
+ # AND is the request a jpg or png? (also grab the basepath %1 to match in the next rule)
291
+ RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$
292
+ # AND does a .ext.webp image exist?
293
+ RewriteCond %{DOCUMENT_ROOT}/%1.webp -f
294
+ # THEN send the webp image and set the env var webp
295
+ RewriteRule (.+)\.(?:jpe?g|png)$ $1.webp [NC,T=image/webp,E=webp,L]
296
+
297
+ </IfModule>
298
+ <IfModule mod_headers.c>
299
+ # If REDIRECT_webp env var exists, append Accept to the Vary header
300
+ Header append Vary Accept env=REDIRECT_webp
301
+ </IfModule>
302
+
303
+ <IfModule mod_mime.c>
304
+ AddType image/webp .webp
305
+ </IfModule>
306
+ ';
307
+
308
+ static::insert_with_markers( $root_htaccess_file_path, $rules );
309
+
310
+ if ( isset( $wp_upload_dir['error'] ) && $wp_upload_dir['error'] !== false ) {
311
+ \WRIO_Logger::error( 'It is not possible to update webp rules for htaccess, because upload dir is not writable.' );
312
+ } else {
313
+
314
+ $upload_base = $wp_upload_dir['basedir'];
315
+ $uploads_htaccess_file_path = trailingslashit( $upload_base ) . '.htaccess';
316
+
317
+ static::insert_with_markers( $uploads_htaccess_file_path, $rules );
318
+ }
319
+
320
+ static::insert_with_markers( $wp_content_htaccess_file_path, $rules );
321
+ }
322
+
323
+ public static function insert_with_markers( $file_path, $content ) {
324
+ if ( ! static::is_writable_htaccess( $file_path ) ) {
325
+ \WRIO_Logger::error( sprintf( "It is not possible to update webp rules for htaccess, because file (%s) is not writable.", $file_path ) );
326
+ } else {
327
+ if ( ! static::got_mod_rewrite() ) {
328
+ \WRIO_Logger::error( "It isn't possible to update webp rules for htaccess, because mode rewrite is unsupported." );
329
+
330
+ return;
331
+ }
332
+
333
+ if ( ! insert_with_markers( $file_path, 'Robin Image Optimizer Webp', $content ) ) {
334
+ \WRIO_Logger::error( 'Failed write webp rules to htaccess file (%s)' );
335
+ }
336
+ }
337
+ }
338
+ }
libs/addons/includes/classes/webp/composer.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ {
2
+ "require": {
3
+ "rosell-dk/dom-util-for-webp": "^0.3.0"
4
+ }
5
+ }
libs/addons/includes/classes/webp/composer.lock ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "content-hash": "8d03ee8cf464879ef4a5fd1a6fba0c95",
8
+ "packages": [
9
+ {
10
+ "name": "rosell-dk/dom-util-for-webp",
11
+ "version": "0.3.0",
12
+ "source": {
13
+ "type": "git",
14
+ "url": "https://github.com/rosell-dk/dom-util-for-webp.git",
15
+ "reference": "c9cebf304553695fb6f7cf5634da51172e8e4aba"
16
+ },
17
+ "dist": {
18
+ "type": "zip",
19
+ "url": "https://api.github.com/repos/rosell-dk/dom-util-for-webp/zipball/c9cebf304553695fb6f7cf5634da51172e8e4aba",
20
+ "reference": "c9cebf304553695fb6f7cf5634da51172e8e4aba",
21
+ "shasum": ""
22
+ },
23
+ "require-dev": {
24
+ "friendsofphp/php-cs-fixer": "^2.11",
25
+ "phpunit/phpunit": "5.7.27",
26
+ "squizlabs/php_codesniffer": "3.*"
27
+ },
28
+ "type": "library",
29
+ "extra": {
30
+ "scripts-descriptions": {
31
+ "ci": "Run tests before CI",
32
+ "phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
33
+ "phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
34
+ "cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
35
+ "cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
36
+ "test": "Launches the preconfigured PHPUnit"
37
+ }
38
+ },
39
+ "autoload": {
40
+ "psr-4": {
41
+ "DOMUtilForWebP\\": "src/"
42
+ }
43
+ },
44
+ "notification-url": "https://packagist.org/downloads/",
45
+ "license": [
46
+ "MIT"
47
+ ],
48
+ "authors": [
49
+ {
50
+ "name": "Bjørn Rosell",
51
+ "homepage": "https://www.bitwise-it.dk/contact",
52
+ "role": "Project Author"
53
+ }
54
+ ],
55
+ "description": "Replace image URLs found in HTML",
56
+ "keywords": [
57
+ "Webp",
58
+ "html",
59
+ "images",
60
+ "replace"
61
+ ],
62
+ "time": "2019-03-07T09:15:07+00:00"
63
+ }
64
+ ],
65
+ "packages-dev": [],
66
+ "aliases": [],
67
+ "minimum-stability": "stable",
68
+ "stability-flags": [],
69
+ "prefer-stable": false,
70
+ "prefer-lowest": false,
71
+ "platform": [],
72
+ "platform-dev": []
73
+ }
libs/addons/includes/classes/webp/vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInitf915fc14971134da4d8f26cdeb45e0c1::getLoader();
libs/addons/includes/classes/webp/vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
libs/addons/includes/classes/webp/vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
libs/addons/includes/classes/webp/vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
libs/addons/includes/classes/webp/vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
libs/addons/includes/classes/webp/vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'DOMUtilForWebP\\' => array($vendorDir . '/rosell-dk/dom-util-for-webp/src'),
10
+ );
libs/addons/includes/classes/webp/vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInitf915fc14971134da4d8f26cdeb45e0c1
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ public static function getLoader()
17
+ {
18
+ if (null !== self::$loader) {
19
+ return self::$loader;
20
+ }
21
+
22
+ spl_autoload_register(array('ComposerAutoloaderInitf915fc14971134da4d8f26cdeb45e0c1', 'loadClassLoader'), true, true);
23
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitf915fc14971134da4d8f26cdeb45e0c1', 'loadClassLoader'));
25
+
26
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
+ if ($useStaticLoader) {
28
+ require_once __DIR__ . '/autoload_static.php';
29
+
30
+ call_user_func(\Composer\Autoload\ComposerStaticInitf915fc14971134da4d8f26cdeb45e0c1::getInitializer($loader));
31
+ } else {
32
+ $map = require __DIR__ . '/autoload_namespaces.php';
33
+ foreach ($map as $namespace => $path) {
34
+ $loader->set($namespace, $path);
35
+ }
36
+
37
+ $map = require __DIR__ . '/autoload_psr4.php';
38
+ foreach ($map as $namespace => $path) {
39
+ $loader->setPsr4($namespace, $path);
40
+ }
41
+
42
+ $classMap = require __DIR__ . '/autoload_classmap.php';
43
+ if ($classMap) {
44
+ $loader->addClassMap($classMap);
45
+ }
46
+ }
47
+
48
+ $loader->register(true);
49
+
50
+ return $loader;
51
+ }
52
+ }
libs/addons/includes/classes/webp/vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInitf915fc14971134da4d8f26cdeb45e0c1
8
+ {
9
+ public static $prefixLengthsPsr4 = array (
10
+ 'D' =>
11
+ array (
12
+ 'DOMUtilForWebP\\' => 15,
13
+ ),
14
+ );
15
+
16
+ public static $prefixDirsPsr4 = array (
17
+ 'DOMUtilForWebP\\' =>
18
+ array (
19
+ 0 => __DIR__ . '/..' . '/rosell-dk/dom-util-for-webp/src',
20
+ ),
21
+ );
22
+
23
+ public static function getInitializer(ClassLoader $loader)
24
+ {
25
+ return \Closure::bind(function () use ($loader) {
26
+ $loader->prefixLengthsPsr4 = ComposerStaticInitf915fc14971134da4d8f26cdeb45e0c1::$prefixLengthsPsr4;
27
+ $loader->prefixDirsPsr4 = ComposerStaticInitf915fc14971134da4d8f26cdeb45e0c1::$prefixDirsPsr4;
28
+
29
+ }, null, ClassLoader::class);
30
+ }
31
+ }
libs/addons/includes/classes/webp/vendor/composer/installed.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "rosell-dk/dom-util-for-webp",
4
+ "version": "0.3.0",
5
+ "version_normalized": "0.3.0.0",
6
+ "source": {
7
+ "type": "git",
8
+ "url": "https://github.com/rosell-dk/dom-util-for-webp.git",
9
+ "reference": "c9cebf304553695fb6f7cf5634da51172e8e4aba"
10
+ },
11
+ "dist": {
12
+ "type": "zip",
13
+ "url": "https://api.github.com/repos/rosell-dk/dom-util-for-webp/zipball/c9cebf304553695fb6f7cf5634da51172e8e4aba",
14
+ "reference": "c9cebf304553695fb6f7cf5634da51172e8e4aba",
15
+ "shasum": ""
16
+ },
17
+ "require-dev": {
18
+ "friendsofphp/php-cs-fixer": "^2.11",
19
+ "phpunit/phpunit": "5.7.27",
20
+ "squizlabs/php_codesniffer": "3.*"
21
+ },
22
+ "time": "2019-03-07T09:15:07+00:00",
23
+ "type": "library",
24
+ "extra": {
25
+ "scripts-descriptions": {
26
+ "ci": "Run tests before CI",
27
+ "phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
28
+ "phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
29
+ "cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
30
+ "cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
31
+ "test": "Launches the preconfigured PHPUnit"
32
+ }
33
+ },
34
+ "installation-source": "dist",
35
+ "autoload": {
36
+ "psr-4": {
37
+ "DOMUtilForWebP\\": "src/"
38
+ }
39
+ },
40
+ "notification-url": "https://packagist.org/downloads/",
41
+ "license": [
42
+ "MIT"
43
+ ],
44
+ "authors": [
45
+ {
46
+ "name": "Bjørn Rosell",
47
+ "homepage": "https://www.bitwise-it.dk/contact",
48
+ "role": "Project Author"
49
+ }
50
+ ],
51
+ "description": "Replace image URLs found in HTML",
52
+ "keywords": [
53
+ "Webp",
54
+ "html",
55
+ "images",
56
+ "replace"
57
+ ]
58
+ }
59
+ ]
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/.php_cs.dist ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $finder = PhpCsFixer\Finder::create()
4
+ ->exclude('tests')
5
+ ->in(__DIR__)
6
+ ;
7
+
8
+ $config = PhpCsFixer\Config::create();
9
+ $config
10
+ ->setRules([
11
+ '@PSR2' => true,
12
+ 'array_syntax' => [
13
+ 'syntax' => 'short',
14
+ ],
15
+ ])
16
+ ->setFinder($finder)
17
+ ;
18
+
19
+ return $config;
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/README.md ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # dom-util-for-webp
2
+
3
+ [![Build Status](https://travis-ci.org/rosell-dk/dom-util-for-webp.png?branch=master)](https://travis-ci.org/rosell-dk/dom-util-for-webp)
4
+
5
+ *Replace image URLs found in HTML*
6
+
7
+ This library can do two things:
8
+
9
+ 1) Replace image URLs in HTML
10
+ 2) Replace *&lt;img&gt;* tags with *&lt;picture&gt;* tags, adding webp versions to sources
11
+
12
+ To setup with composer, run ```composer require rosell-dk/dom-util-for-webp```.
13
+
14
+ ## 1. Replacing image URLs in HTML
15
+
16
+ The *ImageUrlReplacer::replace($html)* method accepts a piece of HTML and returns HTML where where all image URLs have been replaced - even those in inline styles.
17
+
18
+ *Usage:*
19
+
20
+ ```php
21
+ $modifiedHtml = ImageUrlReplacer::replace($html);
22
+ ```
23
+
24
+ ### Example replacements:
25
+
26
+ *input:*
27
+
28
+ ```html
29
+ <img src="image.jpg">
30
+ <img src="1.jpg" srcset="2.jpg 1000w">
31
+ <picture>
32
+ <source srcset="1.jpg" type="image/webp">
33
+ <source srcset="2.png" type="image/webp">
34
+ <source src="3.gif"> <!-- gifs are skipped in default behaviour -->
35
+ <source src="4.jpg?width=200"> <!-- urls with query string are skipped in default behaviour -->
36
+ </picture>
37
+ <div style="background-image: url('image.jpeg')"></div>
38
+ <style>
39
+ #hero {
40
+ background: lightblue url("image.png") no-repeat fixed center;;
41
+ }
42
+ </style>
43
+ <input type="button" src="1.jpg">
44
+ <img data-src="image.jpg"> <!-- any attribute starting with "data-" are replaced (if it ends with "jpg", "jpeg" or "png"). For lazy-loading -->
45
+ ```
46
+
47
+ *output:*
48
+
49
+ ```html
50
+ <img src="image.jpg.webp">
51
+ <img src="1.jpg.webp" srcset="2.jpg.webp 1000w">
52
+ <picture>
53
+ <source srcset="1.jpg.webp" type="image/webp">
54
+ <source srcset="2.jpg.webp" type="image/webp">
55
+ <source srcset="3.gif"> <!-- gifs are skipped in default behaviour -->
56
+ <source srcset="4.jpg?width=200"> <!-- urls with query string are skipped in default behaviour -->
57
+ </picture>
58
+ <div style="background-image: url('image.jpeg.webp')"></div>
59
+ <style>
60
+ #hero {
61
+ background: lightblue url("image.png.webp") no-repeat fixed center;;
62
+ }
63
+ </style>
64
+ <input type="button" src="1.jpg.webp">
65
+ <img data-src="image.jpg.webp"> <!-- any attribute starting with "data-" are replaced (if it ends with "jpg", "jpeg" or "png"). For lazy-loading -->
66
+ ```
67
+
68
+ Default behaviour of *ImageUrlReplacer::replace*:
69
+ - The modified URL is the same as the original, with ".webp" appended (to change, override the `replaceUrl` function)
70
+ - Only replaces URLs that ends with "png", "jpg" or "jpeg" (no query strings either) (to change, override the `replaceUrl` function)
71
+ - Attribute search/replace limits to these tags: *&lt;img&gt;*, *&lt;source&gt;*, *&lt;input&gt;* and *&lt;iframe&gt;* (to change, override the `$searchInTags` property)
72
+ - Attribute search/replace limits to these attributes: "src", "src-set" and any attribute starting with "data-" (to change, override the `attributeFilter` function)
73
+ - Urls inside styles are replaced too (*background-image* and *background* properties)
74
+
75
+ The behaviour can be modified by extending *ImageUrlReplacer* and overriding public methods such as *replaceUrl*
76
+
77
+ ImageUrlReplacer uses the `Sunra\PhpSimple\HtmlDomParser`[library](https://github.com/sunra/php-simple-html-dom-parser) for parsing and modifying HTML. It wraps [simplehtmldom](http://simplehtmldom.sourceforge.net/). Simplehtmldom supports invalid HTML (it does not touch the invalid parts)
78
+
79
+
80
+ ### Example: Customized behaviour
81
+
82
+ ```php
83
+ class ImageUrlReplacerCustomReplacer extends ImageUrlReplacer
84
+ {
85
+ public function replaceUrl($url) {
86
+ // Only accept urls ending with "png", "jpg", "jpeg" and "gif"
87
+ if (!preg_match('#(png|jpe?g|gif)$#', $url)) {
88
+ return;
89
+ }
90
+
91
+ // Only accept full urls (beginning with http:// or https://)
92
+ if (!preg_match('#^https?://#', $url)) {
93
+ return;
94
+ }
95
+
96
+ // PS: You probably want to filter out external images too...
97
+
98
+ // Simply append ".webp" after current extension.
99
+ // This strategy ensures that "logo.jpg" and "logo.gif" gets counterparts with unique names
100
+ return $url . '.webp';
101
+ }
102
+
103
+ public function attributeFilter($attrName) {
104
+ // Don't allow any "data-" attribute, but limit to attributes that smells like they are used for images
105
+ // The following rule matches all attributes used for lazy loading images that we know of
106
+ return preg_match('#^(src|srcset|(data-[^=]*(lazy|small|slide|img|large|src|thumb|source|set|bg-url)[^=]*))$#i', $attrName);
107
+
108
+ // If you want to limit it further, only allowing attributes known to be used for lazy load,
109
+ // use the following regex instead:
110
+ //return preg_match('#^(src|srcset|data-(src|srcset|cvpsrc|cvpset|thumb|bg-url|large_image|lazyload|source-url|srcsmall|srclarge|srcfull|slide-img|lazy-original))$#i', $attrName);
111
+ }
112
+ }
113
+
114
+ $modifiedHtml = ImageUrlReplacerCustomReplacer::replace($html);
115
+ ```
116
+
117
+
118
+ ## 2. Replacing *&lt;img&gt;* tags with *&lt;picture&gt;* tags
119
+
120
+ The *PictureTags::replace($html)* method accepts a piece of HTML and returns HTML where where all &lt;img&gt; tags have been replaced with &lt;picture&gt; tags, adding webp versions to sources
121
+
122
+ Usage:
123
+
124
+ ```php
125
+ $modifiedHtml = PictureTags::replace($html);
126
+ ```
127
+
128
+ #### Example replacements:
129
+
130
+ *Input:*
131
+ ```html
132
+ <img src="1.png">
133
+ <img srcset="3.jpg 1000w" src="3.jpg">
134
+ <img data-lazy-src="9.jpg" style="border:2px solid red" class="something">
135
+ <figure class="wp-block-image">
136
+ <img src="12.jpg" alt="" class="wp-image-6" srcset="12.jpg 492w, 12-300x265.jpg 300w" sizes="(max-width: 492px) 100vw, 492px">
137
+ </figure>
138
+ ```
139
+
140
+ *Output*:
141
+ ```html
142
+ <picture><source srcset="1.png.webp" type="image/webp"><img src="1.png" class="webpexpress-processed"></picture>
143
+ <picture><source srcset="3.jpg.webp 1000w" type="image/webp"><img srcset="3.jpg 1000w" src="3.jpg" class="webpexpress-processed"></picture>
144
+ <picture><source data-lazy-src="9.jpg.webp" type="image/webp"><img data-lazy-src="9.jpg" style="border:2px solid red" class="something webpexpress-processed"></picture>
145
+ <figure class="wp-block-image">
146
+ <picture><source srcset="12.jpg.webp 492w, 12-300x265.jpg.webp 300w" sizes="(max-width: 492px) 100vw, 492px" type="image/webp"><img src="12.jpg" alt="" class="wp-image-6 webpexpress-processed" srcset="12.jpg 492w, 12-300x265.jpg 300w" sizes="(max-width: 492px) 100vw, 492px"></picture>
147
+ </figure>'
148
+ ```
149
+
150
+ Note that with the picture tags, it is still the img tag that shows the selected image. The picture tag is just a wrapper.
151
+ So it is correct behaviour not to copy the *style*, *width*, *class* or any other attributes to the picture tag. See [issue #9](https://github.com/rosell-dk/dom-util-for-webp/issues/9).
152
+
153
+
154
+ As with `ImageUrlReplacer`, you can override the *replaceUrl* function. There is however currently no other methods to override.
155
+
156
+ `PictureTags` currently uses regular expressions to do the replacing. There are plans to change implementation to use `Sunra\PhpSimple\HtmlDomParser`, like our `ImageUrlReplacer` class does.
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/composer.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "rosell-dk/dom-util-for-webp",
3
+ "description": "Replace image URLs found in HTML",
4
+ "type": "library",
5
+ "license": "MIT",
6
+ "minimum-stability": "stable",
7
+ "keywords": ["webp", "replace", "images", "html"],
8
+ "scripts": {
9
+ "ci": [
10
+ "@build",
11
+ "@test",
12
+ "@phpcs-all",
13
+ "@composer validate --no-check-all --strict"
14
+ ],
15
+ "cs-fix-all": [
16
+ "php-cs-fixer fix src"
17
+ ],
18
+ "cs-fix": "php-cs-fixer fix",
19
+ "cs-dry": "php-cs-fixer fix --dry-run --diff",
20
+ "test": "phpunit",
21
+ "phpcs": "phpcs --standard=PSR2",
22
+ "phpcs-all": "phpcs --standard=PSR2 --ignore=src/simple_html_dom/simple_html_dom.inc src",
23
+ "phpcbf": "phpcbf --standard=PSR2"
24
+ },
25
+ "extra": {
26
+ "scripts-descriptions": {
27
+ "ci": "Run tests before CI",
28
+ "phpcs": "Checks coding styles (PSR2) of file/dir, which you must supply. To check all, supply 'src'",
29
+ "phpcbf": "Fix coding styles (PSR2) of file/dir, which you must supply. To fix all, supply 'src'",
30
+ "cs-fix-all": "Fix the coding style of all the source files, to comply with the PSR-2 coding standard",
31
+ "cs-fix": "Fix the coding style of a PHP file or directory, which you must specify.",
32
+ "test": "Launches the preconfigured PHPUnit"
33
+ }
34
+ },
35
+ "autoload": {
36
+ "psr-4": { "DOMUtilForWebP\\": "src/" }
37
+ },
38
+ "autoload-dev": {
39
+ "psr-4": { "DOMUtilForWebPTests\\": "tests/" }
40
+ },
41
+ "authors": [
42
+ {
43
+ "name": "Bjørn Rosell",
44
+ "homepage": "https://www.bitwise-it.dk/contact",
45
+ "role": "Project Author"
46
+ }
47
+ ],
48
+ "require-dev": {
49
+ "friendsofphp/php-cs-fixer": "^2.11",
50
+ "phpunit/phpunit": "5.7.27",
51
+ "squizlabs/php_codesniffer": "3.*"
52
+ },
53
+ "config": {
54
+ "sort-packages": true
55
+ },
56
+ "require": {
57
+ }
58
+ }
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/phpunit.xml.dist ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
5
+ backupGlobals="false"
6
+ backupStaticAttributes="false"
7
+ colors="true"
8
+ convertErrorsToExceptions="true"
9
+ convertNoticesToExceptions="true"
10
+ convertWarningsToExceptions="false"
11
+ processIsolation="false"
12
+ stopOnFailure="false"
13
+ bootstrap="vendor/autoload.php"
14
+ >
15
+ <testsuites>
16
+ <testsuite name="Dom util for WebP Test Suite">
17
+ <directory>./tests/</directory>
18
+ </testsuite>
19
+ </testsuites>
20
+
21
+ <filter>
22
+ <whitelist>
23
+ <directory>./</directory>
24
+ <exclude>
25
+ <directory>./vendor</directory>
26
+ <directory>./tests</directory>
27
+ </exclude>
28
+ </whitelist>
29
+ </filter>
30
+ </phpunit>
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/ImageUrlReplacer.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DOMUtilForWebP;
4
+
5
+ //use Sunra\PhpSimple\HtmlDomParser;
6
+
7
+ /**
8
+ * Highly configurable class for replacing image URLs in HTML (both src and srcset syntax)
9
+ *
10
+ * Based on http://simplehtmldom.sourceforge.net/ - a library for easily manipulating HTML by means of a DOM.
11
+ * The great thing about this library is that it supports working on invalid HTML and it only applies the changes you
12
+ * make - very gently.
13
+ *
14
+ * TODO: Check out how ewww does it
15
+ *
16
+ * Behaviour can be customized by overriding the public methods (replaceUrl, $searchInTags, etc)
17
+ *
18
+ * Default behaviour:
19
+ * - The modified URL is the same as the original, with ".webp" appended (replaceUrl)
20
+ * - Limits to these tags: <img>, <source>, <input> and <iframe> ($searchInTags)
21
+ * - Limits to these attributes: "src", "src-set" and any attribute starting with "data-" (attributeFilter)
22
+ * - Only replaces URLs that ends with "png", "jpg" or "jpeg" (no query strings either) (replaceUrl)
23
+ *
24
+ *
25
+ */
26
+ class ImageUrlReplacer
27
+ {
28
+
29
+ // define tags to be searched. The div and li are on the list because these are often used with lazy loading
30
+ public static $searchInTags = ['img', 'source', 'input', 'iframe', 'div', 'li'];
31
+
32
+ /**
33
+ *
34
+ * @return webp url or, if URL should not be changed, return nothing
35
+ **/
36
+ public function replaceUrl($url)
37
+ {
38
+ if (!preg_match('#(png|jpe?g)$#', $url)) {
39
+ return;
40
+ }
41
+ return $url . '.webp';
42
+ }
43
+
44
+ public function replaceUrlOr($url, $returnValueIfDenied)
45
+ {
46
+ $url = $this->replaceUrl($url);
47
+ return (isset($url) ? $url : $returnValueIfDenied);
48
+ }
49
+
50
+ /*
51
+ public function isValidUrl($url)
52
+ {
53
+ return preg_match('#(png|jpe?g)$#', $url);
54
+ }*/
55
+
56
+ public function handleSrc($attrValue)
57
+ {
58
+ return $this->replaceUrlOr($attrValue, $attrValue);
59
+ }
60
+
61
+ public function handleSrcSet($attrValue)
62
+ {
63
+ // $attrValue is ie: <img data-x="1.jpg 1000w, 2.jpg">
64
+ $srcsetArr = explode(',', $attrValue);
65
+ foreach ($srcsetArr as $i => $srcSetEntry) {
66
+ // $srcSetEntry is ie "image.jpg 520w", but can also lack width, ie just "image.jpg"
67
+ // it can also be ie "image.jpg 2x"
68
+ $srcSetEntry = trim($srcSetEntry);
69
+ $entryParts = preg_split('/\s+/', $srcSetEntry, 2);
70
+ if (count($entryParts) == 2) {
71
+ list($src, $descriptors) = $entryParts;
72
+ } else {
73
+ $src = $srcSetEntry;
74
+ $descriptors = null;
75
+ }
76
+
77
+ $webpUrl = $this->replaceUrlOr($src, false);
78
+ if ($webpUrl !== false) {
79
+ $srcsetArr[$i] = $webpUrl . (isset($descriptors) ? ' ' . $descriptors : '');
80
+ }
81
+ }
82
+ return implode(', ', $srcsetArr);
83
+ }
84
+
85
+ /**
86
+ * Test if attribute value looks like it has srcset syntax.
87
+ * "image.jpg 100w" does for example. And "image.jpg 1x". Also "image1.jpg, image2.jpg 1x"
88
+ * Mixing x and w is invalid (according to
89
+ * https://stackoverflow.com/questions/26928828/html5-srcset-mixing-x-and-w-syntax)
90
+ * But we accept it anyway
91
+ * It is not the job of this function to see if the first part is an image URL
92
+ * That will be done in handleSrcSet.
93
+ *
94
+ */
95
+ public function looksLikeSrcSet($value)
96
+ {
97
+ if (preg_match('#\s\d*(w|x)#', $value)) {
98
+ return true;
99
+ }
100
+ return false;
101
+ }
102
+
103
+ public function handleAttribute($value)
104
+ {
105
+ if (self::looksLikeSrcSet($value)) {
106
+ return self::handleSrcSet($value);
107
+ }
108
+ return self::handleSrc($value);
109
+ }
110
+
111
+ public function attributeFilter($attrName)
112
+ {
113
+ $attrName = strtolower($attrName);
114
+ if (($attrName == 'src') || ($attrName == 'srcset') || (strpos($attrName, 'data-') === 0)) {
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+
120
+ public function processCSSRegExCallback($matches)
121
+ {
122
+ list($all, $pre, $quote, $url, $post) = $matches;
123
+ return $pre . $this->replaceUrlOr($url, $url) . $post;
124
+ }
125
+
126
+ public function processCSS($css)
127
+ {
128
+ $declarations = explode(';', $css);
129
+ foreach ($declarations as $i => &$declaration) {
130
+ if (preg_match('#(background(-image)?)\\s*:#', $declaration)) {
131
+ // https://regexr.com/46qdg
132
+ //$regex = '#(url\s*\(([\"\']?))([^\'\";\)]*)(\2\s*\))#';
133
+ $parts = explode(',', $declaration);
134
+ //print_r($parts);
135
+ foreach ($parts as &$part) {
136
+ //echo 'part:' . $part . "\n";
137
+ $regex = '#(url\\s*\\(([\\"\\\']?))([^\\\'\\";\\)]*)(\\2\\s*\\))#';
138
+ $part = preg_replace_callback($regex, 'self::processCSSRegExCallback', $part);
139
+ //echo 'result:' . $part . "\n";
140
+ }
141
+ $declarations[$i] = implode($parts, ',');
142
+ }
143
+ }
144
+ return implode(';', $declarations);
145
+ }
146
+
147
+ public function replaceHtml($html)
148
+ {
149
+ if ($html == '') {
150
+ return '';
151
+ }
152
+
153
+ // https://stackoverflow.com/questions/4812691/preserve-line-breaks-simple-html-dom-parser
154
+
155
+ // function str_get_html($str, $lowercase=true, $forceTagsClosed=true, $target_charset = DEFAULT_TARGET_CHARSET,
156
+ // $stripRN=true, $defaultBRText=DEFAULT_BR_TEXT, $defaultSpanText=DEFAULT_SPAN_TEXT)
157
+
158
+ //$dom = HtmlDomParser::str_get_html($html, false, false, 'UTF-8', false);
159
+ $dom = str_get_html($html, false, false, 'UTF-8', false);
160
+ if ($dom === false) {
161
+ if (strlen($html) > MAX_FILE_SIZE) {
162
+ return '<!-- Alter HTML was skipped because the HTML is too big to process! ' .
163
+ '(limit is set to ' . MAX_FILE_SIZE . ' bytes) -->' . "\n" . $html;
164
+ }
165
+ return '<!-- Alter HTML was skipped because the helper library refused to process the html -->' .
166
+ "\n" . $html;
167
+ }
168
+
169
+ // Replace attributes (src, srcset, data-src, etc)
170
+ foreach (self::$searchInTags as $tagName) {
171
+ $elems = $dom->find($tagName);
172
+ foreach ($elems as $index => $elem) {
173
+ $attributes = $elem->getAllAttributes();
174
+ foreach ($elem->getAllAttributes() as $attrName => $attrValue) {
175
+ if ($this->attributeFilter($attrName)) {
176
+ $elem->setAttribute($attrName, $this->handleAttribute($attrValue));
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ // Replace <style> elements
183
+ $elems = $dom->find('style');
184
+ foreach ($elems as $index => $elem) {
185
+ $css = $this->processCSS($elem->innertext);
186
+ if ($css != $elem->innertext) {
187
+ $elem->innertext = $css;
188
+ }
189
+ }
190
+
191
+ // Replace "style attributes
192
+ $elems = $dom->find('*[style]');
193
+ foreach ($elems as $index => $elem) {
194
+ $css = $this->processCSS($elem->style);
195
+ if ($css != $elem->style) {
196
+ $elem->style = $css;
197
+ }
198
+ }
199
+
200
+ return $dom->save();
201
+ }
202
+
203
+ /* Main replacer function */
204
+ public static function replace($html)
205
+ {
206
+ if (!function_exists('str_get_html')) {
207
+ require_once 'simple_html_dom/simple_html_dom.inc';
208
+ }
209
+ $iur = new static();
210
+ return $iur->replaceHtml($html);
211
+ }
212
+ }
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/PictureTags.php ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DOMUtilForWebP;
4
+
5
+ //use Sunra\PhpSimple\HtmlDomParser;
6
+ /**
7
+ * Class PictureTags - convert an <img> tag to a <picture> tag and add the webp versions of the images
8
+ * Based this code on code from the ShortPixel plugin, which used code from Responsify WP plugin
9
+ */
10
+
11
+ use \WebPExpress\AlterHtmlHelper;
12
+
13
+ class PictureTags
14
+ {
15
+
16
+ public function replaceUrl($url)
17
+ {
18
+ if (!preg_match('#(png|jpe?g)$#', $url)) {
19
+ return;
20
+ }
21
+ return $url . '.webp';
22
+ }
23
+
24
+ public function replaceUrlOr($url, $returnValueIfDenied)
25
+ {
26
+ $url = $this->replaceUrl($url);
27
+ return (isset($url) ? $url : $returnValueIfDenied);
28
+ }
29
+
30
+ /**
31
+ * Look for attributes such as "data-lazy-src" and "data-src" and prefer them over "src"
32
+ *
33
+ * @param $attributes an array of attributes for the element
34
+ * @param $attrName ie "src", "srcset" or "sizes"
35
+ *
36
+ * @return [value:.., attrName:...] (value is the value of the attribute and
37
+ * attrName is the name of the attribute used)
38
+ *
39
+ */
40
+ private static function lazyGet($attributes, $attrName)
41
+ {
42
+ return array(
43
+ 'value' =>
44
+ (isset($attributes['data-lazy-' . $attrName]) && strlen($attributes['data-lazy-' . $attrName])) ?
45
+ trim($attributes['data-lazy-' . $attrName])
46
+ : (isset($attributes['data-' . $attrName]) && strlen($attributes['data-' . $attrName]) ?
47
+ trim($attributes['data-' . $attrName])
48
+ : (isset($attributes[$attrName]) && strlen($attributes[$attrName]) ?
49
+ trim($attributes[$attrName]) : false)),
50
+ 'attrName' =>
51
+ (isset($attributes['data-lazy-' . $attrName]) && strlen($attributes['data-lazy-' . $attrName])) ?
52
+ 'data-lazy-' . $attrName
53
+ : (isset($attributes['data-' . $attrName]) && strlen($attributes['data-' . $attrName]) ?
54
+ 'data-' . $attrName
55
+ : (isset($attributes[$attrName]) && strlen($attributes[$attrName]) ? $attrName : false))
56
+ );
57
+ }
58
+
59
+ private static function getAttributes($html)
60
+ {
61
+ if (function_exists("mb_convert_encoding")) {
62
+ $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
63
+ }
64
+ if (class_exists('\\DOMDocument')) {
65
+ $dom = new \DOMDocument();
66
+ @$dom->loadHTML($html);
67
+ $image = $dom->getElementsByTagName('img')->item(0);
68
+ $attributes = [];
69
+ foreach ($image->attributes as $attr) {
70
+ $attributes[$attr->nodeName] = $attr->nodeValue;
71
+ }
72
+ return $attributes;
73
+ } else {
74
+ //$dom = HtmlDomParser::str_get_html($html, false, false, 'UTF-8', false);
75
+ $dom = str_get_html($html, false, false, 'UTF-8', false);
76
+ if ($dom !== false) {
77
+ $elems = $dom->find('img,IMG');
78
+ foreach ($elems as $index => $elem) {
79
+ $attributes = [];
80
+ foreach ($elem->getAllAttributes() as $attrName => $attrValue) {
81
+ $attributes[strtolower($attrName)] = $attrValue;
82
+ }
83
+ return $attributes;
84
+ }
85
+ }
86
+ return [];
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Makes a string with all attributes.
92
+ *
93
+ * @param $attribute_array
94
+ * @return string
95
+ */
96
+ private static function createAttributes($attribute_array)
97
+ {
98
+ $attributes = '';
99
+ foreach ($attribute_array as $attribute => $value) {
100
+ $attributes .= $attribute . '="' . $value . '" ';
101
+ }
102
+ if ($attributes == '') {
103
+ return '';
104
+ }
105
+ // Removes the extra space after the last attribute. Add space before
106
+ return ' ' . substr($attributes, 0, -1);
107
+ }
108
+
109
+ /**
110
+ * Replace <image> tag with <picture> tag.
111
+ */
112
+ private function replaceCallback($match)
113
+ {
114
+ $imgTag = $match[0];
115
+
116
+ // Do nothing with images that have the 'webpexpress-processed' class.
117
+ if (strpos($imgTag, 'webpexpress-processed')) {
118
+ return $imgTag;
119
+ }
120
+ $imgAttributes = self::getAttributes($imgTag);
121
+
122
+ $srcInfo = self::lazyGet($imgAttributes, 'src');
123
+ $srcsetInfo = self::lazyGet($imgAttributes, 'srcset');
124
+ $sizesInfo = self::lazyGet($imgAttributes, 'sizes');
125
+
126
+ // add the exclude class so if this content is processed again in other filter,
127
+ // the img is not converted again in picture
128
+ $imgAttributes['class'] = (isset($imgAttributes['class']) ? $imgAttributes['class'] . " " : "") .
129
+ "webpexpress-processed";
130
+
131
+ $srcsetWebP = '';
132
+ if ($srcsetInfo['value']) {
133
+ $srcsetArr = explode(', ', $srcsetInfo["value"]);
134
+ $srcsetArrWebP = [];
135
+ foreach ($srcsetArr as $i => $srcSetEntry) {
136
+ // $srcSetEntry is ie "http://example.com/image.jpg 520w"
137
+ $result = preg_split('/\s+/', trim($srcSetEntry));
138
+ $src = trim($srcSetEntry);
139
+ $width = null;
140
+ if ($result && count($result) >= 2) {
141
+ list($src, $width) = $result;
142
+ }
143
+
144
+ $webpUrl = $this->replaceUrlOr($src, false);
145
+ if ($webpUrl !== false) {
146
+ $srcsetArrWebP[] = $webpUrl . (isset($width) ? ' ' . $width : '');
147
+ }
148
+ }
149
+ $srcsetWebP = implode(', ', $srcsetArrWebP);
150
+ if (strlen($srcsetWebP) == 0) {
151
+ // We have no webps for you, so no reason to create <picture> tag
152
+ return $imgTag;
153
+ }
154
+ $sizesAttr = ($sizesInfo['value'] ? (' ' . $sizesInfo['attrName'] . '="' . $sizesInfo['value'] . '"') : '');
155
+ $sourceSrcAttrName = $srcsetInfo['attrName'];
156
+ if ($sourceSrcAttrName == 'src') {
157
+ // "src" isn't allowed in <source> tag with <picture> tag as parent.
158
+ $sourceSrcAttrName = 'srcset';
159
+ }
160
+ return '<picture>'
161
+ . '<source ' . $sourceSrcAttrName . '="' . $srcsetWebP . '"' . $sizesAttr . ' type="image/webp">'
162
+ . '<img' . self::createAttributes($imgAttributes) . '>'
163
+ . '</picture>';
164
+ } else {
165
+ $srcWebP = $this->replaceUrlOr($srcInfo['value'], false);
166
+ if ($srcWebP === false) {
167
+ // No reason to create <picture> tag
168
+ return $imgTag;
169
+ }
170
+
171
+ $sourceSrcAttrName = $srcInfo['attrName'];
172
+ if ($sourceSrcAttrName == 'src') {
173
+ // "src" isn't allowed in <source> tag with <picture> tag as parent.
174
+ $sourceSrcAttrName = 'srcset';
175
+ }
176
+
177
+ return '<picture>'
178
+ . '<source ' . $sourceSrcAttrName . '="' . $srcWebP . '" type="image/webp">'
179
+ . '<img' . self::createAttributes($imgAttributes) . '>'
180
+ . '</picture>';
181
+ }
182
+ }
183
+
184
+ /*
185
+ *
186
+ */
187
+ public function replaceHtml($content)
188
+ {
189
+ // TODO: We should not replace <img> tags that are inside <picture> tags already, now should we?
190
+ return preg_replace_callback('/<img[^>]*>/i', array($this, 'replaceCallback'), $content);
191
+ }
192
+
193
+ /* Main replacer function */
194
+ public static function replace($html)
195
+ {
196
+ if (!function_exists('str_get_html')) {
197
+ require_once 'simple_html_dom/simple_html_dom.inc';
198
+ }
199
+ $pt = new static();
200
+ return $pt->replaceHtml($html);
201
+ }
202
+ }
libs/addons/includes/classes/webp/vendor/rosell-dk/dom-util-for-webp/src/simple_html_dom/simple_html_dom.inc ADDED
@@ -0,0 +1,2930 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Website: http://sourceforge.net/projects/simplehtmldom/
4
+ * Additional projects: http://sourceforge.net/projects/debugobject/
5
+ * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
6
+ * Contributions by:
7
+ * Yousuke Kumakura (Attribute filters)
8
+ * Vadim Voituk (Negative indexes supports of "find" method)
9
+ * Antcs (Constructor with automatically load contents either text or file/url)
10
+ *
11
+ * all affected sections have comments starting with "PaperG"
12
+ *
13
+ * Paperg - Added case insensitive testing of the value of the selector.
14
+ *
15
+ * Paperg - Added tag_start for the starting index of tags - NOTE: This works
16
+ * but not accurately. This tag_start gets counted AFTER \r\n have been crushed
17
+ * out, and after the remove_noice calls so it will not reflect the REAL
18
+ * position of the tag in the source, it will almost always be smaller by some
19
+ * amount. We use this to determine how far into the file the tag in question
20
+ * is. This "percentage" will never be accurate as the $dom->size is the "real"
21
+ * number of bytes the dom was created from. But for most purposes, it's a
22
+ * really good estimation.
23
+ *
24
+ * Paperg - Added the forceTagsClosed to the dom constructor. Forcing tags
25
+ * closed is great for malformed html, but it CAN lead to parsing errors.
26
+ *
27
+ * Allow the user to tell us how much they trust the html.
28
+ *
29
+ * Paperg add the text and plaintext to the selectors for the find syntax.
30
+ * plaintext implies text in the innertext of a node. text implies that the
31
+ * tag is a text node. This allows for us to find tags based on the text they
32
+ * contain.
33
+ *
34
+ * Create find_ancestor_tag to see if a tag is - at any level - inside of
35
+ * another specific tag.
36
+ *
37
+ * Paperg: added parse_charset so that we know about the character set of
38
+ * the source document. NOTE: If the user's system has a routine called
39
+ * get_last_retrieve_url_contents_content_type availalbe, we will assume it's
40
+ * returning the content-type header from the last transfer or curl_exec, and
41
+ * we will parse that and use it in preference to any other method of charset
42
+ * detection.
43
+ *
44
+ * Found infinite loop in the case of broken html in restore_noise. Rewrote to
45
+ * protect from that.
46
+ *
47
+ * PaperG (John Schlick) Added get_display_size for "IMG" tags.
48
+ *
49
+ * Licensed under The MIT License
50
+ * Redistributions of files must retain the above copyright notice.
51
+ *
52
+ * @author S.C. Chen <me578022@gmail.com>
53
+ * @author John Schlick
54
+ * @author Rus Carroll
55
+ * @version Rev. 1.8.1 (247)
56
+ * @package PlaceLocalInclude
57
+ * @subpackage simple_html_dom
58
+ */
59
+
60
+ /**
61
+ * All of the Defines for the classes below.
62
+ * @author S.C. Chen <me578022@gmail.com>
63
+ */
64
+ define('HDOM_TYPE_ELEMENT', 1);
65
+ define('HDOM_TYPE_COMMENT', 2);
66
+ define('HDOM_TYPE_TEXT', 3);
67
+ define('HDOM_TYPE_ENDTAG', 4);
68
+ define('HDOM_TYPE_ROOT', 5);
69
+ define('HDOM_TYPE_UNKNOWN', 6);
70
+ define('HDOM_QUOTE_DOUBLE', 0);
71
+ define('HDOM_QUOTE_SINGLE', 1);
72
+ define('HDOM_QUOTE_NO', 3);
73
+ define('HDOM_INFO_BEGIN', 0);
74
+ define('HDOM_INFO_END', 1);
75
+ define('HDOM_INFO_QUOTE', 2);
76
+ define('HDOM_INFO_SPACE', 3);
77
+ define('HDOM_INFO_TEXT', 4);
78
+ define('HDOM_INFO_INNER', 5);
79
+ define('HDOM_INFO_OUTER', 6);
80
+ define('HDOM_INFO_ENDSPACE', 7);
81
+
82
+ /** The default target charset */
83
+ defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
84
+
85
+ /** The default <br> text used instead of <br> tags when returning text */
86
+ defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
87
+
88
+ /** The default <span> text used instead of <span> tags when returning text */
89
+ defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
90
+
91
+ /** The maximum file size the parser should load */
92
+ defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
93
+
94
+ /** Contents between curly braces "{" and "}" are interpreted as text */
95
+ define('HDOM_SMARTY_AS_TEXT', 1);
96
+
97
+ // helper functions
98
+ // -----------------------------------------------------------------------------
99
+ // get html dom from file
100
+ // $maxlen is defined in the code as PHP_STREAM_COPY_ALL which is defined as -1.
101
+ function file_get_html(
102
+ $url,
103
+ $use_include_path = false,
104
+ $context = null,
105
+ $offset = 0,
106
+ $maxLen = -1,
107
+ $lowercase = true,
108
+ $forceTagsClosed = true,
109
+ $target_charset = DEFAULT_TARGET_CHARSET,
110
+ $stripRN = true,
111
+ $defaultBRText = DEFAULT_BR_TEXT,
112
+ $defaultSpanText = DEFAULT_SPAN_TEXT
113
+ ) {
114
+ // Ensure maximum length is greater than zero
115
+ if ($maxLen <= 0) {
116
+ $maxLen = MAX_FILE_SIZE;
117
+ }
118
+
119
+ // We DO force the tags to be terminated.
120
+ $dom = new simple_html_dom(
121
+ null,
122
+ $lowercase,
123
+ $forceTagsClosed,
124
+ $target_charset,
125
+ $stripRN,
126
+ $defaultBRText,
127
+ $defaultSpanText
128
+ );
129
+
130
+ /**
131
+ * For sourceforge users: uncomment the next line and comment the
132
+ * retrieve_url_contents line 2 lines down if it is not already done.
133
+ */
134
+ $contents = file_get_contents(
135
+ $url,
136
+ $use_include_path,
137
+ $context,
138
+ $offset,
139
+ $maxLen
140
+ );
141
+
142
+ // Paperg - use our own mechanism for getting the contents as we want to
143
+ // control the timeout.
144
+ // $contents = retrieve_url_contents($url);
145
+ if (empty($contents) || strlen($contents) > $maxLen) {
146
+ return false;
147
+ }
148
+
149
+ // The second parameter can force the selectors to all be lowercase.
150
+ $dom->load($contents, $lowercase, $stripRN);
151
+ return $dom;
152
+ }
153
+
154
+ // get html dom from string
155
+ function str_get_html(
156
+ $str,
157
+ $lowercase = true,
158
+ $forceTagsClosed = true,
159
+ $target_charset = DEFAULT_TARGET_CHARSET,
160
+ $stripRN = true,
161
+ $defaultBRText = DEFAULT_BR_TEXT,
162
+ $defaultSpanText = DEFAULT_SPAN_TEXT
163
+ ) {
164
+ $dom = new simple_html_dom(
165
+ null,
166
+ $lowercase,
167
+ $forceTagsClosed,
168
+ $target_charset,
169
+ $stripRN,
170
+ $defaultBRText,
171
+ $defaultSpanText
172
+ );
173
+
174
+ if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
175
+ $dom->clear();
176
+ return false;
177
+ }
178
+
179
+ $dom->load($str, $lowercase, $stripRN);
180
+ return $dom;
181
+ }
182
+
183
+ // dump html dom tree
184
+ function dump_html_tree($node, $show_attr = true, $deep = 0)
185
+ {
186
+ $node->dump($node);
187
+ }
188
+
189
+ /**
190
+ * simple html dom node
191
+ * PaperG - added ability for "find" routine to lowercase the value of the
192
+ * selector.
193
+ *
194
+ * PaperG - added $tag_start to track the start position of the tag in the total
195
+ * byte index
196
+ *
197
+ * @package PlaceLocalInclude
198
+ */
199
+ class simple_html_dom_node
200
+ {
201
+ /**
202
+ * Node type
203
+ *
204
+ * Default is {@see HDOM_TYPE_TEXT}
205
+ *
206
+ * @var int
207
+ */
208
+ public $nodetype = HDOM_TYPE_TEXT;
209
+
210
+ /**
211
+ * Tag name
212
+ *
213
+ * Default is 'text'
214
+ *
215
+ * @var string
216
+ */
217
+ public $tag = 'text';
218
+
219
+ /**
220
+ * List of attributes
221
+ *
222
+ * @var array
223
+ */
224
+ public $attr = array();
225
+
226
+ /**
227
+ * List of child node objects
228
+ *
229
+ * @var array
230
+ */
231
+ public $children = array();
232
+ public $nodes = array();
233
+
234
+ /**
235
+ * The parent node object
236
+ *
237
+ * @var object|null
238
+ */
239
+ public $parent = null;
240
+
241
+ // The "info" array - see HDOM_INFO_... for what each element contains.
242
+ public $_ = array();
243
+
244
+ /**
245
+ * Start position of the tag in the document
246
+ *
247
+ * @var int
248
+ */
249
+ public $tag_start = 0;
250
+
251
+ /**
252
+ * The DOM object
253
+ *
254
+ * @var object|null
255
+ */
256
+ private $dom = null;
257
+
258
+ /**
259
+ * Construct new node object
260
+ *
261
+ * Adds itself to the list of DOM Nodes {@see simple_html_dom::$nodes}
262
+ */
263
+ function __construct($dom)
264
+ {
265
+ $this->dom = $dom;
266
+ $dom->nodes[] = $this;
267
+ }
268
+
269
+ function __destruct()
270
+ {
271
+ $this->clear();
272
+ }
273
+
274
+ function __toString()
275
+ {
276
+ return $this->outertext();
277
+ }
278
+
279
+ // clean up memory due to php5 circular references memory leak...
280
+ function clear()
281
+ {
282
+ $this->dom = null;
283
+ $this->nodes = null;
284
+ $this->parent = null;
285
+ $this->children = null;
286
+ }
287
+
288
+ // dump node's tree
289
+ function dump($show_attr = true, $deep = 0)
290
+ {
291
+ $lead = str_repeat(' ', $deep);
292
+
293
+ echo $lead . $this->tag;
294
+
295
+ if ($show_attr && count($this->attr) > 0) {
296
+ echo '(';
297
+ foreach ($this->attr as $k => $v) {
298
+ echo "[$k]=>\"" . $this->$k . '", ';
299
+ }
300
+ echo ')';
301
+ }
302
+
303
+ echo "\n";
304
+
305
+ if ($this->nodes) {
306
+ foreach ($this->nodes as $c) {
307
+ $c->dump($show_attr, $deep + 1);
308
+ }
309
+ }
310
+ }
311
+
312
+
313
+ // Debugging function to dump a single dom node with a bunch of information about it.
314
+ function dump_node($echo = true)
315
+ {
316
+ $string = $this->tag;
317
+
318
+ if (count($this->attr) > 0) {
319
+ $string .= '(';
320
+ foreach ($this->attr as $k => $v) {
321
+ $string .= "[$k]=>\"" . $this->$k . '", ';
322
+ }
323
+ $string .= ')';
324
+ }
325
+
326
+ if (count($this->_) > 0) {
327
+ $string .= ' $_ (';
328
+ foreach ($this->_ as $k => $v) {
329
+ if (is_array($v)) {
330
+ $string .= "[$k]=>(";
331
+ foreach ($v as $k2 => $v2) {
332
+ $string .= "[$k2]=>\"" . $v2 . '", ';
333
+ }
334
+ $string .= ')';
335
+ } else {
336
+ $string .= "[$k]=>\"" . $v . '", ';
337
+ }
338
+ }
339
+ $string .= ')';
340
+ }
341
+
342
+ if (isset($this->text)) {
343
+ $string .= ' text: (' . $this->text . ')';
344
+ }
345
+
346
+ $string .= " HDOM_INNER_INFO: '";
347
+
348
+ if (isset($node->_[HDOM_INFO_INNER])) {
349
+ $string .= $node->_[HDOM_INFO_INNER] . "'";
350
+ } else {
351
+ $string .= ' NULL ';
352
+ }
353
+
354
+ $string .= ' children: ' . count($this->children);
355
+ $string .= ' nodes: ' . count($this->nodes);
356
+ $string .= ' tag_start: ' . $this->tag_start;
357
+ $string .= "\n";
358
+
359
+ if ($echo) {
360
+ echo $string;
361
+ return;
362
+ } else {
363
+ return $string;
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Return or set parent node
369
+ *
370
+ * @param object|null $parent (optional) The parent node, `null` to return
371
+ * the current parent node.
372
+ * @return object|null The parent node
373
+ */
374
+ function parent($parent = null)
375
+ {
376
+ // I am SURE that this doesn't work properly.
377
+ // It fails to unset the current node from it's current parents nodes or
378
+ // children list first.
379
+ if ($parent !== null) {
380
+ $this->parent = $parent;
381
+ $this->parent->nodes[] = $this;
382
+ $this->parent->children[] = $this;
383
+ }
384
+
385
+ return $this->parent;
386
+ }
387
+
388
+ /**
389
+ * @return bool True if the node has at least one child node
390
+ */
391
+ function has_child()
392
+ {
393
+ return !empty($this->children);
394
+ }
395
+
396
+ /**
397
+ * Get child node at specified index
398
+ *
399
+ * @param int $idx The index of the child node to return, `-1` to return all
400
+ * child nodes.
401
+ * @return object|array|null The child node at the specified index, all child
402
+ * nodes or null if the index is invalid.
403
+ */
404
+ function children($idx = -1)
405
+ {
406
+ if ($idx === -1) {
407
+ return $this->children;
408
+ }
409
+
410
+ if (isset($this->children[$idx])) {
411
+ return $this->children[$idx];
412
+ }
413
+
414
+ return null;
415
+ }
416
+
417
+ /**
418
+ * Get first child node
419
+ *
420
+ * @return object|null The first child node or null if the current node has
421
+ * no child nodes.
422
+ *
423
+ * @todo Use `empty()` instead of `count()` to improve performance on large
424
+ * arrays.
425
+ */
426
+ function first_child()
427
+ {
428
+ if (count($this->children) > 0) {
429
+ return $this->children[0];
430
+ }
431
+ return null;
432
+ }
433
+
434
+ /**
435
+ * Get last child node
436
+ *
437
+ * @return object|null The last child node or null if the current node has
438
+ * no child nodes.
439
+ *
440
+ * @todo Use `end()` to slightly improve performance on large arrays.
441
+ */
442
+ function last_child()
443
+ {
444
+ if (($count = count($this->children)) > 0) {
445
+ return $this->children[$count - 1];
446
+ }
447
+ return null;
448
+ }
449
+
450
+ /**
451
+ * Get next sibling node
452
+ *
453
+ * @return object|null The sibling node or null if the current node has no
454
+ * sibling nodes.
455
+ */
456
+ function next_sibling()
457
+ {
458
+ if ($this->parent === null) {
459
+ return null;
460
+ }
461
+
462
+ $idx = 0;
463
+ $count = count($this->parent->children);
464
+
465
+ while ($idx < $count && $this !== $this->parent->children[$idx]) {
466
+ ++$idx;
467
+ }
468
+
469
+ if (++$idx >= $count) {
470
+ return null;
471
+ }
472
+
473
+ return $this->parent->children[$idx];
474
+ }
475
+
476
+ /**
477
+ * Get previous sibling node
478
+ *
479
+ * @return object|null The sibling node or null if the current node has no
480
+ * sibling nodes.
481
+ */
482
+ function prev_sibling()
483
+ {
484
+ if ($this->parent === null) {
485
+ return null;
486
+ }
487
+
488
+ $idx = 0;
489
+ $count = count($this->parent->children);
490
+
491
+ while ($idx < $count && $this !== $this->parent->children[$idx]) {
492
+ ++$idx;
493
+ }
494
+
495
+ if (--$idx < 0) {
496
+ return null;
497
+ }
498
+
499
+ return $this->parent->children[$idx];
500
+ }
501
+
502
+ /**
503
+ * Traverse ancestors to the first matching tag.
504
+ *
505
+ * @param string $tag Tag to find
506
+ * @return object|null First matching node in the DOM tree or null if no
507
+ * match was found.
508
+ *
509
+ * @todo Null is returned implicitly by calling ->parent on the root node.
510
+ * This behaviour could change at any time, rendering this function invalid.
511
+ */
512
+ function find_ancestor_tag($tag)
513
+ {
514
+ global $debug_object;
515
+ if (is_object($debug_object)) {
516
+ $debug_object->debug_log_entry(1);
517
+ }
518
+
519
+ // Start by including ourselves in the comparison.
520
+ $returnDom = $this;
521
+
522
+ while (!is_null($returnDom)) {
523
+ if (is_object($debug_object)) {
524
+ $debug_object->debug_log(2, 'Current tag is: ' . $returnDom->tag);
525
+ }
526
+
527
+ if ($returnDom->tag == $tag) {
528
+ break;
529
+ }
530
+
531
+ $returnDom = $returnDom->parent;
532
+ }
533
+
534
+ return $returnDom;
535
+ }
536
+
537
+ /**
538
+ * Get node's inner text (everything inside the opening and closing tags)
539
+ *
540
+ * @return string
541
+ */
542
+ function innertext()
543
+ {
544
+ if (isset($this->_[HDOM_INFO_INNER])) {
545
+ return $this->_[HDOM_INFO_INNER];
546
+ }
547
+
548
+ if (isset($this->_[HDOM_INFO_TEXT])) {
549
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
550
+ }
551
+
552
+ $ret = '';
553
+
554
+ foreach ($this->nodes as $n) {
555
+ $ret .= $n->outertext();
556
+ }
557
+
558
+ return $ret;
559
+ }
560
+
561
+ /**
562
+ * Get node's outer text (everything including the opening and closing tags)
563
+ *
564
+ * @return string
565
+ */
566
+ function outertext()
567
+ {
568
+ global $debug_object;
569
+
570
+ if (is_object($debug_object)) {
571
+ $text = '';
572
+
573
+ if ($this->tag === 'text') {
574
+ if (!empty($this->text)) {
575
+ $text = ' with text: ' . $this->text;
576
+ }
577
+ }
578
+
579
+ $debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
580
+ }
581
+
582
+ if ($this->tag === 'root') {
583
+ return $this->innertext();
584
+ }
585
+
586
+ // trigger callback
587
+ if ($this->dom && $this->dom->callback !== null) {
588
+ call_user_func_array($this->dom->callback, array($this));
589
+ }
590
+
591
+ if (isset($this->_[HDOM_INFO_OUTER])) {
592
+ return $this->_[HDOM_INFO_OUTER];
593
+ }
594
+
595
+ if (isset($this->_[HDOM_INFO_TEXT])) {
596
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
597
+ }
598
+
599
+ // render begin tag
600
+ if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
601
+ $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
602
+ } else {
603
+ $ret = '';
604
+ }
605
+
606
+ // render inner text
607
+ if (isset($this->_[HDOM_INFO_INNER])) {
608
+ // If it's a br tag... don't return the HDOM_INNER_INFO that we
609
+ // may or may not have added.
610
+ if ($this->tag !== 'br') {
611
+ $ret .= $this->_[HDOM_INFO_INNER];
612
+ }
613
+ } else {
614
+ if ($this->nodes) {
615
+ foreach ($this->nodes as $n) {
616
+ $ret .= $this->convert_text($n->outertext());
617
+ }
618
+ }
619
+ }
620
+
621
+ // render end tag
622
+ if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
623
+ $ret .= '</' . $this->tag . '>';
624
+ }
625
+
626
+ return $ret;
627
+ }
628
+
629
+ /**
630
+ * Get node's plain text (everything excluding all tags)
631
+ *
632
+ * @return string
633
+ */
634
+ function text()
635
+ {
636
+ if (isset($this->_[HDOM_INFO_INNER])) {
637
+ return $this->_[HDOM_INFO_INNER];
638
+ }
639
+
640
+ switch ($this->nodetype) {
641
+ case HDOM_TYPE_TEXT:
642
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
643
+ case HDOM_TYPE_COMMENT:
644
+ return '';
645
+ case HDOM_TYPE_UNKNOWN:
646
+ return '';
647
+ }
648
+
649
+ if (strcasecmp($this->tag, 'script') === 0) {
650
+ return '';
651
+ }
652
+ if (strcasecmp($this->tag, 'style') === 0) {
653
+ return '';
654
+ }
655
+
656
+ $ret = '';
657
+
658
+ // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed
659
+ // for some span tags, and some p tags) $this->nodes is set to NULL.
660
+ // NOTE: This indicates that there is a problem where it's set to NULL
661
+ // without a clear happening.
662
+ // WHY is this happening?
663
+ if (!is_null($this->nodes)) {
664
+ foreach ($this->nodes as $n) {
665
+ // Start paragraph after a blank line
666
+ if ($n->tag === 'p') {
667
+ $ret .= "\n\n";
668
+ }
669
+
670
+ $ret .= $this->convert_text($n->text());
671
+
672
+ // If this node is a span... add a space at the end of it so
673
+ // multiple spans don't run into each other. This is plaintext
674
+ // after all.
675
+ if ($n->tag === 'span') {
676
+ $ret .= $this->dom->default_span_text;
677
+ }
678
+ }
679
+ }
680
+ return trim($ret);
681
+ }
682
+
683
+ /**
684
+ * Get node's xml text (inner text as a CDATA section)
685
+ *
686
+ * @return string
687
+ */
688
+ function xmltext()
689
+ {
690
+ $ret = $this->innertext();
691
+ $ret = str_ireplace('<![CDATA[', '', $ret);
692
+ $ret = str_replace(']]>', '', $ret);
693
+ return $ret;
694
+ }
695
+
696
+ // build node's text with tag
697
+ function makeup()
698
+ {
699
+ // text, comment, unknown
700
+ if (isset($this->_[HDOM_INFO_TEXT])) {
701
+ return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
702
+ }
703
+
704
+ $ret = '<' . $this->tag;
705
+ $i = -1;
706
+
707
+ foreach ($this->attr as $key => $val) {
708
+ ++$i;
709
+
710
+ // skip removed attribute
711
+ if ($val === null || $val === false) {
712
+ continue;
713
+ }
714
+
715
+ $ret .= $this->_[HDOM_INFO_SPACE][$i][0];
716
+
717
+ //no value attr: nowrap, checked selected...
718
+ if ($val === true) {
719
+ $ret .= $key;
720
+ } else {
721
+ switch ($this->_[HDOM_INFO_QUOTE][$i]) {
722
+ case HDOM_QUOTE_DOUBLE:
723
+ $quote = '"';
724
+ break;
725
+ case HDOM_QUOTE_SINGLE:
726
+ $quote = '\'';
727
+ break;
728
+ default:
729
+ $quote = '';
730
+ }
731
+
732
+ $ret .= $key
733
+ . $this->_[HDOM_INFO_SPACE][$i][1]
734
+ . '='
735
+ . $this->_[HDOM_INFO_SPACE][$i][2]
736
+ . $quote
737
+ . $val
738
+ . $quote;
739
+ }
740
+ }
741
+
742
+ $ret = $this->dom->restore_noise($ret);
743
+ return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
744
+ }
745
+
746
+ /**
747
+ * Find elements by CSS selector
748
+ *
749
+ * @param string $selector The CSS selector
750
+ * @param int|null $idx Index of element to return form the list of matching
751
+ * elements (default: `null` = disabled).
752
+ * @param bool $lowercase Matches tag names case insensitive (lowercase) if
753
+ * enabled (default: `false`)
754
+ * @return array|object|null A list of elements matching the specified CSS
755
+ * selector or a single element if $idx is specified or null if no element
756
+ * was found.
757
+ */
758
+ function find($selector, $idx = null, $lowercase = false)
759
+ {
760
+ $selectors = $this->parse_selector($selector);
761
+ if (($count = count($selectors)) === 0) {
762
+ return array();
763
+ }
764
+ $found_keys = array();
765
+
766
+ // find each selector
767
+ for ($c = 0; $c < $count; ++$c) {
768
+ // The change on the below line was documented on the sourceforge
769
+ // code tracker id 2788009
770
+ // used to be: if (($levle=count($selectors[0]))===0) return array();
771
+ if (($levle = count($selectors[$c])) === 0) {
772
+ return array();
773
+ }
774
+ if (!isset($this->_[HDOM_INFO_BEGIN])) {
775
+ return array();
776
+ }
777
+
778
+ $head = array($this->_[HDOM_INFO_BEGIN] => 1);
779
+ $cmd = ' '; // Combinator
780
+
781
+ // handle descendant selectors, no recursive!
782
+ for ($l = 0; $l < $levle; ++$l) {
783
+ $ret = array();
784
+
785
+ foreach ($head as $k => $v) {
786
+ $n = ($k === -1) ? $this->dom->root : $this->dom->nodes[$k];
787
+ //PaperG - Pass this optional parameter on to the seek function.
788
+ $n->seek($selectors[$c][$l], $ret, $cmd, $lowercase);
789
+ }
790
+
791
+ $head = $ret;
792
+ $cmd = $selectors[$c][$l][4]; // Next Combinator
793
+ }
794
+
795
+ foreach ($head as $k => $v) {
796
+ if (!isset($found_keys[$k])) {
797
+ $found_keys[$k] = 1;
798
+ }
799
+ }
800
+ }
801
+
802
+ // sort keys
803
+ ksort($found_keys);
804
+
805
+ $found = array();
806
+ foreach ($found_keys as $k => $v) {
807
+ $found[] = $this->dom->nodes[$k];
808
+ }
809
+
810
+ // return nth-element or array
811
+ if (is_null($idx)) {
812
+ return $found;
813
+ } elseif ($idx < 0) {
814
+ $idx = count($found) + $idx;
815
+ }
816
+ return (isset($found[$idx])) ? $found[$idx] : null;
817
+ }
818
+
819
+ /**
820
+ * Seek DOM elements by selector
821
+ *
822
+ * **Note**
823
+ * The selector element must be compatible to a selector from
824
+ * {@see simple_html_dom_node::parse_selector()}
825
+ *
826
+ * @param array $selector A selector element
827
+ * @param array $ret An array of matches
828
+ * @param bool $lowercase Matches tag names case insensitive (lowercase) if
829
+ * enabled (default: `false`)
830
+ * @return void
831
+ */
832
+ protected function seek($selector, &$ret, $parent_cmd, $lowercase = false)
833
+ {
834
+ global $debug_object;
835
+ if (is_object($debug_object)) {
836
+ $debug_object->debug_log_entry(1);
837
+ }
838
+
839
+ list($tag, $id, $class, $attributes, $cmb) = $selector;
840
+ $nodes = array();
841
+
842
+ if ($parent_cmd === ' ') { // Descendant Combinator
843
+ // Find parent closing tag if the current element doesn't have a closing
844
+ // tag (i.e. void element)
845
+ $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
846
+ if ($end == 0) {
847
+ $parent = $this->parent;
848
+ while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null) {
849
+ $end -= 1;
850
+ $parent = $parent->parent;
851
+ }
852
+ $end += $parent->_[HDOM_INFO_END];
853
+ }
854
+
855
+ // Get list of target nodes
856
+ $nodes_start = $this->_[HDOM_INFO_BEGIN] + 1;
857
+ $nodes_count = $end - $nodes_start;
858
+ $nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
859
+ } elseif ($parent_cmd === '>') { // Child Combinator
860
+ $nodes = $this->children;
861
+ } elseif ($parent_cmd === '+'
862
+ && $this->parent
863
+ && in_array($this, $this->parent->children)) { // Next-Sibling Combinator
864
+ $index = array_search($this, $this->parent->children, true) + 1;
865
+ $nodes[] = $this->parent->children[$index];
866
+ } elseif ($parent_cmd === '~'
867
+ && $this->parent
868
+ && in_array($this, $this->parent->children)) { // Subsequent Sibling Combinator
869
+ $index = array_search($this, $this->parent->children, true);
870
+ $nodes = array_slice($this->parent->children, $index);
871
+ }
872
+
873
+ // Go throgh each element starting at this element until the end tag
874
+ // Note: If this element is a void tag, any previous void element is
875
+ // skipped.
876
+ foreach ($nodes as $node) {
877
+ $pass = true;
878
+
879
+ // Skip root nodes
880
+ if (!$node->parent) {
881
+ $pass = false;
882
+ }
883
+
884
+ // Skip if node isn't a child node (i.e. text nodes)
885
+ if ($pass && !in_array($node, $node->parent->children, true)) {
886
+ $pass = false;
887
+ }
888
+
889
+ // Skip if tag doesn't match
890
+ if ($pass && $tag !== '' && $tag !== $node->tag && $tag !== '*') {
891
+ $pass = false;
892
+ }
893
+
894
+ // Skip if ID doesn't exist
895
+ if ($pass && $id !== '' && !isset($node->attr['id'])) {
896
+ $pass = false;
897
+ }
898
+
899
+ // Check if ID matches
900
+ if ($pass && $id !== '' && isset($node->attr['id'])) {
901
+ // Note: Only consider the first ID (as browsers do)
902
+ $node_id = explode(' ', trim($node->attr['id']))[0];
903
+
904
+ if ($id !== $node_id) {
905
+ $pass = false;
906
+ }
907
+ }
908
+
909
+ // Check if all class(es) exist
910
+ if ($pass && $class !== '' && is_array($class) && !empty($class)) {
911
+ if (isset($node->attr['class'])) {
912
+ $node_classes = explode(' ', $node->attr['class']);
913
+
914
+ if ($lowercase) {
915
+ $node_classes = array_map('strtolower', $node_classes);
916
+ }
917
+
918
+ foreach ($class as $c) {
919
+ if (!in_array($c, $node_classes)) {
920
+ $pass = false;
921
+ break;
922
+ }
923
+ }
924
+ } else {
925
+ $pass = false;
926
+ }
927
+ }
928
+
929
+ // Check attributes
930
+ if ($pass
931
+ && $attributes !== ''
932
+ && is_array($attributes)
933
+ && !empty($attributes)) {
934
+ foreach ($attributes as $a) {
935
+ list (
936
+ $att_name,
937
+ $att_expr,
938
+ $att_val,
939
+ $att_inv,
940
+ $att_case_sensitivity
941
+ ) = $a;
942
+
943
+ // Handle indexing attributes (i.e. "[2]")
944
+ /**
945
+ * Note: This is not supported by the CSS Standard but adds
946
+ * the ability to select items compatible to XPath (i.e.
947
+ * the 3rd element within it's parent).
948
+ *
949
+ * Note: This doesn't conflict with the CSS Standard which
950
+ * doesn't work on numeric attributes anyway.
951
+ */
952
+ if (is_numeric($att_name)
953
+ && $att_expr === ''
954
+ && $att_val === '') {
955
+ $count = 0;
956
+
957
+ // Find index of current element in parent
958
+ foreach ($node->parent->children as $c) {
959
+ if ($c->tag === $node->tag) {
960
+ ++$count;
961
+ }
962
+ if ($c === $node) {
963
+ break;
964
+ }
965
+ }
966
+
967
+ // If this is the correct node, continue with next
968
+ // attribute
969
+ if ($count === (int)$att_name) {
970
+ continue;
971
+ }
972
+ }
973
+
974
+ // Check attribute availability
975
+ if ($att_inv) { // Attribute should NOT be set
976
+ if (isset($node->attr[$att_name])) {
977
+ $pass = false;
978
+ break;
979
+ }
980
+ } else { // Attribute should be set
981
+ // todo: "plaintext" is not a valid CSS selector!
982
+ if ($att_name !== 'plaintext'
983
+ && !isset($node->attr[$att_name])) {
984
+ $pass = false;
985
+ break;
986
+ }
987
+ }
988
+
989
+ // Continue with next attribute if expression isn't defined
990
+ if ($att_expr === '') {
991
+ continue;
992
+ }
993
+
994
+ // If they have told us that this is a "plaintext"
995
+ // search then we want the plaintext of the node - right?
996
+ // todo "plaintext" is not a valid CSS selector!
997
+ if ($att_name === 'plaintext') {
998
+ $nodeKeyValue = $node->text();
999
+ } else {
1000
+ $nodeKeyValue = $node->attr[$att_name];
1001
+ }
1002
+
1003
+ if (is_object($debug_object)) {
1004
+ $debug_object->debug_log(
1005
+ 2,
1006
+ 'testing node: '
1007
+ . $node->tag
1008
+ . ' for attribute: '
1009
+ . $att_name
1010
+ . $att_expr
1011
+ . $att_val
1012
+ . ' where nodes value is: '
1013
+ . $nodeKeyValue
1014
+ );
1015
+ }
1016
+
1017
+ // If lowercase is set, do a case insensitive test of
1018
+ // the value of the selector.
1019
+ if ($lowercase) {
1020
+ $check = $this->match(
1021
+ $att_expr,
1022
+ strtolower($att_val),
1023
+ strtolower($nodeKeyValue),
1024
+ $att_case_sensitivity
1025
+ );
1026
+ } else {
1027
+ $check = $this->match(
1028
+ $att_expr,
1029
+ $att_val,
1030
+ $nodeKeyValue,
1031
+ $att_case_sensitivity
1032
+ );
1033
+ }
1034
+
1035
+ if (is_object($debug_object)) {
1036
+ $debug_object->debug_log(
1037
+ 2,
1038
+ 'after match: '
1039
+ . ($check ? 'true' : 'false')
1040
+ );
1041
+ }
1042
+
1043
+ if (!$check) {
1044
+ $pass = false;
1045
+ break;
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ // Found a match. Add to list and clear node
1051
+ if ($pass) {
1052
+ $ret[$node->_[HDOM_INFO_BEGIN]] = 1;
1053
+ }
1054
+ unset($node);
1055
+ }
1056
+ // It's passed by reference so this is actually what this function returns.
1057
+ if (is_object($debug_object)) {
1058
+ $debug_object->debug_log(1, 'EXIT - ret: ', $ret);
1059
+ }
1060
+ }
1061
+
1062
+ /**
1063
+ * Match value and pattern for a given CSS expression
1064
+ *
1065
+ * **Supported Expressions**
1066
+ *
1067
+ * | Expression | Description
1068
+ * | ---------- | -----------
1069
+ * | `=` | $value and $pattern must be equal
1070
+ * | `!=` | $value and $pattern must not be equal
1071
+ * | `^=` | $value must start with $pattern
1072
+ * | `$=` | $value must end with $pattern
1073
+ * | `*=` | $value must contain $pattern
1074
+ *
1075
+ * @param string $exp The expression.
1076
+ * @param string $pattern The pattern
1077
+ * @param string $value The value
1078
+ * @value bool True if $value matches $pattern
1079
+ */
1080
+ protected function match($exp, $pattern, $value, $case_sensitivity)
1081
+ {
1082
+ global $debug_object;
1083
+ if (is_object($debug_object)) {
1084
+ $debug_object->debug_log_entry(1);
1085
+ }
1086
+
1087
+ if ($case_sensitivity === 'i') {
1088
+ $pattern = strtolower($pattern);
1089
+ $value = strtolower($value);
1090
+ }
1091
+
1092
+ switch ($exp) {
1093
+ case '=':
1094
+ return ($value === $pattern);
1095
+ case '!=':
1096
+ return ($value !== $pattern);
1097
+ case '^=':
1098
+ return preg_match('/^' . preg_quote($pattern, '/') . '/', $value);
1099
+ case '$=':
1100
+ return preg_match('/' . preg_quote($pattern, '/') . '$/', $value);
1101
+ case '*=':
1102
+ return preg_match('/' . preg_quote($pattern, '/') . '/', $value);
1103
+ case '|=':
1104
+ /**
1105
+ * [att|=val]
1106
+ *
1107
+ * Represents an element with the att attribute, its value
1108
+ * either being exactly "val" or beginning with "val"
1109
+ * immediately followed by "-" (U+002D).
1110
+ */
1111
+ return strpos($value, $pattern) === 0;
1112
+ case '~=':
1113
+ /**
1114
+ * [att~=val]
1115
+ *
1116
+ * Represents an element with the att attribute whose value is a
1117
+ * whitespace-separated list of words, one of which is exactly
1118
+ * "val". If "val" contains whitespace, it will never represent
1119
+ * anything (since the words are separated by spaces). Also if
1120
+ * "val" is the empty string, it will never represent anything.
1121
+ */
1122
+ return in_array($pattern, explode(' ', trim($value)), true);
1123
+ }
1124
+ return false;
1125
+ }
1126
+
1127
+ /**
1128
+ * Parse CSS selector
1129
+ *
1130
+ * @param string $selector_string CSS selector string
1131
+ * @return array List of CSS selectors. The format depends on the type of
1132
+ * selector:
1133
+ *
1134
+ * ```php
1135
+ *
1136
+ * array( // list of selectors (each separated by a comma), i.e. 'img, p, div'
1137
+ * array( // list of combinator selectors, i.e. 'img > p > div'
1138
+ * array( // selector element
1139
+ * [0], // (string) The element tag
1140
+ * [1], // (string) The element id
1141
+ * [2], // (array<string>) The element classes
1142
+ * [3], // (array<array<string>>) The list of attributes, each
1143
+ * // with four elements: name, expression, value, inverted
1144
+ * [4] // (string) The selector combinator (' ' | '>' | '+' | '~')
1145
+ * )
1146
+ * )
1147
+ * )
1148
+ * ```
1149
+ *
1150
+ * @link https://www.w3.org/TR/selectors/#compound Compound selector
1151
+ */
1152
+ protected function parse_selector($selector_string)
1153
+ {
1154
+ global $debug_object;
1155
+ if (is_object($debug_object)) {
1156
+ $debug_object->debug_log_entry(1);
1157
+ }
1158
+
1159
+ /**
1160
+ * Pattern of CSS selectors, modified from mootools (https://mootools.net/)
1161
+ *
1162
+ * Paperg: Add the colon to the attribute, so that it properly finds
1163
+ * <tag attr:ibute="something" > like google does.
1164
+ *
1165
+ * Note: if you try to look at this attribute, you MUST use getAttribute
1166
+ * since $dom->x:y will fail the php syntax check.
1167
+ *
1168
+ * Notice the \[ starting the attribute? and the @? following? This
1169
+ * implies that an attribute can begin with an @ sign that is not
1170
+ * captured. This implies that an html attribute specifier may start
1171
+ * with an @ sign that is NOT captured by the expression. Farther study
1172
+ * is required to determine of this should be documented or removed.
1173
+ *
1174
+ * Matches selectors in this order:
1175
+ *
1176
+ * [0] - full match
1177
+ *
1178
+ * [1] - tag name
1179
+ * ([\w:\*-]*)
1180
+ * Matches the tag name consisting of zero or more words, colons,
1181
+ * asterisks and hyphens.
1182
+ *
1183
+ * [2] - id name
1184
+ * (?:\#([\w-]+))
1185
+ * Optionally matches a id name, consisting of an "#" followed by
1186
+ * the id name (one or more words and hyphens).
1187
+ *
1188
+ * [3] - class names (including dots)
1189
+ * (?:\.([\w\.-]+))?
1190
+ * Optionally matches a list of classs, consisting of an "."
1191
+ * followed by the class name (one or more words and hyphens)
1192
+ * where multiple classes can be chained (i.e. ".foo.bar.baz")
1193
+ *
1194
+ * [4] - attributes
1195
+ * ((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?
1196
+ * Optionally matches the attributes list
1197
+ *
1198
+ * [5] - separator
1199
+ * ([\/, >+~]+)
1200
+ * Matches the selector list separator
1201
+ */
1202
+ // phpcs:ignore Generic.Files.LineLength
1203
+ $pattern = "/([\w:\*-]*)(?:\#([\w-]+))?(?:|\.([\w\.-]+))?((?:\[@?(?:!?[\w:-]+)(?:(?:[!*^$|~]?=)[\"']?(?:.*?)[\"']?)?(?:\s*?(?:[iIsS])?)?\])+)?([\/, >+~]+)/is";
1204
+
1205
+ preg_match_all(
1206
+ $pattern,
1207
+ trim($selector_string) . ' ', // Add final ' ' as pseudo separator
1208
+ $matches,
1209
+ PREG_SET_ORDER
1210
+ );
1211
+
1212
+ if (is_object($debug_object)) {
1213
+ $debug_object->debug_log(2, 'Matches Array: ', $matches);
1214
+ }
1215
+
1216
+ $selectors = array();
1217
+ $result = array();
1218
+
1219
+ foreach ($matches as $m) {
1220
+ $m[0] = trim($m[0]);
1221
+
1222
+ // Skip NoOps
1223
+ if ($m[0] === '' || $m[0] === '/' || $m[0] === '//') {
1224
+ continue;
1225
+ }
1226
+
1227
+ // Convert to lowercase
1228
+ if ($this->dom->lowercase) {
1229
+ $m[1] = strtolower($m[1]);
1230
+ }
1231
+
1232
+ // Extract classes
1233
+ if ($m[3] !== '') {
1234
+ $m[3] = explode('.', $m[3]);
1235
+ }
1236
+
1237
+ /* Extract attributes (pattern based on the pattern above!)
1238
+
1239
+ * [0] - full match
1240
+ * [1] - attribute name
1241
+ * [2] - attribute expression
1242
+ * [3] - attribute value
1243
+ * [4] - case sensitivity
1244
+ *
1245
+ * Note: Attributes can be negated with a "!" prefix to their name
1246
+ */
1247
+ if ($m[4] !== '') {
1248
+ preg_match_all(
1249
+ "/\[@?(!?[\w:-]+)(?:([!*^$|~]?=)[\"']?(.*?)[\"']?)?(?:\s*?([iIsS])?)?\]/is",
1250
+ trim($m[4]),
1251
+ $attributes,
1252
+ PREG_SET_ORDER
1253
+ );
1254
+
1255
+ // Replace element by array
1256
+ $m[4] = array();
1257
+
1258
+ foreach ($attributes as $att) {
1259
+ // Skip empty matches
1260
+ if (trim($att[0]) === '') {
1261
+ continue;
1262
+ }
1263
+
1264
+ $inverted = (isset($att[1][0]) && $att[1][0] === '!');
1265
+ $m[4][] = array(
1266
+ $inverted ? substr($att[1], 1) : $att[1], // Name
1267
+ (isset($att[2])) ? $att[2] : '', // Expression
1268
+ (isset($att[3])) ? $att[3] : '', // Value
1269
+ $inverted, // Inverted Flag
1270
+ (isset($att[4])) ? strtolower($att[4]) : '', // Case-Sensitivity
1271
+ );
1272
+ }
1273
+ }
1274
+
1275
+ // Sanitize Separator
1276
+ if ($m[5] !== '' && trim($m[5]) === '') { // Descendant Separator
1277
+ $m[5] = ' ';
1278
+ } else { // Other Separator
1279
+ $m[5] = trim($m[5]);
1280
+ }
1281
+
1282
+ // Clear Separator if it's a Selector List
1283
+ if ($is_list = ($m[5] === ',')) {
1284
+ $m[5] = '';
1285
+ }
1286
+
1287
+ // Remove full match before adding to results
1288
+ array_shift($m);
1289
+ $result[] = $m;
1290
+
1291
+ if ($is_list) { // Selector List
1292
+ $selectors[] = $result;
1293
+ $result = array();
1294
+ }
1295
+ }
1296
+
1297
+ if (count($result) > 0) {
1298
+ $selectors[] = $result;
1299
+ }
1300
+ return $selectors;
1301
+ }
1302
+
1303
+ function __get($name)
1304
+ {
1305
+ if (isset($this->attr[$name])) {
1306
+ return $this->convert_text($this->attr[$name]);
1307
+ }
1308
+ switch ($name) {
1309
+ case 'outertext':
1310
+ return $this->outertext();
1311
+ case 'innertext':
1312
+ return $this->innertext();
1313
+ case 'plaintext':
1314
+ return $this->text();
1315
+ case 'xmltext':
1316
+ return $this->xmltext();
1317
+ default:
1318
+ return array_key_exists($name, $this->attr);
1319
+ }
1320
+ }
1321
+
1322
+ function __set($name, $value)
1323
+ {
1324
+ global $debug_object;
1325
+ if (is_object($debug_object)) {
1326
+ $debug_object->debug_log_entry(1);
1327
+ }
1328
+
1329
+ switch ($name) {
1330
+ case 'outertext':
1331
+ return $this->_[HDOM_INFO_OUTER] = $value;
1332
+ case 'innertext':
1333
+ if (isset($this->_[HDOM_INFO_TEXT])) {
1334
+ return $this->_[HDOM_INFO_TEXT] = $value;
1335
+ }
1336
+ return $this->_[HDOM_INFO_INNER] = $value;
1337
+ }
1338
+
1339
+ if (!isset($this->attr[$name])) {
1340
+ $this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
1341
+ $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
1342
+ }
1343
+
1344
+ $this->attr[$name] = $value;
1345
+ }
1346
+
1347
+ function __isset($name)
1348
+ {
1349
+ switch ($name) {
1350
+ case 'outertext':
1351
+ return true;
1352
+ case 'innertext':
1353
+ return true;
1354
+ case 'plaintext':
1355
+ return true;
1356
+ }
1357
+ //no value attr: nowrap, checked selected...
1358
+ return (array_key_exists($name, $this->attr)) ? true : isset($this->attr[$name]);
1359
+ }
1360
+
1361
+ function __unset($name)
1362
+ {
1363
+ if (isset($this->attr[$name])) {
1364
+ unset($this->attr[$name]);
1365
+ }
1366
+ }
1367
+
1368
+ // PaperG - Function to convert the text from one character set to another
1369
+ // if the two sets are not the same.
1370
+ function convert_text($text)
1371
+ {
1372
+ global $debug_object;
1373
+ if (is_object($debug_object)) {
1374
+ $debug_object->debug_log_entry(1);
1375
+ }
1376
+
1377
+ $converted_text = $text;
1378
+
1379
+ $sourceCharset = '';
1380
+ $targetCharset = '';
1381
+
1382
+ if ($this->dom) {
1383
+ $sourceCharset = strtoupper($this->dom->_charset);
1384
+ $targetCharset = strtoupper($this->dom->_target_charset);
1385
+ }
1386
+
1387
+ if (is_object($debug_object)) {
1388
+ $debug_object->debug_log(
1389
+ 3,
1390
+ 'source charset: '
1391
+ . $sourceCharset
1392
+ . ' target charaset: '
1393
+ . $targetCharset
1394
+ );
1395
+ }
1396
+
1397
+ if (!empty($sourceCharset)
1398
+ && !empty($targetCharset)
1399
+ && (strcasecmp($sourceCharset, $targetCharset) != 0)) {
1400
+ // Check if the reported encoding could have been incorrect and the text is actually already UTF-8
1401
+ if ((strcasecmp($targetCharset, 'UTF-8') == 0)
1402
+ && ($this->is_utf8($text))) {
1403
+ $converted_text = $text;
1404
+ } else {
1405
+ $converted_text = iconv($sourceCharset, $targetCharset, $text);
1406
+ }
1407
+ }
1408
+
1409
+ // Lets make sure that we don't have that silly BOM issue with any of the utf-8 text we output.
1410
+ if ($targetCharset === 'UTF-8') {
1411
+ if (substr($converted_text, 0, 3) === "\xef\xbb\xbf") {
1412
+ $converted_text = substr($converted_text, 3);
1413
+ }
1414
+
1415
+ if (substr($converted_text, -3) === "\xef\xbb\xbf") {
1416
+ $converted_text = substr($converted_text, 0, -3);
1417
+ }
1418
+ }
1419
+
1420
+ return $converted_text;
1421
+ }
1422
+
1423
+ /**
1424
+ * Returns true if $string is valid UTF-8 and false otherwise.
1425
+ *
1426
+ * @param mixed $str String to be tested
1427
+ * @return boolean
1428
+ */
1429
+ static function is_utf8($str)
1430
+ {
1431
+ $c = 0;
1432
+ $b = 0;
1433
+ $bits = 0;
1434
+ $len = strlen($str);
1435
+ for ($i = 0; $i < $len; $i++) {
1436
+ $c = ord($str[$i]);
1437
+ if ($c > 128) {
1438
+ if (($c >= 254)) {
1439
+ return false;
1440
+ } elseif ($c >= 252) {
1441
+ $bits = 6;
1442
+ } elseif ($c >= 248) {
1443
+ $bits = 5;
1444
+ } elseif ($c >= 240) {
1445
+ $bits = 4;
1446
+ } elseif ($c >= 224) {
1447
+ $bits = 3;
1448
+ } elseif ($c >= 192) {
1449
+ $bits = 2;
1450
+ } else {
1451
+ return false;
1452
+ }
1453
+ if (($i + $bits) > $len) {
1454
+ return false;
1455
+ }
1456
+ while ($bits > 1) {
1457
+ $i++;
1458
+ $b = ord($str[$i]);
1459
+ if ($b < 128 || $b > 191) {
1460
+ return false;
1461
+ }
1462
+ $bits--;
1463
+ }
1464
+ }
1465
+ }
1466
+ return true;
1467
+ }
1468
+
1469
+ /**
1470
+ * Function to try a few tricks to determine the displayed size of an img on
1471
+ * the page. NOTE: This will ONLY work on an IMG tag. Returns FALSE on all
1472
+ * other tag types.
1473
+ *
1474
+ * @author John Schlick
1475
+ * @version April 19 2012
1476
+ * @return array an array containing the 'height' and 'width' of the image
1477
+ * on the page or -1 if we can't figure it out.
1478
+ */
1479
+ function get_display_size()
1480
+ {
1481
+ global $debug_object;
1482
+
1483
+ $width = -1;
1484
+ $height = -1;
1485
+
1486
+ if ($this->tag !== 'img') {
1487
+ return false;
1488
+ }
1489
+
1490
+ // See if there is aheight or width attri