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

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