PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.12.0
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.12.0
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.12.0, at inc/admin.php

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