PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.11.1
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.11.1
4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 3.12.1 All 134 releases
optimole-wp / inc / admin.php

admin.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 3.11.1, at inc/admin.php

1,562 lines 79.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin class.
4 *
5 * Author: Andrei Baicus <andrei@themeisle.com>
6 * Created on: 19/07/2018
7 *
8 * @soundtrack Somewhere Else - Marillion
9 * @package \Optimole\Inc
10 * @author Optimole <friends@optimole.com>
11 */
12
13 /**
14 * Class Optml_Admin
15 */
16 class Optml_Admin {
17 use Optml_Normalizer;
18
19 const IMAGE_DATA_COLLECTED = 'optml_image_data_collected';
20
21 const IMAGE_DATA_COLLECTED_BATCH = 100;
22 /**
23 * Hold the settings object.
24 *
25 * @var Optml_Settings Settings object.
26 */
27 public $settings;
28
29 /**
30 * Hold the plugin conflict object.
31 *
32 * @var Optml_Conflicting_Plugins Settings object.
33 */
34 public $conflicting_plugins;
35
36 const NEW_USER_DEFAULTS_UPDATED = 'optml_defaults_updated';
37 const OLD_USER_ENABLED_LD = 'optml_enabled_limit_dimensions';
38
39 /**
40 * Optml_Admin constructor.
41 */
42 public function __construct() {
43 $this->settings = new Optml_Settings();
44 $this->conflicting_plugins = new Optml_Conflicting_Plugins();
45
46 add_filter( 'plugin_action_links_' . plugin_basename( OPTML_BASEFILE ), [ $this, 'add_action_links' ] );
47 add_action( 'admin_menu', [ $this, 'add_dashboard_page' ] );
48 add_action( 'admin_menu', [ $this, 'add_settings_subpage' ], 99 );
49 add_action( 'admin_enqueue_scripts', [ $this, 'menu_icon_style' ] );
50 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ], PHP_INT_MIN );
51 add_action( 'admin_notices', [ $this, 'add_notice' ] );
52 add_action( 'admin_notices', [ $this, 'add_notice_upgrade' ] );
53 add_action( 'admin_notices', [ $this, 'add_notice_conflicts' ] );
54 add_action( 'optml_daily_sync', [ $this, 'daily_sync' ] );
55 add_action( 'admin_init', [ $this, 'redirect_old_dashboard' ] );
56
57 if ( $this->settings->is_connected() ) {
58 add_action( 'init', [$this, 'check_domain_change'] );
59 add_action( 'optml_pull_image_data_init', [$this, 'pull_image_data_init'] );
60 add_action( 'optml_pull_image_data', [$this, 'pull_image_data'] );
61
62 // Backwards compatibility for older versions of WordPress < 6.0.0 requiring 3 parameters for this specific filter.
63 $below_6_0_0 = version_compare( get_bloginfo( 'version' ), '6.0.0', '<' );
64 if ( $below_6_0_0 ) {
65 add_filter( 'wp_insert_attachment_data', [$this, 'legacy_detect_image_title_changes'], 10, 3 );
66 } else {
67 add_filter( 'wp_insert_attachment_data', [$this, 'detect_image_title_changes'], 10, 4 );
68 }
69
70 add_action( 'updated_post_meta', [$this, 'detect_image_alt_change' ], 10, 4 );
71 add_action( 'added_post_meta', [$this, 'detect_image_alt_change'], 10, 4 );
72 add_action( 'init', [ $this, 'schedule_data_enhance_cron' ] );
73 }
74 add_action( 'init', [ $this, 'update_default_settings' ] );
75 add_action( 'init', [ $this, 'update_limit_dimensions' ] );
76 add_action( 'admin_init', [ $this, 'maybe_redirect' ] );
77 add_action( 'admin_init', [ $this, 'init_no_script' ] );
78 if ( ! is_admin() && $this->settings->is_connected() && ! wp_next_scheduled( 'optml_daily_sync' ) ) {
79 wp_schedule_event( time() + 10, 'daily', 'optml_daily_sync', [] );
80 }
81 add_action( 'optml_after_setup', [ $this, 'register_public_actions' ], 999999 );
82
83 if ( ! function_exists( 'is_wpcom_vip' ) ) {
84 add_filter( 'upload_mimes', [ $this, 'allow_meme_types' ] ); // phpcs:ignore WordPressVIPMinimum.Hooks.RestrictedHooks.upload_mimes
85 }
86 }
87 /**
88 * Schedules the hourly cron that starts the querying for images alt/title attributes
89 *
90 * @uses action: init
91 */
92 public function schedule_data_enhance_cron() {
93 if ( ! wp_next_scheduled( 'optml_pull_image_data_init' ) ) {
94 wp_schedule_event( time(), 'hourly', 'optml_pull_image_data_init' );
95 }
96 }
97
98 /**
99 * Query the database for images and extract the alt/title to send them to the API
100 *
101 * @uses action: optml_pull_image_data
102 */
103 public function pull_image_data() {
104 // Get all image attachments that are not processed
105 $args = [
106 'post_type' => 'attachment',
107 'post_mime_type' => 'image',
108 'posts_per_page' => self::IMAGE_DATA_COLLECTED_BATCH,
109 'meta_query' => [
110 'relation' => 'AND',
111 [
112 'key' => self::IMAGE_DATA_COLLECTED,
113 'compare' => 'NOT EXISTS',
114 ],
115 ],
116 ];
117 $attachments = get_posts( $args );
118
119 $image_data = [];
120
121 foreach ( $attachments as $attachment ) {
122 // Get image URL, alt, and title
123 $image_url = wp_get_attachment_url( $attachment->ID );
124 $image_alt = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true );
125 $image_title = $attachment->post_title;
126 $image_data[ $image_url ] = [];
127
128 if ( ! empty( $image_alt ) ) {
129 $image_data[ $image_url ]['alt'] = $image_alt;
130 }
131 if ( ! empty( $image_title ) ) {
132 $image_data[ $image_url ]['title'] = $image_title;
133 }
134 if ( empty( $image_data[ $image_url ] ) ) {
135 unset( $image_data[ $image_url ] );
136 }
137
138 // Mark the image as processed
139 update_post_meta( $attachment->ID, self::IMAGE_DATA_COLLECTED, 'yes' );
140 }
141
142 if ( ! empty( $image_data ) ) {
143 $api = new Optml_Api();
144 $api->call_data_enrich_api( $image_data );
145 }
146 if ( ! empty( $attachments ) ) {
147 wp_schedule_single_event( time() + 5, 'optml_pull_image_data' );
148 }
149 }
150
151 /**
152 * Schedule the event to pull image alt/title
153 *
154 * @uses action: optml_pull_image_data_init
155 */
156 public function pull_image_data_init() {
157 if ( ! wp_next_scheduled( 'optml_pull_image_data' ) ) {
158 wp_schedule_single_event( time() + 5, 'optml_pull_image_data' );
159 }
160 }
161
162 /**
163 * Delete the processed meta from an image when the alt text is changed
164 *
165 * @uses action: updated_post_meta, added_post_meta
166 */
167 public function detect_image_alt_change( $meta_id, $post_id, $meta_key, $meta_value ) {
168
169 // Check if the updated metadata is for alt text and it's an attachment
170 if ( $meta_key === '_wp_attachment_image_alt' && get_post_type( $post_id ) === 'attachment' ) {
171 delete_post_meta( $post_id, self::IMAGE_DATA_COLLECTED );
172 }
173 }
174 /**
175 * Delete the processed meta from an image when the title is changed
176 *
177 * @uses filter: wp_insert_attachment_data
178 */
179 public function detect_image_title_changes( $data, $postarr, $unsanitized_postarr, $update ) {
180
181 // Check if it's an attachment being updated
182 if ( $data['post_type'] !== 'attachment' ) {
183 return $data;
184 }
185 if ( ! isset( $postarr['ID'] ) ) {
186 return $data;
187 }
188 if ( isset( $postarr['post_title'] ) && isset( $postarr['original_post_title'] ) && $postarr['post_title'] !== $postarr['original_post_title'] ) {
189 delete_post_meta( $postarr['ID'], self::IMAGE_DATA_COLLECTED );
190 }
191
192 return $data;
193 }
194 /**
195 * Delete the processed meta from an image when the title is changed
196 *
197 * @uses filter: wp_insert_attachment_data
198 */
199 public function legacy_detect_image_title_changes( $data, $postarr, $unsanitized_postarr ) {
200 return $this->detect_image_title_changes( $data, $postarr, $unsanitized_postarr, true );
201 }
202 /**
203 * Init no_script setup value based on whether the user is connected or not.
204 */
205 public function init_no_script() {
206 if ( $this->settings->is_connected() ) {
207 $raw_settings = $this->settings->get_raw_settings();
208 if ( ! isset( $raw_settings['no_script'] ) ) {
209 $this->settings->update( 'no_script', 'enabled' );
210 }
211 }
212 }
213 /**
214 * Checks if domain has changed
215 */
216 public function check_domain_change() {
217 $previous_domain = get_option( 'optml_current_domain', 0 );
218 $site_url = $this->to_domain_hash( get_home_url() );
219
220 if ( $site_url !== $previous_domain ) {
221 update_option( 'optml_current_domain', $site_url );
222 if ( $previous_domain !== 0 ) {
223 $this->daily_sync();
224 }
225 }
226 }
227 /**
228 * Update the limit dimensions setting to enabled if the user is new.
229 */
230 public function update_default_settings() {
231 if ( get_option( self::NEW_USER_DEFAULTS_UPDATED ) === 'yes' ) {
232 return;
233 }
234
235 if ( $this->settings->is_connected() ) {
236 update_option( self::NEW_USER_DEFAULTS_UPDATED, 'yes' );
237 return;
238 }
239
240 $this->settings->update( 'limit_dimensions', 'enabled' );
241 $this->settings->update( 'lazyload', 'enabled' );
242
243 update_option( self::NEW_USER_DEFAULTS_UPDATED, 'yes' );
244 }
245 /**
246 * Enable limit dimensions for old users after removing the Resize option.
247 *
248 * @return void
249 */
250 public function update_limit_dimensions() {
251 if ( get_option( self::OLD_USER_ENABLED_LD ) === 'yes' ) {
252 return;
253 }
254
255 // New users already have this enabled as we changed defaults.
256 if ( ! $this->settings->is_connected() ) {
257 return;
258 }
259
260 $this->settings->update( 'limit_dimensions', 'enabled' );
261
262 update_option( self::OLD_USER_ENABLED_LD, 'yes' );
263 }
264 /**
265 * Adds Optimole tag to admin bar
266 */
267 public function add_report_menu() {
268 global $wp_admin_bar;
269
270 $wp_admin_bar->add_node(
271 [
272 'id' => 'optml_report_script',
273 'href' => '#',
274 'title' => '<span class="ab-icon"></span>Optimole ' . __( 'debugger', 'optimole-wp' ),
275 ]
276 );
277 $wp_admin_bar->add_menu(
278 [
279 'id' => 'optml_status',
280 'title' => __( 'Troubleshoot this page', 'optimole-wp' ),
281 'parent' => 'optml_report_script',
282 ]
283 );
284 }
285
286 /**
287 * Adds Optimole css to admin bar
288 */
289 public function print_report_css() {
290 ?>
291 <style type="text/css">
292 li#wp-admin-bar-optml_report_script > div :hover {
293 cursor: pointer;
294 color: #00b9eb !important;
295 text-decoration: underline;
296 }
297
298 #wpadminbar #wp-admin-bar-optml_report_script .ab-icon:before {
299 content: "\f227";
300 top: 3px;
301 }
302
303 /* The Modal (background) */
304 .optml-modal {
305 display: none; /* Hidden by default */
306 position: fixed; /* Stay in place */
307 z-index: 2147483641; /* Sit on top */
308 padding-top: 100px; /* Location of the box */
309 left: 0;
310 top: 0;
311 width: 100%; /* Full width */
312 height: 100%; /* Full height */
313 overflow: auto; /* Enable scroll if needed */
314 background-color: rgb(0, 0, 0); /* Fallback color */
315 background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
316 }
317
318 /* Modal Content */
319 .optml-modal-content {
320 background-color: #fefefe;
321 margin: auto;
322 padding: 20px;
323 border: 1px solid #888;
324 width: 80%;
325 }
326
327 /* The Close Button */
328 .optml-close {
329 color: #aaaaaa;
330 float: right;
331 font-size: 28px;
332 font-weight: bold;
333 }
334
335 .optml-modal-content ul {
336 list-style: none;
337 font-size: 80%;
338 margin-top: 50px;
339 }
340
341 .optml-close:hover,
342 .optml-close:focus {
343 color: #000;
344 text-decoration: none;
345 cursor: pointer;
346 }
347 </style>
348 <?php
349 }
350
351 /**
352 * Register public actions.
353 */
354 public function register_public_actions() {
355 add_action( 'wp_head', [ $this, 'generator' ] );
356 add_filter( 'wp_resource_hints', [ $this, 'add_dns_prefetch' ], 10, 2 );
357
358 if ( Optml_Manager::should_ignore_image_tags() ) {
359 return;
360 }
361 if ( ! is_admin() && $this->settings->get( 'report_script' ) === 'enabled' && current_user_can( 'manage_options' ) ) {
362
363 add_action( 'wp_head', [ $this, 'print_report_css' ] );
364 add_action( 'wp_before_admin_bar_render', [ $this, 'add_report_menu' ] );
365 add_action( 'wp_enqueue_scripts', [ $this, 'add_diagnosis_script' ] );
366 }
367 if ( ! $this->settings->use_lazyload()
368 || ( $this->settings->get( 'native_lazyload' ) === 'enabled'
369 && $this->settings->get( 'video_lazyload' ) === 'disabled'
370 && $this->settings->get( 'bg_replacer' ) === 'disabled' ) ) {
371 return;
372 }
373 add_action( 'wp_enqueue_scripts', [ $this, 'frontend_scripts' ] );
374 add_action( 'wp_head', [ $this, 'inline_bootstrap_script' ] );
375
376 add_filter( 'optml_additional_html_classes', [ $this, 'add_no_js_class_to_html_tag' ], 10 );
377 }
378
379 /**
380 * Use filter to add additional class to html tag.
381 *
382 * @param array $classes The classes to be added.
383 *
384 * @return array
385 */
386 public function add_no_js_class_to_html_tag( $classes ) {
387 // we need the space padding since some plugins might not target correctly with js there own classes
388 // this causes some issues if they concat directly, this way we can protect against that since no matter what
389 // there will be an extra space padding so that classes can be easily identified
390 return array_merge( $classes, [ ' optml_no_js ' ] );
391 }
392
393 /**
394 * Adds script for lazyload/js replacement.
395 */
396 public function inline_bootstrap_script() {
397 $domain = 'https://' . $this->settings->get_cdn_url() . '/js-lib';
398
399 if ( defined( 'OPTML_JS_CDN' ) && constant( 'OPTML_JS_CDN' ) ) {
400 $domain = 'https://' . constant( 'OPTML_JS_CDN' ) . '/js-lib';
401 }
402
403 $min = ! OPTML_DEBUG ? '.min' : '';
404 $bgclasses = Optml_Lazyload_Replacer::get_lazyload_bg_classes();
405 $watcher_classes = Optml_Lazyload_Replacer::get_watcher_lz_classes();
406 $lazyload_bg_selectors = Optml_Lazyload_Replacer::get_background_lazyload_selectors();
407 foreach ( $bgclasses as $key ) {
408 $lazyload_bg_selectors[] = '.' . $key;
409 }
410 $lazyload_bg_selectors = empty( $lazyload_bg_selectors ) ? '' : sprintf( '%s', implode( ', ', (array) $lazyload_bg_selectors ) );
411 $bgclasses = empty( $bgclasses ) ? '' : sprintf( '"%s"', implode( '","', (array) $bgclasses ) );
412 $watcher_classes = empty( $watcher_classes ) ? '' : sprintf( '"%s"', implode( '","', (array) $watcher_classes ) );
413 $default_network = ( $this->settings->get( 'network_optimization' ) === 'enabled' );
414 $limit_dimensions = $this->settings->get( 'limit_dimensions' ) === 'enabled';
415 $limit_width = $limit_dimensions ? $this->settings->get( 'limit_width' ) : 0;
416 $limit_height = $limit_dimensions ? $this->settings->get( 'limit_height' ) : 0;
417 $retina_ready = $limit_dimensions ||
418 ! ( $this->settings->get( 'retina_images' ) === 'enabled' );
419 $scale_is_disabled = ( $this->settings->get( 'scale' ) === 'enabled' );
420 $native_lazy_enabled = ( $this->settings->get( 'native_lazyload' ) === 'enabled' );
421 $output = sprintf(
422 '
423 <style type="text/css">
424 img[data-opt-src]:not([data-opt-lazy-loaded]) {
425 transition: .2s filter linear, .2s opacity linear, .2s border-radius linear;
426 -webkit-transition: .2s filter linear, .2s opacity linear, .2s border-radius linear;
427 -moz-transition: .2s filter linear, .2s opacity linear, .2s border-radius linear;
428 -o-transition: .2s filter linear, .2s opacity linear, .2s border-radius linear;
429 }
430 img[data-opt-src]:not([data-opt-lazy-loaded]) {
431 opacity: .75;
432 -webkit-filter: blur(8px);
433 -moz-filter: blur(8px);
434 -o-filter: blur(8px);
435 -ms-filter: blur(8px);
436 filter: blur(8px);
437 transform: scale(1.04);
438 animation: 0.1s ease-in;
439 -webkit-transform: translate3d(0, 0, 0);
440 }
441 %s
442 </style>
443 <script type="application/javascript">
444 document.documentElement.className = document.documentElement.className.replace(/\boptml_no_js\b/g, "");
445 (function(w, d){
446 var b = d.getElementsByTagName("head")[0];
447 var s = d.createElement("script");
448 var v = ("IntersectionObserver" in w && "isIntersecting" in w.IntersectionObserverEntry.prototype) ? "_no_poly" : "";
449 s.async = true;
450 s.src = "%s/v2/latest/optimole_lib" + v + "%s.js";
451 b.appendChild(s);
452 w.optimoleData = {
453 lazyloadOnly: "optimole-lazy-only",
454 backgroundReplaceClasses: [%s],
455 nativeLazyload : %s,
456 scalingDisabled: %s,
457 watchClasses: [%s],
458 backgroundLazySelectors: "%s",
459 network_optimizations: %s,
460 ignoreDpr: %s,
461 quality: %d,
462 maxWidth: %d,
463 maxHeight: %d,
464 }
465 }(window, document));
466 </script>',
467 Optml_Lazyload_Replacer::IFRAME_TEMP_COMMENT,
468 esc_url( $domain ),
469 $min,
470 wp_strip_all_tags( $bgclasses ),
471 $native_lazy_enabled ? 'true' : 'false',
472 $scale_is_disabled ? 'true' : 'false',
473 wp_strip_all_tags( $watcher_classes ),
474 addcslashes( wp_strip_all_tags( $lazyload_bg_selectors ), '"' ),
475 defined( 'OPTML_NETWORK_ON' ) && constant( 'OPTML_NETWORK_ON' ) ? ( OPTML_NETWORK_ON ? 'true' : 'false' ) : ( $default_network ? 'true' : 'false' ),
476 $retina_ready ? 'true' : 'false',
477 $this->settings->get_numeric_quality(),
478 $limit_width,
479 $limit_height
480 );
481 echo $output;
482 }
483
484 /**
485 * Adds script for lazyload/js replacement.
486 */
487 public function add_diagnosis_script() {
488
489 wp_enqueue_script( 'optml-report', OPTML_URL . 'assets/js/report_script.js' );
490 $ignored_domains = [ 'gravatar.com', 'instagram.com', 'fbcdn' ];
491 $report_script = [
492 'optmlCdn' => $this->settings->get_cdn_url(),
493 'restUrl' => untrailingslashit( rest_url( OPTML_NAMESPACE . '/v1' ) ) . '/check_redirects',
494 'nonce' => wp_create_nonce( 'wp_rest' ),
495 'ignoredDomains' => $ignored_domains,
496 'wait' => __( 'We are checking the current page for any issues with optimized images ...', 'optimole-wp' ),
497 'description' => __( 'Optimole page analyzer', 'optimole-wp' ),
498 ];
499 wp_localize_script( 'optml-report', 'reportScript', $report_script );
500 }
501
502 /**
503 * Add settings links in the plugin listing page.
504 *
505 * @param array $links Old plugin links.
506 *
507 * @return array Altered links.
508 */
509 function add_action_links( $links ) {
510 if ( ! is_array( $links ) ) {
511 return $links;
512 }
513
514 return array_merge(
515 $links,
516 [
517 '<a href="' . admin_url( 'admin.php?page=optimole' ) . '">' . __( 'Settings', 'optimole-wp' ) . '</a>',
518 ]
519 );
520 }
521
522 /**
523 * Check if we should show the notice.
524 *
525 * @return bool Should show?
526 */
527 public function should_show_notice() {
528 if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
529 return false;
530 }
531
532 if ( is_network_admin() ) {
533 return false;
534 }
535
536 if ( ! current_user_can( 'manage_options' ) ) {
537 return false;
538 }
539
540 if ( $this->settings->is_connected() ) {
541 return false;
542 }
543
544 $current_screen = get_current_screen();
545
546 if ( empty( $current_screen ) ) {
547 return false;
548 }
549
550 if ( ( get_option( 'optml_notice_optin', 'no' ) === 'yes' ) ) {
551 return false;
552 }
553
554 return true;
555 }
556
557 /**
558 * Show upgrade notice.
559 */
560 public function add_notice_upgrade() {
561 if ( ! $this->should_show_upgrade() ) {
562 return;
563 }
564 ?>
565 <div class="notice optml-notice-optin" style="background-color: #577BF9; color:white; border: none !important; display: flex;">
566 <div style="margin: 1% 2%;">
567 <img src='<?php echo OPTML_URL . 'assets/img/upgrade_icon.png'; ?>'>
568 </div>
569 <div style="margin-top: 0.7%;">
570 <p style="font-size: 16px !important;"> <?php printf( __( '%1$sIt seems your are close to the 5.0000 visits limit with %3$sOptiMole%4$s for this month.%2$s %5$s For a larger quota you may want to check the upgrade plans. If you exceed the quota we will need to deliver back your original, un-optimized images, which might decrease your site speed performance.', 'optimole-wp' ), '<strong>', '</strong>', '<strong>', '</strong>', '<br/><br/>', '<i>', '</i >', '<strong>', '</strong>' ); ?></p>
571 <p style="margin: 1.5% 0;">
572 <a href="https://optimole.com/pricing" target="_blank" style="border-radius: 4px;padding: 9px 10px;border: 2px solid #FFF;color: white;text-decoration: none;"><?php _e( 'Check upgrade plans', 'optimole-wp' ); ?>
573 </a>
574 <a style="padding: 2%; color: white;"
575 href="<?php echo wp_nonce_url( add_query_arg( [ 'optml_hide_upg' => 'yes' ] ), 'hide_nonce', 'optml_nonce' ); ?>"><?php _e( 'I have already done this', 'optimole-wp' ); ?></a>
576 </p>
577 </div>
578 </div>
579 <?php
580 }
581
582 /**
583 * Check if we should show the upgrade notice to users.
584 *
585 * @return bool Should we show it?
586 */
587 public function should_show_upgrade() {
588 $current_screen = get_current_screen();
589 if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
590 is_network_admin() ||
591 ! current_user_can( 'manage_options' ) ||
592 ! $this->settings->is_connected() ||
593 empty( $current_screen )
594 ) {
595 return false;
596 }
597 if ( get_option( 'optml_notice_hide_upg', 'no' ) === 'yes' ) {
598 return false;
599 }
600 if ( $current_screen->base !== 'upload' ) {
601 return false;
602 }
603 $service_data = $this->settings->get( 'service_data' );
604 if ( ! isset( $service_data['plan'] ) ) {
605 return false;
606 }
607 if ( $service_data['plan'] !== 'free' ) {
608 return false;
609 }
610 $visitors_limit = isset( $service_data['visitors_limit'] ) ? (int) $service_data['visitors_limit'] : 0;
611 $visitors_left = isset( $service_data['visitors_left'] ) ? (int) $service_data['visitors_left'] : 0;
612 if ( $visitors_limit === 0 ) {
613 return false;
614 }
615 if ( $visitors_left > 2000 ) {
616 return false;
617 }
618
619 return true;
620 }
621
622 /**
623 * CSS styles for Notice.
624 */
625 public static function notice_styles() {
626 ?>
627 <style>
628 .optml-notice-optin:not(.has-dismiss) {
629 background: url(" <?php echo esc_attr( OPTML_URL . '/assets/img/disconnected.svg' ); ?> ") #fff 100% 0 no-repeat;
630 position: relative;
631 padding: 0;
632 }
633
634 .optml-notice-optin.has-dismiss {
635 position: relative;
636 }
637
638 .optml-notice-optin .content {
639 background: rgba(255, 255, 255, 0.75);
640 display: flex;
641 align-items: center;
642 padding: 20px;
643 }
644
645 .optml-notice-optin img {
646 max-width: 100px;
647 margin-right: 20px;
648 display: none;
649 }
650
651 .optml-notice-optin .description {
652 font-size: 14px;
653 margin-bottom: 20px;
654 color: #000;
655 }
656
657 .optml-notice-optin .actions {
658 margin-top: auto;
659 display: flex;
660 gap: 20px;
661 }
662
663 @media screen and (min-width: 768px) {
664 .optml-notice-optin img {
665 display: block;
666 }
667 }
668 </style>
669 <?php
670 }
671
672 /**
673 * JS for Notice.
674 */
675 public static function notice_js( $action ) {
676 ?>
677 <script>
678 jQuery(document).ready(function($) {
679 // AJAX request to update the option value
680 $( '.optml-notice-optin button.notice-dismiss' ).click(function(e) {
681 e.preventDefault();
682
683 var notice = $(this).closest( '.optml-notice-optin' );
684 var nonce = '<?php echo esc_attr( wp_create_nonce( $action ) ); ?>';
685
686 $.ajax({
687 url: window.ajaxurl,
688 type: 'POST',
689 data: {
690 action: '<?php echo esc_attr( $action ); ?>',
691 nonce
692 },
693 complete() {
694 notice.remove();
695 }
696 });
697 });
698 });
699 </script>
700 <?php
701 }
702
703 /**
704 * Adds opt in notice.
705 */
706 public function add_notice() {
707 if ( ! $this->should_show_notice() ) {
708 return;
709 }
710
711 self::notice_styles();
712 ?>
713 <div class="notice notice-info optml-notice-optin">
714 <div class="content">
715 <img src="<?php echo OPTML_URL . '/assets/img/logo.svg'; ?>" alt="<?php echo esc_attr__( 'Logo', 'optimole-wp' ); ?>"/>
716
717 <div>
718 <p class="notice-title"> <?php echo esc_html__( 'Finish setting up!', 'optimole-wp' ); ?></p>
719 <p class="description"> <?php printf( __( 'Welcome to %1$sOptiMole%2$s, the easiest way to optimize your website images. Your users will enjoy a %3$sfaster%4$s website after you connect it with our service.', 'optimole-wp' ), '<strong>', '</strong>', '<strong>', '</strong>' ); ?></p>
720 <div class="actions">
721 <a href="<?php echo esc_url( admin_url( 'admin.php?page=optimole' ) ); ?>"
722 class="button button-primary button-hero"><?php _e( 'Connect to OptiMole', 'optimole-wp' ); ?>
723 </a>
724 <a class="button button-secondary button-hero"
725 href="<?php echo wp_nonce_url( add_query_arg( [ 'optml_hide_optin' => 'yes' ] ), 'hide_nonce', 'optml_nonce' ); ?>"><?php _e( 'I will do it later', 'optimole-wp' ); ?>
726 </a>
727 </div>
728 </div>
729 </div>
730 </div>
731 <?php
732 }
733
734 /**
735 * Adds conflicts notice.
736 */
737 public function add_notice_conflicts() {
738 if ( $this->settings->is_connected() || ! $this->conflicting_plugins->should_show_notice() ) {
739 return;
740 }
741
742 $plugins = $this->conflicting_plugins->get_conflicting_plugins();
743 $names = [];
744
745 foreach ( $plugins as $plugin ) {
746 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin );
747 $names[] = $plugin_data['Name'];
748 }
749
750 $names = implode( ', ', $names );
751
752 self::notice_styles();
753 self::notice_js( 'optml_dismiss_conflict_notice' );
754 ?>
755 <div class="notice notice-info optml-notice-optin has-dismiss">
756 <div class="content">
757 <img src="<?php echo OPTML_URL . '/assets/img/logo.svg'; ?>" alt="<?php echo esc_attr__( 'Logo', 'optimole-wp' ); ?>"/>
758
759 <div>
760 <p class="notice-title"><strong><?php echo esc_html__( 'Oops... Multiple image optimization plugins active', 'optimole-wp' ); ?></strong></p>
761 <p class="description"> <?php printf( __( 'We noticed multiple image optimization plugins active on your site, which may cause issues in Optimole. We recommend using only one image optimization plugin on your site for the best results. The following plugins may cause issues in Optimole: %2$s%1$s%3$s.', 'optimole-wp' ), $names, '<strong>', '</strong>' ); ?></p>
762 <div class="actions">
763 <a href="<?php echo esc_url( admin_url( 'plugins.php?optimole_conflicts' ) ); ?>"
764 class="button button-primary button-hero"><?php _e( 'Manage Plugins', 'optimole-wp' ); ?>
765 </a>
766 </div>
767 </div>
768 </div>
769
770 <button type="button" class="notice-dismiss">
771 <span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'optimole-wp' ); ?></span>
772 </button>
773 </div>
774 <?php
775 }
776
777 /**
778 * Add style classes for lazy loading background images.
779 */
780 protected function get_background_lazy_css() {
781
782 $watchers = Optml_Lazyload_Replacer::get_background_lazyload_selectors();
783
784 $css = [];
785 foreach ( $watchers as $selector ) {
786 $css[] = 'html ' . $selector . ':not(.optml-bg-lazyloaded)';
787 }
788 if ( empty( $css ) ) {
789 return '';
790 }
791 $css = implode( ",\n", $css ) . ' { background-image: none !important; } ';
792
793 return strip_tags( $css );
794 }
795
796 /**
797 * Enqueue frontend scripts.
798 */
799 public function frontend_scripts() {
800
801 $bg_css = $this->get_background_lazy_css();
802
803 wp_register_style( 'optm_lazyload_noscript_style', false );
804 wp_enqueue_style( 'optm_lazyload_noscript_style' );
805 wp_add_inline_style( 'optm_lazyload_noscript_style', "html.optml_no_js img[data-opt-src] { display: none !important; } \n " . $bg_css );
806
807 if ( $this->settings->use_lazyload() === true ) {
808 wp_register_script( 'optml-print', false );
809 wp_enqueue_script( 'optml-print' );
810 $script = '
811 (function(w, d){
812 w.addEventListener("beforeprint", function(){
813 let images = d.getElementsByTagName( "img" );
814 for (let img of images) {
815 if ( !img.dataset.optSrc) {
816 continue;
817 }
818 img.src = img.dataset.optSrc;
819 delete img.dataset.optSrc;
820 }
821 });
822
823 }(window, document));
824 ';
825 wp_add_inline_script( 'optml-print', $script );
826 }
827
828 }
829
830 /**
831 * Maybe redirect to dashboard page.
832 */
833 public function maybe_redirect() {
834 if ( isset( $_GET['optml_nonce'] ) && isset( $_GET['optml_hide_optin'] ) && $_GET['optml_hide_optin'] === 'yes' && wp_verify_nonce( $_GET['optml_nonce'], 'hide_nonce' ) ) {
835 update_option( 'optml_notice_optin', 'yes' );
836 }
837
838 if ( isset( $_GET['optml_nonce'] ) && isset( $_GET['optml_hide_upg'] ) && $_GET['optml_hide_upg'] === 'yes' && wp_verify_nonce( $_GET['optml_nonce'], 'hide_nonce' ) ) {
839 update_option( 'optml_notice_hide_upg', 'yes' );
840 }
841
842 if ( ! get_transient( 'optml_fresh_install' ) ) {
843 return;
844 }
845
846 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
847 return;
848 }
849
850 delete_transient( 'optml_fresh_install' );
851
852 if ( is_network_admin() || isset( $_GET['activate-multi'] ) ) {
853 return;
854 }
855
856 if ( $this->settings->is_connected() ) {
857 return;
858 }
859
860 wp_safe_redirect( admin_url( 'admin.php?page=optimole' ) );
861 exit;
862 }
863
864 /**
865 * Output Generator tag.
866 */
867 public function generator() {
868 if ( ! $this->settings->is_connected() ) {
869 return;
870 }
871 if ( ! $this->settings->is_enabled() ) {
872 return;
873 }
874 echo '<meta name="generator" content="Optimole ' . esc_attr( OPTML_VERSION ) . '">';
875 }
876
877 /**
878 * Update daily the quota routine.
879 */
880 function daily_sync() {
881
882 $api_key = $this->settings->get( 'api_key' );
883 $service_data = $this->settings->get( 'service_data' );
884 $application = '';
885
886 if ( isset( $service_data['cdn_key'] ) ) {
887 $application = $service_data['cdn_key'];
888 }
889
890 if ( empty( $api_key ) ) {
891 return;
892 }
893
894 $request = new Optml_Api();
895 $data = $request->get_user_data( $api_key, $application );
896 if ( $data === false || is_wp_error( $data ) ) {
897 return;
898 }
899 if ( $data === 'disconnect' ) {
900 $settings = new Optml_Settings();
901 $settings->reset();
902 return;
903 }
904
905 add_filter( 'optml_dont_trigger_settings_updated', '__return_true' );
906 $this->settings->update( 'service_data', $data );
907
908 if ( isset( $data['extra_visits'] ) ) {
909 $this->settings->update_frontend_banner_from_remote( $data['extra_visits'] );
910 }
911
912 remove_filter( 'optml_dont_trigger_settings_updated', '__return_true' );
913
914 }
915
916 /**
917 * Adds cdn url for prefetch.
918 *
919 * @param array $hints Hints array.
920 * @param string $relation_type Type of relation.
921 *
922 * @return array Altered hints array.
923 */
924 public function add_dns_prefetch( $hints, $relation_type ) {
925 if ( 'dns-prefetch' !== $relation_type &&
926 'preconnect' !== $relation_type
927 ) {
928 return $hints;
929 }
930 if ( ! $this->settings->is_connected() ) {
931 return $hints;
932 }
933 if ( ! $this->settings->is_enabled() ) {
934 return $hints;
935 }
936 $hints[] = sprintf( 'https://%s', $this->settings->get_cdn_url() );
937
938 return $hints;
939 }
940
941 /**
942 * Add the dashboard page.
943 */
944 public function add_dashboard_page() {
945 if ( defined( 'OPTIOMLE_HIDE_ADMIN_AREA' ) && OPTIOMLE_HIDE_ADMIN_AREA ) {
946 return;
947 }
948
949 add_menu_page(
950 'Optimole',
951 'Optimole',
952 'manage_options',
953 'optimole',
954 [ $this, 'render_dashboard_page' ],
955 OPTML_URL . 'assets/img/logo.svg',
956 11
957 );
958 }
959
960 /**
961 * Add menu icon style.
962 *
963 * @return void
964 */
965 public function menu_icon_style() {
966 echo '<style>#toplevel_page_optimole img{ max-width:22px;padding-top:6px!important;opacity:.9!important;} #toplevel_page_optimole li.wp-first-item{ display:none }</style>';
967 }
968
969 /**
970 * Add the settings page.
971 *
972 * We do it with another priority as it goes above the cloud library otherwise.
973 */
974 public function add_settings_subpage() {
975 if ( defined( 'OPTIOMLE_HIDE_ADMIN_AREA' ) && OPTIOMLE_HIDE_ADMIN_AREA ) {
976 return;
977 }
978
979 if ( ! $this->settings->is_connected() ) {
980 return;
981 }
982
983 add_submenu_page(
984 'optimole',
985 __( 'Settings', 'optimole-wp' ),
986 __( 'Settings', 'optimole-wp' ),
987 'manage_options',
988 'optimole#settings',
989 [ $this, 'render_dashboard_page' ]
990 );
991 }
992
993
994 /**
995 * Redirect old dashboard.
996 *
997 * @return void
998 */
999 public function redirect_old_dashboard() {
1000 if ( ! is_admin() ) {
1001 return;
1002 }
1003
1004 global $pagenow;
1005
1006 if ( $pagenow !== 'upload.php' ) {
1007 return;
1008 }
1009
1010 if ( ! isset( $_GET['page'] ) ) {
1011 return;
1012 }
1013
1014 if ( $_GET['page'] !== 'optimole' ) {
1015 return;
1016 }
1017
1018 wp_safe_redirect( admin_url( 'admin.php?page=optimole' ) );
1019
1020 exit;
1021 }
1022
1023 /**
1024 * Render dashboard page.
1025 */
1026 public function render_dashboard_page() {
1027 if ( get_option( 'optml_notice_optin', 'no' ) !== 'yes' ) {
1028 update_option( 'optml_notice_optin', 'yes' );
1029 }
1030 ?>
1031 <div id="optimole-app"></div>
1032 <?php
1033 }
1034
1035 /**
1036 * Enqueue scripts needed for admin functionality.
1037 *
1038 * @codeCoverageIgnore
1039 */
1040 public function enqueue() {
1041
1042 $current_screen = get_current_screen();
1043 if ( ! isset( $current_screen->id ) ) {
1044 return;
1045 }
1046
1047 if ( $current_screen->id !== 'toplevel_page_optimole' ) {
1048 return;
1049 }
1050
1051 $asset_file = include OPTML_PATH . 'assets/build/dashboard/index.asset.php';
1052
1053 wp_register_script(
1054 OPTML_NAMESPACE . '-admin',
1055 OPTML_URL . 'assets/build/dashboard/index.js',
1056 $asset_file['dependencies'],
1057 $asset_file['version'],
1058 true
1059 );
1060
1061 wp_localize_script( OPTML_NAMESPACE . '-admin', 'optimoleDashboardApp', $this->localize_dashboard_app() );
1062 wp_enqueue_script( OPTML_NAMESPACE . '-admin' );
1063
1064 wp_enqueue_style(
1065 OPTML_NAMESPACE . '-admin',
1066 OPTML_URL . 'assets/build/dashboard/style-index.css',
1067 [
1068 'wp-components',
1069 ],
1070 $asset_file['version']
1071 );
1072 }
1073
1074 /**
1075 * Localize the dashboard app.
1076 *
1077 * @codeCoverageIgnore
1078 * @return array
1079 */
1080 private function localize_dashboard_app() {
1081
1082 global $wp_version;
1083 $is_offload_media_available = 'no';
1084 if ( version_compare( $wp_version, '5.3', '>=' ) ) {
1085 $is_offload_media_available = 'yes';
1086 }
1087 $api_key = $this->settings->get( 'api_key' );
1088 $service_data = $this->settings->get( 'service_data' );
1089 $user = get_userdata( get_current_user_id() );
1090 $user_status = 'inactive';
1091 $auto_connect = get_option( Optml_Settings::OPTML_USER_EMAIL, 'no' );
1092 $available_apps = isset( $service_data['available_apps'] ) ? $service_data['available_apps'] : null;
1093 if ( isset( $service_data['cdn_key'] ) && $available_apps !== null ) {
1094 foreach ( $service_data['available_apps'] as $app ) {
1095 if ( isset( $app['key'] ) && $app['key'] === $service_data['cdn_key'] && isset( $app['status'] ) && $app['status'] === 'active' ) {
1096 $user_status = 'active';
1097 }
1098 }
1099 }
1100 $routes = [];
1101 foreach ( Optml_Rest::$rest_routes as $route_type ) {
1102 foreach ( $route_type as $route => $details ) {
1103 $routes[ $route ] = OPTML_NAMESPACE . '/v1/' . $route;
1104 }
1105 }
1106
1107 return [
1108 'strings' => $this->get_dashboard_strings(),
1109 'assets_url' => OPTML_URL . 'assets/',
1110 'dam_url' => 'admin.php?page=optimole-dam',
1111 'connection_status' => empty( $service_data ) ? 'no' : 'yes',
1112 'has_application' => isset( $service_data['app_count'] ) && $service_data['app_count'] >= 1 ? 'yes' : 'no',
1113 'user_status' => $user_status,
1114 'available_apps' => $available_apps,
1115 'api_key' => $api_key,
1116 'routes' => $routes,
1117 'nonce' => wp_create_nonce( 'wp_rest' ),
1118 'user_data' => $service_data,
1119 'remove_latest_images' => defined( 'OPTML_REMOVE_LATEST_IMAGES' ) && constant( 'OPTML_REMOVE_LATEST_IMAGES' ) ? ( OPTML_REMOVE_LATEST_IMAGES ? 'yes' : 'no' ) : 'no',
1120 'current_user' => [
1121 'email' => $user->user_email,
1122 ],
1123 'site_settings' => $this->settings->get_site_settings(),
1124 'home_url' => home_url(),
1125 'is_offload_media_available' => $is_offload_media_available,
1126 'auto_connect' => $auto_connect,
1127 'submenu_links' => [
1128 [
1129 'href' => 'admin.php?page=optimole#settings',
1130 'text' => __( 'Settings', 'optimole-wp' ),
1131 'hash' => '#settings',
1132 ],
1133 ],
1134 ];
1135 }
1136
1137 /**
1138 * Get all dashboard strings.
1139 *
1140 * @codeCoverageIgnore
1141 * @return array
1142 */
1143 private function get_dashboard_strings() {
1144 return [
1145 'optimole' => 'Optimole',
1146 'version' => OPTML_VERSION,
1147 'terms_menu' => __( 'Terms', 'optimole-wp' ),
1148 'privacy_menu' => __( 'Privacy', 'optimole-wp' ),
1149 'testdrive_menu' => __( 'Test Optimole', 'optimole-wp' ),
1150 'service_details' => __( 'Image optimization service', 'optimole-wp' ),
1151 'connect_btn' => __( 'Connect to Optimole', 'optimole-wp' ),
1152 'disconnect_btn' => __( 'Disconnect', 'optimole-wp' ),
1153 'select' => __( 'Select', 'optimole-wp' ),
1154 'your_domain' => __( 'your domain', 'optimole-wp' ),
1155 'add_api' => __( 'Add your API Key', 'optimole-wp' ),
1156 'your_api_key' => __( 'Your API Key', 'optimole-wp' ),
1157 'looking_for_api_key' => __( 'LOOKING FOR YOUR API KEY?', 'optimole-wp' ),
1158 'refresh_stats_cta' => __( 'Refresh Stats', 'optimole-wp' ),
1159 'updating_stats_cta' => __( 'UPDATING STATS', 'optimole-wp' ),
1160 'api_key_placeholder' => __( 'API Key', 'optimole-wp' ),
1161 'account_needed_heading' => __( 'Sign-up for API key', 'optimole-wp' ),
1162 'invalid_key' => __( 'Invalid API Key', 'optimole-wp' ),
1163 'keep_connected' => __( 'Ok, keep me connected', 'optimole-wp' ),
1164 'cloud_library' => __( 'Cloud Library', 'optimole-wp' ),
1165 'disconnect_title' => __( 'You are about to disconnect from the Optimole API', 'optimole-wp' ),
1166 'disconnect_desc' => __(
1167 'Please note that disconnecting your site from the Optimole API will impact your website performance.
1168 If you still want to disconnect click the button below.',
1169 'optimole-wp'
1170 ),
1171 'email_address_label' => __( 'Your email address', 'optimole-wp' ),
1172 'steps_connect_api_title' => __( 'Connect your account', 'optimole-wp' ),
1173 'register_btn' => __( 'Create & connect your account ', 'optimole-wp' ),
1174 'step_one_api_title' => __( 'Enter your API key.', 'optimole-wp' ),
1175 'optml_dashboard' => sprintf( __( 'Get it from the %1$s Optimole Dashboard%2$s.', 'optimole-wp' ), '<a style="white-space:nowrap; text-decoration: underline !important;" href="https://dashboard.optimole.com/" target="_blank"> ', '<span style="text-decoration:none; font-size:15px; margin-top:2px;" class="dashicons dashicons-external"></span></a>' ),
1176 'steps_connect_api_desc' => sprintf( __( 'Copy the API Key you have received via email or you can get it from %1$s Optimole dashboard%2$s. If your account has multiple domains select the one you want to use. <br/>', 'optimole-wp' ), '<a href="https://dashboard.optimole.com/" target="_blank"> ', '</a>' ),
1177 'api_exists' => __( 'I already have an API key.', 'optimole-wp' ),
1178 'back_to_register' => __( 'Register account', 'optimole-wp' ),
1179 'back_to_connect' => __( 'Go to previous step', 'optimole-wp' ),
1180 'error_register' => sprintf( __( 'Error registering account. You can try again %1$shere%2$s ', 'optimole-wp' ), '<a href="https://dashboard.optimole.com/register" target="_blank"> ', '</a>' ),
1181 'invalid_email' => __( 'Please use a valid email address.', 'optimole-wp' ),
1182 'connected' => __( 'CONNECTED', 'optimole-wp' ),
1183 'connecting' => __( 'CONNECTING', 'optimole-wp' ),
1184 'not_connected' => __( 'NOT CONNECTED', 'optimole-wp' ),
1185 'usage' => __( 'Monthly Usage', 'optimole-wp' ),
1186 'quota' => __( 'Monthly visits quota', 'optimole-wp' ),
1187 'logged_in_as' => __( 'LOGGED IN AS', 'optimole-wp' ),
1188 'private_cdn_url' => __( 'IMAGES DOMAIN', 'optimole-wp' ),
1189 'existing_user' => __( 'Existing user?', 'optimole-wp' ),
1190 'notification_message_register' => __( 'We sent you the API Key in the email. Add it below to connect to Optimole.', 'optimole-wp' ),
1191 'premium_support' => __( 'Access our Premium Support', 'optimole-wp' ),
1192 'account_needed_title' => sprintf(
1193 __( 'In order to get access to free image optimization service you will need an API key from %s.', 'optimole-wp' ),
1194 ' <a href="https://dashboard.optimole.com/register" target="_blank">optimole.com</a>'
1195 ),
1196 'account_needed_subtitle_1' => sprintf(
1197 __( 'You will get access to our %1$simage optimization service for FREE%2$s in the limit of %3$s5k%4$s %5$svisitors%6$s per month. ', 'optimole-wp' ),
1198 '<strong>',
1199 '</strong>',
1200 '<strong>',
1201 '</strong>',
1202 '<a href="https://docs.optimole.com/article/1134-how-optimole-counts-the-number-of-visitors" target="_blank">',
1203 '</a>'
1204 ),
1205 'account_needed_subtitle_3' => sprintf(
1206 __( 'Need help? %1$sGetting Started with Optimole%2$s', 'optimole-wp' ),
1207 '<a target="_blank" href="https://docs.optimole.com/article/1173-how-to-get-started-with-optimole-in-just-3-steps">',
1208 '</a>'
1209 ),
1210 'account_needed_subtitle_2' => sprintf(
1211 __(
1212 'Bonus, if you dont use a CDN, we got you covered, %1$swe will serve the images using CloudFront CDN%2$s from 450+ locations.',
1213 'optimole-wp'
1214 ),
1215 '<strong>',
1216 '</strong>'
1217 ),
1218 'account_needed_footer' => __( 'Trusted by more than 100k happy users', 'optimole-wp' ),
1219 'account_connecting_title' => __( 'Connecting to Optimole', 'optimole-wp' ),
1220 'account_connecting_subtitle' => __( 'Sit tight while we connect you to the Dashboard', 'optimole-wp' ),
1221 'notice_just_activated' => ! $this->settings->is_connected() ?
1222 sprintf( __( '%1$sImage optimisation is currently running.%2$s <br/> Your visitors will now view the best image for their device automatically, all served from the Optimole Cloud Service on the fly. You might see for the very first image request being redirected to the original URL while we do the optimization in the background. You can relax, we\'ll take it from here.', 'optimole-wp' ), '<strong>', '</strong>' )
1223 : '',
1224 'notice_api_not_working' => __(
1225 'It seems there is an issue with your WordPress configuration and the core REST API functionality is not available. This is crucial as Optimole relies on this functionality in order to work.<br/>
1226 The root cause might be either a security plugin which blocks this feature or some faulty server configuration which constrain this WordPress feature.You can try to disable any of the security plugins that you use in order to see if the issue persists or ask the hosting company to further investigate.',
1227 'optimole-wp'
1228 ),
1229 'notice_disabled_account' => sprintf( __( '%3$sYour account has been disabled due to exceeding quota.%4$s All images are being redirected to the original unoptimized URL. %5$sPlease %1$supgrade%2$s to re-activate the account.', 'optimole-wp' ), '<b><a href="https://optimole.com/pricing">', '</a></b>', '<b>', '</b>', '<br>' ),
1230 'signup_terms' => sprintf( __( 'By signing up, you agree to our %1$sTerms of Service %3$s and %2$sPrivacy Policy %3$s.', 'optimole-wp' ), '<a href="https://optimole.com/terms/" target="_blank" >', '<a href="https://optimole.com/privacy-policy/" target="_blank">', '</a>' ),
1231 'dashboard_menu_item' => __( 'Dashboard', 'optimole-wp' ),
1232 'settings_menu_item' => __( 'Settings', 'optimole-wp' ),
1233 'help_menu_item' => __( 'Help', 'optimole-wp' ),
1234 'settings_exclusions_menu_item' => __( 'Exclusions', 'optimole-wp' ),
1235 'settings_resize_menu_item' => __( 'Resize', 'optimole-wp' ),
1236 'settings_compression_menu_item' => __( 'Compression', 'optimole-wp' ),
1237 'advanced_settings_menu_item' => __( 'Advanced', 'optimole-wp' ),
1238 'general_settings_menu_item' => __( 'General', 'optimole-wp' ),
1239 'lazyload_settings_menu_item' => __( 'Lazyload', 'optimole-wp' ),
1240 'offload_media_settings_menu_item' => __( 'Cloud Integration', 'optimole-wp' ),
1241 'watermarks_menu_item' => __( 'Watermark', 'optimole-wp' ),
1242 'conflicts_menu_item' => __( 'Possible Issues', 'optimole-wp' ),
1243 'conflicts' => [
1244 'title' => __( 'We might have some possible conflicts with the plugins that you use. In order to benefit from Optimole\'s full potential you will need to address this issues.', 'optimole-wp' ),
1245 'message' => __( 'Details', 'optimole-wp' ),
1246 'conflict_close' => __( 'I\'ve done this.', 'optimole-wp' ),
1247 'no_conflicts_found' => __( 'No conflicts found. We are all peachy now. 🍑', 'optimole-wp' ),
1248 ],
1249 'upgrade' => [
1250 'title' => __( 'Upgrade', 'optimole-wp' ),
1251 'title_long' => __( 'Upgrade to Optimole Pro', 'optimole-wp' ),
1252 'reason_1' => __( 'Priority & Live Chat support', 'optimole-wp' ),
1253 'reason_2' => __( 'Extend visits limit', 'optimole-wp' ),
1254 'reason_3' => __( 'Custom domain', 'optimole-wp' ),
1255 'reason_4' => __( 'Site audit', 'optimole-wp' ),
1256 'cta' => __( 'View plans', 'optimole-wp' ),
1257 ],
1258 'neve' => [
1259 'is_active' => defined( 'NEVE_VERSION' ) ? 'yes' : 'no',
1260 'byline' => __( 'Fast, perfomance built-in WordPress theme.', 'optimole-wp' ),
1261 'reason_1' => __( 'Lightweight, 25kB in page-weight.', 'optimole-wp' ),
1262 'reason_2' => __( '100+ Starter Sites available.', 'optimole-wp' ),
1263 'reason_3' => __( 'AMP/Mobile ready.', 'optimole-wp' ),
1264 'reason_4' => __( 'Lots of customizations options.', 'optimole-wp' ),
1265 'reason_5' => __( 'Fully compatible with Optimole.', 'optimole-wp' ),
1266 ],
1267 'metrics' => [
1268 'metricsTitle1' => __( 'Images optimized', 'optimole-wp' ),
1269 'metricsSubtitle1' => __( 'Since plugin activation', 'optimole-wp' ),
1270 'metricsTitle2' => __( 'Saved file size', 'optimole-wp' ),
1271 'metricsSubtitle2' => __( 'For the latest 10 images', 'optimole-wp' ),
1272 'metricsTitle3' => __( 'Average compression', 'optimole-wp' ),
1273 'metricsSubtitle3' => __( 'During last month', 'optimole-wp' ),
1274 'metricsTitle4' => __( 'Traffic', 'optimole-wp' ),
1275 'metricsSubtitle4' => __( 'During last month', 'optimole-wp' ),
1276 ],
1277 'options_strings' => [
1278 'best_format_title' => __( 'Automatic Best Image Format Selection', 'optimole-wp' ),
1279 'best_format_desc' => sprintf( __( 'When enabled, Optimole picks the ideal format for your images, balancing quality and speed. It tests different formats, like AVIF and WebP, ensuring images look good and load quickly. %1$sLearn more%2$s.', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1942-best-format">', '</a>' ),
1280 'add_filter' => __( 'Add filter', 'optimole-wp' ),
1281 'add_site' => __( 'Add site', 'optimole-wp' ),
1282 'admin_bar_desc' => __( 'Show in the WordPress admin bar the available quota from Optimole service.', 'optimole-wp' ),
1283 'auto_q_title' => __( 'Auto', 'optimole-wp' ),
1284 'cache_desc' => sprintf( __( 'Clears all Optimole’s cached resources (images, JS, CSS). Useful if you made changes to your images and don\'t see those applying on your site. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1941-clear-cached-resources">', '</a>' ),
1285 'cache_title' => __( 'Clear Cached Resources', 'optimole-wp' ),
1286 'clear_cache_notice' => __( 'Clearing cached resources will re-optimize the images and might affect the site performance for a few minutes.', 'optimole-wp' ),
1287 'image_size_notice' => sprintf( __( 'Use this option if you notice images are not cropped correctly after using Optimole. Add the affected image sizes here to automatically adjust and correct their cropping for optimal display. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1947-add-new-image-crop-size">', '</a>' ),
1288 'clear_cache_images' => __( 'Clear cached images', 'optimole-wp' ),
1289 'clear_cache_assets' => __( 'Clear cached CSS & JS', 'optimole-wp' ),
1290 'connect_step_0' => __( 'Connecting your site to the Optimole service.', 'optimole-wp' ),
1291 'connect_step_1' => __( 'Checking for possible conflicts.', 'optimole-wp' ),
1292 'connect_step_2' => __( 'Inspecting the images from your site.', 'optimole-wp' ),
1293 'connect_step_3' => __( 'All done, Optimole is currently optimizing your site.', 'optimole-wp' ),
1294 'disabled' => __( 'Disabled', 'optimole-wp' ),
1295 'enable_avif_title' => __( 'AVIF Image Support', 'optimole-wp' ),
1296 'enable_avif_desc' => sprintf( __( 'Enable this to automatically convert images to the AVIF format on browsers that support it. This format provides quality images with reduced file sizes, and faster webpage loading. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1943-avif-conversion">', '</a>' ),
1297 'enable_bg_lazyload_desc' => sprintf( __( 'Enable this to lazy-load images set as CSS backgrounds. If Optimole misses any, you can directly target specific CSS selectors to ensure all background images are optimized. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1169-how-to-enable-the-background-lazyload-feature-for-certain-background-images">', '</a>' ),
1298 'enable_bg_lazyload_title' => __( 'CSS Background Lazy Load', 'optimole-wp' ),
1299 'enable_video_lazyload_desc' => sprintf( __( 'By default, lazy loading does not work for embedded videos and iframes. Enable this option to activate the lazy-load on these elements. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1952-lazy-loading-for-embedded-videos-and-iframes">', '</a>' ),
1300 'enable_video_lazyload_title' => __( 'Lazy Loading for Embedded Videos and Iframes', 'optimole-wp' ),
1301 'enable_noscript_desc' => sprintf( __( 'Enables fallback images for browsers that can\'t handle JavaScript-based lazy loading or related features. Disabling it may resolve conflicts with other plugins or configurations and decrease HTML page size. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1959-noscript-tag">', '</a>' ),
1302 'enable_noscript_title' => __( 'Noscript Tag', 'optimole-wp' ),
1303 'enable_gif_replace_title' => __( 'GIF to Video Conversion', 'optimole-wp' ),
1304 'enable_report_title' => __( 'Enable Error Diagnosis Tool', 'optimole-wp' ),
1305 'enable_report_desc' => sprintf( __( 'Activates the Optimole debugging tool in the admin bar for reports on Optimole-related website issues using the built-in diagnostic feature. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1390-how-does-the-error-diagnosis-tool-work">', '</a>' ),
1306 'enable_offload_media_title' => __( 'Store Your Images in Optimole Cloud', 'optimole-wp' ),
1307 'enable_offload_media_desc' => sprintf( __( 'Free up space on your server by transferring your images to Optimole Cloud; you can transfer them back anytime. Once moved, the images will still be visible in the Media Library and can be used as before. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1967-store-your-images-in-optimole-cloud">', '</a>' ),
1308 'enable_cloud_images_title' => __( 'Unified Image Access', 'optimole-wp' ),
1309 'enable_cloud_images_desc' => sprintf( __( 'Enable this setting to access all your Optimole images, including those from other websites connected to your Optimole account, directly on this site. They will be available for browsing in the Cloud Library tab. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1323-cloud-library-browsing">', '</a>' ),
1310 'enable_image_replace' => __( 'Enable Optimole Image Handling', 'optimole-wp' ),
1311 'enable_lazyload_placeholder_desc' => sprintf( __( 'Enable this to use a generic transparent placeholder instead of the blurry images during lazy loading. Enhance the visual experience by selecting a custom color for the placeholder. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1192-lazy-load-generic-placeholder">', '</a>' ),
1312 'enable_lazyload_placeholder_title' => __( 'Lazy Load with Generic Placeholder', 'optimole-wp' ),
1313 'enable_network_opt_desc' => sprintf( __( 'When enabled, Optimole will automatically reduce the image quality when it detects a slower network, making your images load faster on low-speed internet connections. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1945-network-based-optimizations">', '</a>' ),
1314 'enable_network_opt_title' => __( 'Network-based Optimizations', 'optimole-wp' ),
1315 'enable_resize_smart_desc' => sprintf( __( 'When enabled, Optimole automatically detects the most interesting or important part of your images. When pictures are resized or cropped, this feature ensures the focus stays on the most interesting part of the picture. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1871-how-to-use-the-smart-cropping-in-optimole">', '</a>' ),
1316 'enable_resize_smart_title' => __( 'Smart Cropping', 'optimole-wp' ),
1317 'enable_retina_desc' => sprintf( __( 'Enable this feature to optimize your images for Retina displays. Retina-ready images are optimized to look sharp on screens with higher pixel density, offering viewers enhanced visual quality. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1391-what-is-a-retina-image">', '</a>' ),
1318 'enable_retina_title' => __( 'Retina Quality', 'optimole-wp' ),
1319 'enable_limit_dimensions_desc' => sprintf( __( 'Define the max width or height limits for images on your website. Larger images will be automatically adjusted to fit within these parameters while preserving their original aspect ratio. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1946-limit-image-sizes">', '</a>' ),
1320 'enable_limit_dimensions_title' => __( 'Limit Image Sizes', 'optimole-wp' ),
1321 'enable_limit_dimensions_notice' => __( 'When you enable this feature to define a max width or height for image resizing, please note that DPR (retina) images will be disabled. This is done to ensure consistency in image dimensions across your website. Although this may result in slightly lower image quality for high-resolution displays, it will help maintain uniform image sizes, improving your website\'s overall layout and potentially boosting performance. ', 'optimole-wp' ),
1322 'enable_badge_title' => __( 'Enable Optimole Badge', 'optimole-wp' ),
1323 'enable_badge_description' => sprintf( __( 'Get 20.000 more visits for free by enabling the Optimole badge on your websites. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1940-optimole-badge">', '</a>' ),
1324 'image_sizes_title' => __( 'Your cropped image sizes', 'optimole-wp' ),
1325 'enabled' => __( 'Enabled', 'optimole-wp' ),
1326 'exclude_class_desc' => sprintf( __( '%1$sImage tag%2$s contains class', 'optimole-wp' ), '<strong>', '</strong>' ),
1327 'exclude_ext_desc' => sprintf( __( '%1$sImage extension%2$s is', 'optimole-wp' ), '<strong>', '</strong>' ),
1328 'exclude_filename_desc' => sprintf( __( '%1$sImage filename%2$s contains', 'optimole-wp' ), '<strong>', '</strong>' ),
1329 'exclude_desc_optimize' => sprintf( __( 'Here you can define exceptions, in case you don\'t want some images to be optimised. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1191-exclude-from-optimizing-or-lazy-loading">', '</a>' ),
1330 'exclude_title_lazyload' => __( 'Don\'t lazy-load images if', 'optimole-wp' ),
1331 'exclude_desc_lazyload' => sprintf( __( 'Define exceptions, in case you don\'t want the lazy-load to be active on certain images. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1191-exclude-from-optimizing-or-lazy-loading">', '</a>' ),
1332 'exclude_title_optimize' => __( 'Don\'t optimize images if', 'optimole-wp' ),
1333 'exclude_url_desc' => sprintf( __( '%1$sPage url%2$s contains', 'optimole-wp' ), '<strong>', '</strong>' ),
1334 'name' => sprintf( __( '%1$sName: %2$s', 'optimole-wp' ), '<strong>', '</strong>' ),
1335 'cropped' => __( 'cropped', 'optimole-wp' ),
1336 'exclude_url_match_desc' => sprintf( __( '%1$sPage url%2$s matches', 'optimole-wp' ), '<strong>', '</strong>' ),
1337 'exclude_first' => __( 'Exclude first', 'optimole-wp' ),
1338 'images' => __( 'images', 'optimole-wp' ),
1339 'exclude_first_images_title' => __( 'Bypass Lazy Load for First Images', 'optimole-wp' ),
1340 'exclude_first_images_desc' => sprintf( __( 'Indicate how many images at the top of each page should bypass lazy loading, ensuring they’re instantly visible. Enter 0 to not exclude any images from the lazy loading process. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1948-bypass-lazy-load-for-first-images">', '</a>' ),
1341 'filter_class' => __( 'Image class', 'optimole-wp' ),
1342 'filter_ext' => __( 'Image extension', 'optimole-wp' ),
1343 'filter_filename' => __( 'Image filename', 'optimole-wp' ),
1344 'filter_operator_contains' => __( 'contains', 'optimole-wp' ),
1345 'filter_operator_matches' => __( 'matches', 'optimole-wp' ),
1346 'filter_operator_is' => __( 'is', 'optimole-wp' ),
1347 'filter_url' => __( 'Page URL', 'optimole-wp' ),
1348 'filter_helper' => __( 'For homepage use `home` keyword.', 'optimole-wp' ),
1349 'gif_replacer_desc' => sprintf( __( 'Enable this to automatically convert GIF images to Video files (MP4 and WebM). %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1171-gif-to-video-conversion">', '</a>' ),
1350 'height_field' => __( 'Height', 'optimole-wp' ),
1351 'add_image_size_button' => __( 'Add size', 'optimole-wp' ),
1352 'add_image_size_desc' => __( 'Add New Image Crop Size', 'optimole-wp' ),
1353 'here' => __( ' here.', 'optimole-wp' ),
1354 'hide' => __( 'Hide', 'optimole-wp' ),
1355 'high_q_title' => __( 'High', 'optimole-wp' ),
1356 'image_1_label' => __( 'Original', 'optimole-wp' ),
1357 'image_2_label' => __( 'Optimized', 'optimole-wp' ),
1358 'lazyload_desc' => sprintf( __( 'Scales large images to fit their display space, ensuring your website runs fast. With lazy loading, images appear when needed while scrolling, making navigation smoother. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1939-scale-images-lazy-load">', '</a>' ),
1359 'filter_length_error' => __( 'The filter should be at least 3 characters long.', 'optimole-wp' ),
1360 'scale_desc' => sprintf( __( 'Enable this to allow Optimole to resize lazy-loaded images for optimal display on your screen. Keep it disabled to retain the original image size, though it may result in slower page loads. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1950-image-scaling">', '</a>' ),
1361 'low_q_title' => __( 'Low', 'optimole-wp' ),
1362 'medium_q_title' => __( 'Medium', 'optimole-wp' ),
1363 'no_images_found' => __( 'You dont have any images in your Media Library. Add one and check how the Optimole will perform.', 'optimole-wp' ),
1364 'native_desc' => sprintf( __( 'Enable to use the browser\'s built-in lazy loading feature. Enabling this will disable the auto scale feature, meaning images will not be automatically resized to fit the screen dimensions. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1949-browser-native-lazy-load">', '</a>' ),
1365 'option_saved' => __( 'Option saved.', 'optimole-wp' ),
1366 'ml_quality_desc' => sprintf( __( 'Optimole ML algorithms will predict the optimal image quality to get the smallest possible size with minimum perceived quality losses. When disabled, you can control the quality manually. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1016-what-is-the-difference-between-the-auto-high-medium-low-compression-levels">', '</a>' ),
1367 'quality_desc' => __( 'Lower image quality might boost your loading speed by lowering the size. However, the low image quality may negatively impact the visual appearance of the images. Try experimenting with the setting, then click the View sample image link to see what option works best for you.', 'optimole-wp' ),
1368 'quality_selected_value' => __( 'Selected value', 'optimole-wp' ),
1369 'quality_slider_desc' => __( 'See one sample image which will help you choose the right quality of the compression.', 'optimole-wp' ),
1370 'quality_title' => __( 'Auto Quality Powered by ML(Machine Learning)', 'optimole-wp' ),
1371 'strip_meta_title' => __( 'Strip Image Metadata', 'optimole-wp' ),
1372 'strip_meta_desc' => sprintf( __( 'Removes extra information from images, including EXIF and IPTC data (like camera settings and copyright info). This makes the pictures lighter and helps your website load faster. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1944-strip-image-metadata">', '</a>' ),
1373 'replacer_desc' => sprintf( __( 'When enabled, Optimole will manage, optimize, and serve all the images on your website. If disabled, optimization, lazy loading, and other features will no longer be available. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1218-how-to-turn-off-image-replacement-in-optimoles-wordpress-plugin">', '</a>' ),
1374 'sample_image_loading' => __( 'Loading a sample image. ', 'optimole-wp' ),
1375 'save_changes' => __( 'Save changes', 'optimole-wp' ),
1376 'show' => __( 'Show', 'optimole-wp' ),
1377 'selected_sites_title' => __( 'CURRENTLY SHOWING IMAGES FROM', 'optimole-wp' ),
1378 'selected_sites_desc' => __( 'Site: ', 'optimole-wp' ),
1379 'selected_all_sites_desc' => __( 'Currently viewing images from all sites ', 'optimole-wp' ),
1380 'select_all_sites_desc' => __( 'View images from all sites ', 'optimole-wp' ),
1381 'select_site' => __( 'Select a website', 'optimole-wp' ),
1382 'cloud_site_title' => __( 'Show images only from these sites: ', 'optimole-wp' ),
1383 'cloud_site_desc' => __( 'Browse images only from the specified websites. Otherwise, images from all websites will appear in the library.', 'optimole-wp' ),
1384 'toggle_ab_item' => __( 'Admin bar status', 'optimole-wp' ),
1385 'toggle_lazyload' => __( 'Scale Images & Lazy loading', 'optimole-wp' ),
1386 'toggle_scale' => __( 'Image Scaling', 'optimole-wp' ),
1387 'toggle_native' => __( 'Browser Native Lazy Load', 'optimole-wp' ),
1388 'on_toggle' => __( 'On', 'optimole-wp' ),
1389 'off_toggle' => __( 'Off', 'optimole-wp' ),
1390 'view_sample_image' => __( 'View sample image', 'optimole-wp' ),
1391 'watch_placeholder_lazyload' => __( 'Add each CSS selector on a new line or separated by comma(,)', 'optimole-wp' ),
1392 'watch_desc_lazyload' => sprintf( __( 'Enter CSS selectors for any background images not covered by the default lazy loading. This ensures those images also benefit from the optimized loading process. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1169-how-to-enable-the-background-lazyload-feature-for-certain-background-images">', '</a>' ),
1393 'watch_title_lazyload' => __( 'Extend CSS Background Lazy Loading', 'optimole-wp' ),
1394 'width_field' => __( 'Width', 'optimole-wp' ),
1395 'crop' => __( 'crop', 'optimole-wp' ),
1396 'toggle_cdn' => __( 'Serve CSS & JS Through Optimole', 'optimole-wp' ),
1397 'cdn_desc' => sprintf( __( 'When enabled, Optimole will optimize your CSS and JS files and, if they contain images, the images as well, then deliver them via the CDN for faster webpage loading. %1$sLearn more%2$s', 'optimole-wp' ), '<a class="inline-block text-purple-gray underline" target=”_blank” href="https://docs.optimole.com/article/1966-serve-css-js-through-optimole">', '</a>' ),
1398 'enable_css_minify_title' => __( 'Minify CSS files', 'optimole-wp' ),
1399 'css_minify_desc' => __( 'Once Optimole will serve your CSS files, it will also minify the files and serve them via CDN.', 'optimole-wp' ),
1400 'enable_js_minify_title' => __( 'Minify JS files', 'optimole-wp' ),
1401 'js_minify_desc' => __( 'Once Optimole will serve your JS files, it will also minify the files and serve them via CDN.', 'optimole-wp' ),
1402 'sync_title' => __( 'Offload Existing Images', 'optimole-wp' ),
1403 'rollback_title' => __( 'Restore Offloaded Images', 'optimole-wp' ),
1404 'sync_desc' => __( 'Right now all the new images uploaded to your site are moved automatically to Optimole Cloud. In order to offload the existing ones, please click Sync images and wait for the process to finish. You can rollback anytime.', 'optimole-wp' ),
1405 'rollback_desc' => __( 'Pull all the offloaded images to Optimole Cloud back to your server.', 'optimole-wp' ),
1406 'sync_media' => __( 'Sync images', 'optimole-wp' ),
1407 'rollback_media' => __( 'Rollback images', 'optimole-wp' ),
1408 'sync_media_progress' => __( 'Moving your images to Optimole...', 'optimole-wp' ),
1409 'estimated_time' => __( 'Estimated time remaining', 'optimole-wp' ),
1410 'calculating_estimated_time' => __( 'We are currently calculating the estimated time for this job...', 'optimole-wp' ),
1411 'images_processing' => __( 'We are currently processing your images in the background. Leaving the page won\'t stop the process.', 'optimole-wp' ),
1412 'active_optimize_exclusions' => __( 'Active Optimizing Exclusions', 'optimole-wp' ),
1413 'active_lazyload_exclusions' => __( 'Active Lazy-loading Exclusions', 'optimole-wp' ),
1414 'minutes' => __( 'minutes', 'optimole-wp' ),
1415 'stop' => __( 'Stop', 'optimole-wp' ),
1416 'show_logs' => __( 'Show Logs', 'optimole-wp' ),
1417 'hide_logs' => __( 'Hide Logs', 'optimole-wp' ),
1418 'view_logs' => __( 'View Full Logs', 'optimole-wp' ),
1419 'rollback_media_progress' => __( 'Moving images into your media library...', 'optimole-wp' ),
1420 'rollback_media_error' => __( 'An unexpected error occured while pulling the offloaded back to your site', 'optimole-wp' ),
1421 'rollback_media_error_desc' => __( 'You can try again to pull back the rest of the images.', 'optimole-wp' ),
1422 'remove_notice' => __( 'Remove notice', 'optimole-wp' ),
1423 'sync_media_error' => __( 'An unexpected error occured while offloading all existing images from your site to Optimole ', 'optimole-wp' ),
1424 'sync_media_link' => __( 'The selected images have been offloaded to our servers, you can check them', 'optimole-wp' ),
1425 'rollback_media_link' => __( 'The selected images have been restored to your server, you can check them', 'optimole-wp' ),
1426 'sync_media_error_desc' => __( 'You can try again to offload the rest of the images to Optimole.', 'optimole-wp' ),
1427 'offload_disable_warning_title' => __( 'Important! Please read carefully', 'optimole-wp' ),
1428 'offload_disable_warning_desc' => __( 'If you turn off this option, you will not be able to see the images in the Media Library without restoring the images first. Do you want to restore the images to your site upon turning off the option?', 'optimole-wp' ),
1429 'offload_enable_info_desc' => sprintf( __( 'You are not required to use the offload functionality for the plugin to work, use it if you want to save on hosting space. %1$s More details%2$s', 'optimole-wp' ), '<a style="white-space: nowrap;" target=”_blank” href="https://docs.optimole.com/article/1323-cloud-library-browsing">', '<span style="font-size:15px; margin-top:2px;" class="dashicons dashicons-external"></span></a>' ),
1430 'offload_conflicts_part_1' => __( 'We have detected the following plugins that conflict with the offload features: ', 'optimole-wp' ),
1431 'offload_conflicts_part_2' => __( 'Please disable those plugins temporarily in order for Optimole to rollback the images to your site.', 'optimole-wp' ),
1432 'select' => __( 'Please select one ...', 'optimole-wp' ),
1433 'yes' => __( 'Restore images after disabling', 'optimole-wp' ),
1434 'no' => __( 'Do not restore images after disabling', 'optimole-wp' ),
1435 'lazyload_placeholder_color' => __( 'Placeholder Color', 'optimole-wp' ),
1436 'clear' => __( 'Clear', 'optimole-wp' ),
1437 'settings_saved' => __( 'Settings saved', 'optimole-wp' ),
1438 'settings_saved_error' => __( 'Error saving settings. Please reload the page and try again.', 'optimole-wp' ),
1439 'cache_cleared' => __( 'Cache cleared', 'optimole-wp' ),
1440 'cache_cleared_error' => __( 'Error clearing cache. Please reload the page and try again.', 'optimole-wp' ),
1441 'offloading_start_title' => __( 'Transfer your images to Optimole', 'optimole-wp' ),
1442 'offloading_start_description' => __( 'This process will transfer and store your images in Optimole Cloud and may take a while, depending on the number of images.', 'optimole-wp' ),
1443 'offloading_start_action' => __( 'Transfer to Optimole Cloud', 'optimole-wp' ),
1444 'offloading_stop_title' => __( 'Are you sure?', 'optimole-wp' ),
1445 'offloading_stop_description' => __( 'This will halt the ongoing process. To retrieve images transferred from the Optimole Cloud, use the Rollback option.', 'optimole-wp' ),
1446 'offloading_stop_action' => __( 'Cancel the transfer to Optimole', 'optimole-wp' ),
1447 'rollback_start_title' => __( 'Transfer back all images to your site', 'optimole-wp' ),
1448 'rollback_start_description' => __( 'This process will transfer back all images from Optimole to your website and may take a while, depending on the number of images.', 'optimole-wp' ),
1449 'rollback_start_action' => __( 'Transfer back from Optimole', 'optimole-wp' ),
1450 'rollback_stop_title' => __( 'Are you sure?', 'optimole-wp' ),
1451 'rollback_stop_description' => __( 'Canceling will halt the ongoing process, and any remaining images will stay in the Optimole Cloud. To transfer images to the Optimole Cloud, use the Offloading option.', 'optimole-wp' ),
1452 'rollback_stop_action' => __( 'Cancel the transfer from Optimole', 'optimole-wp' ),
1453 ],
1454 'help' => [
1455 'section_one_title' => __( 'Help and Support', 'optimole-wp' ),
1456 'section_two_title' => __( 'Documentation', 'optimole-wp' ),
1457 'section_two_sub' => __( 'Docs Page', 'optimole-wp' ),
1458 'get_support_title' => __( 'Get Support', 'optimole-wp' ),
1459 'get_support_desc' => __( 'Need help or got a question? Submit a ticket and we\'ll get back to you.', 'optimole-wp' ),
1460 'get_support_cta' => __( 'Contact Support', 'optimole-wp' ),
1461 'feat_request_title' => __( 'Have a feature request?', 'optimole-wp' ),
1462 'feat_request_desc' => __( 'Help us improve Optimole by sharing feedback and ideas for new features.', 'optimole-wp' ),
1463 'feat_request_cta' => __( 'Submit a Feature Request', 'optimole-wp' ),
1464 'feedback_title' => __( 'Changelog', 'optimole-wp' ),
1465 'feedback_desc' => __( 'Check our changelog to see latest fixes and features implemented.', 'optimole-wp' ),
1466 'feedback_cta' => __( 'View Changelog', 'optimole-wp' ),
1467 'account_title' => __( 'Account', 'optimole-wp' ),
1468 'account_item_one' => __( 'How Optimole counts the visitors?', 'optimole-wp' ),
1469 'account_item_two' => __( 'What happens if I exceed plan limits?', 'optimole-wp' ),
1470 'account_item_three' => __( 'Visits based plan', 'optimole-wp' ),
1471 'image_processing_title' => __( 'Image Processing', 'optimole-wp' ),
1472 'image_processing_item_one' => __( 'Getting Started With Optimole', 'optimole-wp' ),
1473 'image_processing_item_two' => __( 'How Optimole can serve WebP images', 'optimole-wp' ),
1474 'image_processing_item_three' => __( 'Adding Watermarks to your images', 'optimole-wp' ),
1475 'api_title' => __( 'API', 'optimole-wp' ),
1476 'api_item_one' => __( 'Cloud Library Browsing', 'optimole-wp' ),
1477 'api_item_two' => __( 'Exclude from Optimizing or Lazy Loading', 'optimole-wp' ),
1478 'api_item_three' => __( 'Custom Integration', 'optimole-wp' ),
1479 ],
1480 'watermarks' => [
1481 'image' => __( 'Image', 'optimole-wp' ),
1482 'loading_remove_watermark' => __( 'Removing watermark resource ...', 'optimole-wp' ),
1483 'max_allowed' => __( 'You are allowed to save maximum 5 images.', 'optimole-wp' ),
1484 'list_header' => __( 'Possible watermarks', 'optimole-wp' ),
1485 'settings_header' => __( 'Watermarks position settings', 'optimole-wp' ),
1486 'no_images_found' => __( 'No images available for watermark. Please upload one.', 'optimole-wp' ),
1487 'id' => __( 'ID', 'optimole-wp' ),
1488 'name' => __( 'Name', 'optimole-wp' ),
1489 'type' => __( 'Type', 'optimole-wp' ),
1490 'action' => __( 'Action', 'optimole-wp' ),
1491 'upload' => __( 'Upload', 'optimole-wp' ),
1492 'add_desc' => __( 'Add new watermark', 'optimole-wp' ),
1493 'wm_title' => __( 'Active watermark', 'optimole-wp' ),
1494 'wm_desc' => __( 'The active watermark to use from the list of uploaded watermarks.', 'optimole-wp' ),
1495 'opacity_field' => __( 'Opacity', 'optimole-wp' ),
1496 'opacity_title' => __( 'Watermark opacity', 'optimole-wp' ),
1497 'opacity_desc' => __( 'A value between 0 and 100 for the opacity level. If set to 0 it will disable the watermark.', 'optimole-wp' ),
1498 'position_title' => __( 'Watermark position', 'optimole-wp' ),
1499 'position_desc' => __( 'The place relative to the image where the watermark should be placed.', 'optimole-wp' ),
1500 'pos_nowe_title' => __( 'North-West', 'optimole-wp' ),
1501 'pos_no_title' => __( 'North', 'optimole-wp' ),
1502 'pos_noea_title' => __( 'North-East', 'optimole-wp' ),
1503 'pos_we_title' => __( 'West', 'optimole-wp' ),
1504 'pos_ce_title' => __( 'Center', 'optimole-wp' ),
1505 'pos_ea_title' => __( 'East', 'optimole-wp' ),
1506 'pos_sowe_title' => __( 'South-West', 'optimole-wp' ),
1507 'pos_so_title' => __( 'South', 'optimole-wp' ),
1508 'pos_soea_title' => __( 'South-East', 'optimole-wp' ),
1509 'offset_x_field' => __( 'Offset X', 'optimole-wp' ),
1510 'offset_y_field' => __( 'Offset Y', 'optimole-wp' ),
1511 'offset_title' => __( 'Watermark offset', 'optimole-wp' ),
1512 'offset_desc' => __( 'Offset the watermark from set position on X and Y axis. Values can be positive or negative.', 'optimole-wp' ),
1513 'scale_field' => __( 'Scale', 'optimole-wp' ),
1514 'scale_title' => __( 'Watermark scale', 'optimole-wp' ),
1515 'scale_desc' => __( 'A value between 0 and 300 for the scale of the watermark (100 is the original size and 300 is 3x the size) relative to the resulting image size. If set to 0 it will default to the original size.', 'optimole-wp' ),
1516 'save_changes' => __( 'Save changes', 'optimole-wp' ),
1517 ],
1518 'latest_images' => [
1519 'image' => __( 'Image', 'optimole-wp' ),
1520 'no_images_found' => sprintf( __( 'We are currently optimizing your images. Meanwhile you can visit your %1$shomepage%2$s and check how our plugin performs. ', 'optimole-wp' ), '<a href="' . esc_url( home_url() ) . '" target="_blank" >', '</a>' ),
1521 'compression' => __( 'Optimization', 'optimole-wp' ),
1522 'loading_latest_images' => __( 'Loading your optimized images...', 'optimole-wp' ),
1523 'last' => __( 'Last', 'optimole-wp' ),
1524 'saved' => __( 'Saved', 'optimole-wp' ),
1525 'smaller' => __( 'smaller', 'optimole-wp' ),
1526 'optimized_images' => __( 'optimized images', 'optimole-wp' ),
1527 'same_size' => __( '🙉 We couldn\'t do better, this image is already optimized at maximum. ', 'optimole-wp' ),
1528 'small_optimization' => __( '😬 Not that much, just <strong>{ratio}</strong> smaller.', 'optimole-wp' ),
1529 'medium_optimization' => __( '🤓 We are on the right track, <strong>{ratio}</strong> squeezed.', 'optimole-wp' ),
1530 'big_optimization' => __( '❤️❤️❤️ Our moles just nailed it, this one is <strong>{ratio}</strong> smaller. ', 'optimole-wp' ),
1531 ],
1532 'csat' => [
1533 'title' => __( 'Your opinion matters', 'optimole-wp' ),
1534 'close' => __( 'Close', 'optimole-wp' ),
1535 'heading_one' => __( 'How easy did you find to get started using Optimole, on a scale of 1 to 5?', 'optimole-wp' ),
1536 'heading_two' => __( 'Any specific feedback you would like to add?', 'optimole-wp' ),
1537 'heading_three' => __( 'Thank you!', 'optimole-wp' ),
1538 'low' => __( 'Very Poor', 'optimole-wp' ),
1539 'high' => __( 'Excellent', 'optimole-wp' ),
1540 'feedback_placeholder' => __( 'Add your feedback here (optional)', 'optimole-wp' ),
1541 'skip' => __( 'Skip', 'optimole-wp' ),
1542 'submit' => __( 'Submit', 'optimole-wp' ),
1543 'thank_you' => __( 'Your input is highly appreciated and helps us shape a better experience in Optimole.', 'optimole-wp' ),
1544 ],
1545 ];
1546 }
1547
1548 /**
1549 * Allow SVG uploads
1550 *
1551 * @param array $mimes Supported mimes.
1552 *
1553 * @return array
1554 * @access public
1555 * @uses filter:upload_mimes
1556 */
1557 public function allow_meme_types( $mimes ) {
1558 $mimes['svg'] = 'image/svg+xml';
1559 return $mimes;
1560 }
1561 }
1562