PluginProbe
Sharing Image / 2.0.14
Sharing Image v2.0.14
3.10 trunk 2.0 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0 3.1 3.2 3.3 All 29 releases
sharing-image / classes / class-settings.php

class-settings.php in Sharing Image 2.0.14, at classes/class-settings.php

1,522 lines 36.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings page class
4 *
5 * @package sharing-image
6 * @author Anton Lukin
7 */
8
9 namespace Sharing_Image;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 die;
13 }
14
15 /**
16 * Settings page class
17 *
18 * @class Settings
19 */
20 class Settings {
21 /**
22 * Admin screen id.
23 *
24 * @var string
25 */
26 const SCREEN_ID = 'settings_page_sharing-image';
27
28 /**
29 * Sharing Image templates options name.
30 *
31 * @var string
32 */
33 const OPTION_TEMPLATES = 'sharing_image_templates';
34
35 /**
36 * Sharing Image config options name.
37 *
38 * @var string
39 */
40 const OPTION_CONFIG = 'sharing_image_config';
41
42 /**
43 * Sharing Image license options name.
44 *
45 * @var string
46 */
47 const OPTION_LICENSE = 'sharing_image_license';
48
49 /**
50 * Plugin admin menu slug.
51 *
52 * @var string
53 */
54 const SETTINGS_SLUG = 'sharing-image';
55
56 /**
57 * Remote licenses API url.
58 *
59 * @var string
60 */
61 const REMOTE_LICENSES = 'https://wpset.org/sharing-image/verify/';
62
63 /**
64 * Premium verification event name.
65 *
66 * @var string
67 */
68 const EVENT_PREMIUM = 'sharing_image_event_premium';
69
70 /**
71 * List of settings tabs.
72 *
73 * @var array
74 */
75 private $tabs = array();
76
77 /**
78 * Settings constructor.
79 */
80 public function __construct() {
81 $this->init_tabs();
82 }
83
84 /**
85 * Init class actions and filters.
86 */
87 public function init() {
88 add_action( 'admin_menu', array( $this, 'add_menu' ) );
89
90 // Handle settings POST requests.
91 add_action( 'admin_init', array( $this, 'handle_post_requests' ) );
92
93 // Handle settings AJAX requests.
94 add_action( 'admin_init', array( $this, 'handle_ajax_requests' ) );
95
96 // Allow uploading custom fonts for templates editor.
97 add_action( 'admin_init', array( $this, 'allow_custom_fonts' ) );
98
99 // Add settings link to plugins list.
100 add_filter( 'plugin_action_links', array( $this, 'add_settings_link' ), 10, 2 );
101
102 // Update admin title for different tabs.
103 add_action( 'admin_title', array( $this, 'update_settings_title' ) );
104
105 // Schedule Premium license verification.
106 add_action( self::EVENT_PREMIUM, array( $this, 'launch_verification_event' ), 10, 1 );
107 }
108
109 /**
110 * Add plugin settings page in WordPress menu.
111 */
112 public function add_menu() {
113 /**
114 * Easy way to hide settings page.
115 *
116 * @param bool $hide_settings Set true to hide settings page.
117 */
118 $hide_settings = apply_filters( 'sharing_image_hide_settings', false );
119
120 if ( $hide_settings ) {
121 return;
122 }
123
124 add_options_page(
125 esc_html__( 'Sharing Image settings', 'sharing-image' ),
126 esc_html__( 'Sharing Image', 'sharing-image' ),
127 'manage_options',
128 self::SETTINGS_SLUG,
129 array( $this, 'display_settings' )
130 );
131
132 // Add required assets and objects.
133 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
134 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
135 }
136
137 /**
138 * Handle settings POST requests.
139 */
140 public function handle_post_requests() {
141 $actions = array(
142 'config' => 'save_settings_config',
143 'editor' => 'save_settings_template',
144 'delete' => 'delete_settings_template',
145 );
146
147 foreach ( $actions as $key => $method ) {
148 $action = 'sharing_image_' . $key;
149
150 if ( method_exists( $this, $method ) ) {
151 add_action( 'admin_post_' . $action, array( $this, $method ) );
152 }
153 }
154 }
155
156 /**
157 * Handle settings AJAX requests.
158 */
159 public function handle_ajax_requests() {
160 $actions = array(
161 'show' => 'show_template_preview',
162 'save' => 'save_template_preview',
163 'verify' => 'verify_premium_key',
164 'revoke' => 'revoke_premium_access',
165 );
166
167 foreach ( $actions as $key => $method ) {
168 $action = 'sharing_image_' . $key;
169
170 if ( method_exists( $this, $method ) ) {
171 add_action( 'wp_ajax_' . $action, array( $this, $method ) );
172 }
173 }
174 }
175
176 /**
177 * Allow uploading custom fonts for templates editor.
178 * This function may affect the security of the site.
179 * Disable font uploading if you are not going to use it.
180 */
181 public function allow_custom_fonts() {
182 /**
183 * Easy way to disable custom font uploading.
184 *
185 * @param bool $disable_fonts Set true to disable fonts uploading.
186 */
187 $disable_fonts = apply_filters( 'sharing_image_disable_custom_fonts', false );
188
189 if ( $disable_fonts ) {
190 return;
191 }
192
193 // Allow True Type fonts uploading.
194 add_filter( 'wp_check_filetype_and_ext', array( $this, 'fix_ttf_mime_type' ), 10, 3 );
195
196 // Add new .ttf font mime type.
197 add_filter( 'upload_mimes', array( $this, 'add_ttf_mime_type' ) );
198 }
199
200 /**
201 * Add settings link to plugins list.
202 *
203 * @param array $actions An array of plugin action links.
204 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
205
206 * @return array Array of settings actions.
207 */
208 public function add_settings_link( $actions, $plugin_file ) {
209 $actions = (array) $actions;
210
211 if ( plugin_basename( SHARING_IMAGE_FILE ) === $plugin_file ) {
212 $actions[] = sprintf(
213 '<a href="%s">%s</a>',
214 admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ),
215 __( 'Settings', 'sharing-image' )
216 );
217 }
218
219 return $actions;
220 }
221
222 /**
223 * Save settings config fields.
224 */
225 public function save_settings_config() {
226 check_admin_referer( basename( __FILE__ ), 'sharing_image_nonce' );
227
228 if ( ! current_user_can( 'manage_options' ) ) {
229 wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'sharing-image' ) );
230 }
231
232 $return = $this->get_tab_link( 'config' );
233
234 if ( null === $return ) {
235 $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG );
236 }
237
238 if ( ! isset( $_POST['sharing_image_config'] ) ) {
239 $this->redirect_with_message( $return, 5 );
240 }
241
242 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
243 $config = $this->sanitize_config( wp_unslash( $_POST['sharing_image_config'] ) );
244
245 $this->update_config( $config );
246
247 // Redirect with success message.
248 $this->redirect_with_message( $return, 1 );
249 }
250
251 /**
252 * Save template editor fields.
253 */
254 public function save_settings_template() {
255 check_admin_referer( basename( __FILE__ ), 'sharing_image_nonce' );
256
257 if ( ! current_user_can( 'manage_options' ) ) {
258 wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'sharing-image' ) );
259 }
260
261 $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG );
262
263 if ( ! isset( $_POST['sharing_image_index'] ) ) {
264 $this->redirect_with_message( $return, 2 );
265 }
266
267 $index = absint( wp_unslash( $_POST['sharing_image_index'] ) );
268
269 if ( ! isset( $_POST['sharing_image_editor'] ) ) {
270 $this->redirect_with_message( $return, 2 );
271 }
272
273 // Skip 2nd+ templates if the Premium is not active.
274 if ( $index > 0 && ! $this->is_premium_features() ) {
275 $this->redirect_with_message( $return, 2 );
276 }
277
278 $return = add_query_arg( array( 'template' => $index + 1 ), $return );
279
280 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
281 $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) );
282
283 $this->update_templates( $index, $editor );
284
285 // Redirect with success message.
286 $this->redirect_with_message( $return, 1 );
287 }
288
289 /**
290 * Action to delete template from editor page.
291 */
292 public function delete_settings_template() {
293 check_admin_referer( basename( __FILE__ ), 'nonce' );
294
295 if ( ! current_user_can( 'manage_options' ) ) {
296 return;
297 }
298
299 $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG );
300
301 if ( ! isset( $_REQUEST['template'] ) ) {
302 $this->redirect_with_message( $return, 4 );
303 }
304
305 // Get index from template ID.
306 $index = absint( $_REQUEST['template'] ) - 1;
307
308 if ( ! $this->update_templates( $index ) ) {
309 $this->redirect_with_message( $return, 4 );
310 }
311
312 $this->redirect_with_message( $return, 3 );
313 }
314
315 /**
316 * Show generated template from AJAX request.
317 */
318 public function show_template_preview() {
319 $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false );
320
321 if ( false === $check ) {
322 wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 );
323 }
324
325 if ( ! isset( $_POST['sharing_image_index'] ) ) {
326 wp_send_json_error( __( 'Poster index undefined.', 'sharing-image' ), 400 );
327 }
328
329 $index = absint( wp_unslash( $_POST['sharing_image_index'] ) );
330
331 if ( ! isset( $_POST['sharing_image_editor'] ) ) {
332 wp_send_json_error( __( 'Editor settings are not set.', 'sharing-image' ), 400 );
333 }
334
335 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
336 $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) );
337
338 // Show poster using generator class.
339 $source = ( new Generator() )->show( $editor, $index );
340
341 if ( is_wp_error( $source ) ) {
342 wp_send_json_error( $source->get_error_message(), 400 );
343 }
344 }
345
346 /**
347 * Show generated template from AJAX request.
348 */
349 public function save_template_preview() {
350 $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false );
351
352 if ( false === $check ) {
353 wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 );
354 }
355
356 if ( ! isset( $_POST['sharing_image_index'] ) ) {
357 wp_send_json_error( __( 'Poster index undefined.', 'sharing-image' ), 400 );
358 }
359
360 $index = absint( wp_unslash( $_POST['sharing_image_index'] ) );
361
362 if ( ! isset( $_POST['sharing_image_editor'] ) ) {
363 wp_send_json_error( __( 'Editor settings are not set.', 'sharing-image' ), 400 );
364 }
365
366 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
367 $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) );
368
369 // Save poster using generator class.
370 $source = ( new Generator() )->save( $editor, $index );
371
372 if ( is_wp_error( $source ) ) {
373 wp_send_json_error( $source->get_error_message(), 400 );
374 }
375
376 wp_send_json_success( $source );
377 }
378
379 /**
380 * Verify Premium key from AJAX request.
381 */
382 public function verify_premium_key() {
383 $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false );
384
385 if ( false === $check ) {
386 wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 );
387 }
388
389 if ( empty( $_POST['sharing_image_key'] ) ) {
390 wp_send_json_error( __( 'Premium key undefined.', 'sharing-image' ), 400 );
391 }
392
393 $key = sanitize_text_field( wp_unslash( $_POST['sharing_image_key'] ) );
394
395 $args = array(
396 'body' => array(
397 'key' => $key,
398 'domain' => wp_parse_url( site_url(), PHP_URL_HOST ),
399 ),
400 );
401
402 $response = wp_remote_post( self::REMOTE_LICENSES, $args );
403
404 if ( is_wp_error( $response ) ) {
405 wp_send_json_error( __( 'Remote request error: ', 'sharing-image' ) . $response->get_error_message(), 400 );
406 }
407
408 $answer = json_decode( $response['body'], true );
409
410 if ( ! isset( $answer['success'] ) ) {
411 wp_send_json_error( __( 'Invalid response received from the verification server.', 'sharing-image' ), 400 );
412 }
413
414 // Remove license verification event.
415 wp_unschedule_hook( self::EVENT_PREMIUM );
416
417 if ( true === $answer['success'] ) {
418 $license = $this->update_license( true, $key );
419
420 // Schedule license verification twice daily.
421 $this->schedule_verification( array( $key ) );
422
423 wp_send_json_success( $license );
424 }
425
426 $error = array(
427 'success' => false,
428 'data' => __( 'Verification failed.', 'sharing-image' ),
429 );
430
431 if ( isset( $answer['result'] ) ) {
432 $error['code'] = $answer['result'];
433 }
434
435 $this->update_license( false, $key );
436
437 wp_send_json( $error, 403 );
438 }
439
440 /**
441 * Revoke Premium access from AJAX request.
442 */
443 public function revoke_premium_access() {
444 $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false );
445
446 if ( false === $check ) {
447 wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 );
448 }
449
450 // Remove license verification event.
451 wp_unschedule_hook( self::EVENT_PREMIUM );
452
453 // Disable Premium license.
454 $license = $this->update_license( false );
455
456 wp_send_json_success( $license );
457 }
458
459 /**
460 * Show plugin settings.
461 */
462 public function display_settings() {
463 if ( ! $this->is_settings_screen() ) {
464 return;
465 }
466
467 include_once SHARING_IMAGE_DIR . 'templates/settings.php';
468
469 /**
470 * Fires on settings template including.
471 */
472 do_action( 'sharing_image_settings' );
473 }
474
475 /**
476 * Fix .ttf files mime.
477 *
478 * @param array $types Values for the extension, mime type, and corrected filename.
479 * @param string $file Full path to the file.
480 * @param string $filename The name of the file (may differ from $file due to.
481 *
482 * @return array List of file types.
483 */
484 public function fix_ttf_mime_type( $types, $file, $filename ) {
485 $extension = pathinfo( $filename, PATHINFO_EXTENSION );
486
487 if ( 'ttf' === $extension ) {
488 $types['ext'] = false;
489
490 if ( current_user_can( 'manage_options' ) ) {
491 $types['ext'] = 'ttf';
492 $types['type'] = 'application/x-font-ttf';
493 }
494 }
495
496 return $types;
497 }
498
499 /**
500 * Add new .ttf font mime type.
501 *
502 * @param array $types Allowed file types to upload.
503 *
504 * @return array Allowed file types to upload.
505 */
506 public function add_ttf_mime_type( $types ) {
507 $types['ttf'] = 'application/x-font-ttf';
508
509 return $types;
510 }
511
512 /**
513 * Enqueue settings styles.
514 */
515 public function enqueue_styles() {
516 if ( ! $this->is_settings_screen() ) {
517 return;
518 }
519
520 wp_enqueue_style(
521 'sharing-image-settings',
522 SHARING_IMAGE_URL . 'assets/styles/settings.css',
523 array(),
524 SHARING_IMAGE_VERSION,
525 'all'
526 );
527 }
528
529 /**
530 * Enqueue settings scripts.
531 */
532 public function enqueue_scripts() {
533 if ( ! $this->is_settings_screen() ) {
534 return;
535 }
536
537 wp_enqueue_script(
538 'sharing-image-settings',
539 SHARING_IMAGE_URL . 'assets/scripts/settings.js',
540 array( 'wp-i18n', 'wp-polyfill-url', 'wp-polyfill-formdata' ),
541 SHARING_IMAGE_VERSION,
542 true
543 );
544
545 wp_enqueue_media();
546
547 // Translations availible only for WP 5.0+.
548 wp_set_script_translations( 'sharing-image-settings', 'sharing-image' );
549
550 $object = $this->create_script_object();
551
552 // Add settings script object.
553 wp_localize_script( 'sharing-image-settings', 'sharingImageSettings', $object );
554 }
555
556 /**
557 * Get templates list from options.
558 *
559 * @return array List of templates.
560 */
561 public function get_templates() {
562 $templates = get_option( self::OPTION_TEMPLATES, array() );
563
564 if ( ! $this->is_premium_features() ) {
565 $templates = array_slice( $templates, 0, 1 );
566 }
567
568 /**
569 * Filters list of templates.
570 *
571 * @param array $templates List of templates.
572 */
573 return apply_filters( 'sharing_image_get_templates', $templates );
574 }
575
576 /**
577 * Update templates using index.
578 *
579 * @param int $index Template index to update.
580 * @param array $editor New template data.
581 */
582 public function update_templates( $index, $editor = null ) {
583 // Method get_templates() is not used to save old templates during Premium switching.
584 $templates = get_option( self::OPTION_TEMPLATES, array() );
585
586 $templates[ $index ] = $editor;
587
588 if ( null === $editor ) {
589 unset( $templates[ $index ] );
590 }
591
592 /**
593 * Filters list of templates before update in database.
594 *
595 * @param array $templates List of reindexed templates.
596 */
597 $templates = apply_filters( 'sharing_image_update_templates', array_values( $templates ) );
598
599 return update_option( self::OPTION_TEMPLATES, $templates );
600 }
601
602 /**
603 * Get plugin config settings.
604 *
605 * @return array List of plugin config settings.
606 */
607 public function get_config() {
608 $config = get_option( self::OPTION_CONFIG, array() );
609
610 /**
611 * Filters settigns config.
612 *
613 * @param array List of plugin config settings.
614 */
615 return apply_filters( 'sharing_image_get_config', $config );
616 }
617
618 /**
619 * Update config settings.
620 *
621 * @param array $config License settings config data.
622 */
623 public function update_config( $config ) {
624 /**
625 * Filters config options before their update in database.
626 *
627 * @param array $config Settings config data.
628 */
629 $config = apply_filters( 'sharing_image_update_config', $config );
630
631 update_option( self::OPTION_CONFIG, $config );
632 }
633
634 /**
635 * Get plugin license settings.
636 *
637 * @return array List of plugin license settings.
638 */
639 public function get_license() {
640 $license = get_option( self::OPTION_LICENSE, array() );
641
642 /**
643 * Check if the plugin in development mode.
644 *
645 * @param bool Current development state. Disabled by default.
646 */
647 $develop = apply_filters( 'sharing_image_develop', false );
648
649 if ( $develop ) {
650 $license['develop'] = true;
651 }
652
653 /**
654 * Filters license settings.
655 *
656 * @param array List of plugin license settings.
657 */
658 return apply_filters( 'sharing_image_get_license', $license );
659 }
660
661 /**
662 * Set license options.
663 *
664 * @param bool $premium Premium status.
665 * @param string $key License key.
666 * @param string $error Verification error code.
667
668 * @return array License options.
669 */
670 public function update_license( $premium, $key = '', $error = '' ) {
671 $license = get_option( self::OPTION_LICENSE, array() );
672
673 $license['premium'] = $premium;
674
675 if ( ! empty( $key ) ) {
676 $license['key'] = $key;
677 }
678
679 unset( $license['error'] );
680
681 if ( ! empty( $error ) ) {
682 $license['error'] = $error;
683 }
684
685 // Save updated license settings in database.
686 update_option( self::OPTION_LICENSE, $license );
687
688 return $license;
689 }
690
691 /**
692 * Get directory to uploaded posters.
693 *
694 * @return array Path and url to upload directory.
695 */
696 public function get_upload_dir() {
697 $config = $this->get_config();
698
699 if ( ! isset( $config['uploads'] ) ) {
700 $config['uploads'] = 'default';
701 }
702
703 // Create custom upload directory.
704 if ( isset( $config['storage'] ) && 'custom' === $config['uploads'] ) {
705 return $this->create_upload_dir( $config['storage'] );
706 }
707
708 $uploads = wp_upload_dir();
709
710 /**
711 * Filters upload directory.
712 *
713 * @param array $dir Path and url to upload directory.
714 */
715 return apply_filters( 'sharing_image_upload_dir', array( $uploads['path'], $uploads['url'] ) );
716 }
717
718 /**
719 * Get generated image file format.
720 *
721 * @param string $format Optional. Default image format.
722
723 * @return string Image file format.
724 */
725 public function get_file_format( $format = 'jpg' ) {
726 $config = $this->get_config();
727
728 if ( isset( $config['format'] ) ) {
729 $format = $config['format'];
730 }
731
732 /**
733 * Filters Image file format.
734 *
735 * @param string $format Image file format.
736 */
737 return apply_filters( 'sharing_image_file_format', $format );
738 }
739
740 /**
741 * Get quality of generated poster.
742 *
743 * @param int $quality Optional. Default image quality.
744 *
745 * @return int Image quality.
746 */
747 public function get_quality( $quality = 90 ) {
748 $config = $this->get_config();
749
750 if ( isset( $config['quality'] ) ) {
751 $quality = $config['quality'];
752 }
753
754 /**
755 * Filters poster image quality.
756 *
757 * @param string $quality Image quality.
758 */
759 return apply_filters( 'sharing_image_poster_quality', $quality );
760 }
761
762 /**
763 * Try to get default poster image data.
764 * Returns array with image url, width and height.
765 *
766 * @see https://developer.wordpress.org/reference/functions/wp_get_attachment_image_src/
767 *
768 * @return array|false Array of image data, or boolean false if no image is available.
769 */
770 public function get_default_poster_src() {
771 $config = $this->get_config();
772
773 if ( empty( $config['default'] ) ) {
774 return false;
775 }
776
777 $poster = wp_get_attachment_image_src( $config['default'], 'full' );
778
779 if ( is_array( $poster ) ) {
780 $poster = array_slice( $poster, 0, 3 );
781 }
782
783 /**
784 * Filters default poster data.
785 *
786 * @param array|false Array of image data, or boolean false if no image is available.
787 */
788 return apply_filters( 'sharing_image_default_poster_src', $poster );
789 }
790
791 /**
792 * Update settings page title.
793 *
794 * @param string $title Plugin settings page title.
795 *
796 * @return string Plugin settings title
797 */
798 public function update_settings_title( $title ) {
799 if ( ! $this->is_settings_screen() ) {
800 return $title;
801 }
802
803 $tab = $this->get_current_tab();
804
805 if ( null === $tab ) {
806 return $title;
807 }
808
809 if ( empty( $this->tabs[ $tab ]['label'] ) ) {
810 return $title;
811 }
812
813 $label = esc_html( $this->tabs[ $tab ]['label'] );
814
815 return sprintf( '%s &ndash; %s', $label, $title );
816 }
817
818 /**
819 * Launch scheduled license verification event.
820 * Do not disable Premium if the verification server does not respond.
821 *
822 * @param string $key License key.
823 */
824 public function launch_verification_event( $key ) {
825 $args = array(
826 'body' => array(
827 'key' => $key,
828 'domain' => wp_parse_url( site_url(), PHP_URL_HOST ),
829 ),
830 );
831
832 $response = wp_remote_post( self::REMOTE_LICENSES, $args );
833
834 if ( is_wp_error( $response ) ) {
835 return;
836 }
837
838 $answer = json_decode( $response['body'], true );
839
840 if ( ! isset( $answer['success'] ) ) {
841 return;
842 }
843
844 if ( true === $answer['success'] ) {
845 return $this->update_license( true, $key );
846 }
847
848 if ( ! isset( $answer['result'] ) ) {
849 return $this->update_license( false, $key );
850 }
851
852 $this->update_license( false, $key, $answer['result'] );
853 }
854
855 /**
856 * Check if Premium features availible.
857 *
858 * @return bool Whether premium featured enabled.
859 */
860 public function is_premium_features() {
861 $license = $this->get_license();
862
863 if ( ! empty( $license['premium'] ) || ! empty( $license['develop'] ) ) {
864 return true;
865 }
866
867 return false;
868 }
869
870 /**
871 * Schedule license verification.
872 *
873 * @param array $args List of event arguments. License key by default.
874 */
875 public function schedule_verification( $args = array() ) {
876 if ( wp_next_scheduled( self::EVENT_PREMIUM, $args ) ) {
877 return;
878 }
879
880 wp_schedule_event( time() + DAY_IN_SECONDS / 2, 'twicedaily', self::EVENT_PREMIUM, $args );
881 }
882
883 /**
884 * Create script object to inject with settings.
885 *
886 * @return array Filtered script settings object.
887 */
888 private function create_script_object() {
889 $uploads = wp_get_upload_dir();
890
891 // Get uploads directory path from WordPress root.
892 $basedir = str_replace( ABSPATH, '', $uploads['basedir'] );
893
894 $object = array(
895 'nonce' => wp_create_nonce( basename( __FILE__ ) ),
896 'links' => array(
897 'uploads' => esc_url( admin_url( 'upload.php' ) ),
898 'action' => esc_url( admin_url( 'admin-post.php' ) ),
899 'premium' => esc_url_raw( $this->get_tab_link( 'premium' ) ),
900 'storage' => path_join( $basedir, 'sharing-image' ),
901 ),
902 'templates' => $this->get_templates(),
903 'config' => $this->get_config(),
904 'license' => $this->get_license(),
905 'fonts' => $this->get_fonts(),
906 );
907
908 /**
909 * Filters settings script object.
910 *
911 * @param array $object Array of settings script object.
912 */
913 return apply_filters( 'sharing_image_settings_object', $object );
914 }
915
916 /**
917 * Sanitize editor template settings.
918 *
919 * @param array $editor Template editor settings.
920 *
921 * @return array
922 */
923 private function sanitize_editor( $editor ) {
924 $sanitized = array();
925
926 if ( ! empty( $editor['preview'] ) ) {
927 $sanitized['preview'] = sanitize_text_field( $editor['preview'] );
928 }
929
930 if ( ! empty( $editor['title'] ) ) {
931 $sanitized['title'] = sanitize_text_field( $editor['title'] );
932 }
933
934 if ( ! empty( $editor['attachment'] ) ) {
935 $sanitized['attachment'] = absint( $editor['attachment'] );
936 }
937
938 if ( ! empty( $editor['suspend'] ) ) {
939 $sanitized['suspend'] = 'suspend';
940 }
941
942 $sanitized['fill'] = '#000000';
943
944 if ( ! empty( $editor['fill'] ) ) {
945 $sanitized['fill'] = sanitize_hex_color( $editor['fill'] );
946 }
947
948 $sanitized['background'] = 'blank';
949
950 if ( isset( $editor['background'] ) ) {
951 $background = array( 'dynamic', 'blank', 'permanent' );
952
953 // Set default background for permanent option without attachment.
954 if ( empty( $sanitized['attachment'] ) ) {
955 $background = array_diff( $background, array( 'permanent' ) );
956 }
957
958 if ( in_array( $editor['background'], $background, true ) ) {
959 $sanitized['background'] = $editor['background'];
960 }
961 }
962
963 $sanitized['width'] = 1200;
964
965 if ( ! empty( $editor['width'] ) ) {
966 $sanitized['width'] = absint( $editor['width'] );
967 }
968
969 $sanitized['height'] = 630;
970
971 if ( ! empty( $editor['height'] ) ) {
972 $sanitized['height'] = absint( $editor['height'] );
973 }
974
975 if ( isset( $editor['layers'] ) && is_array( $editor['layers'] ) ) {
976 $layers = array();
977
978 foreach ( $editor['layers'] as $layer ) {
979 if ( empty( $layer['type'] ) ) {
980 continue;
981 }
982
983 switch ( $layer['type'] ) {
984 case 'text':
985 $layers[] = $this->sanitize_text_layer( $layer );
986 break;
987
988 case 'image':
989 $layers[] = $this->sanitize_image_layer( $layer );
990 break;
991
992 case 'filter':
993 $layers[] = $this->sanitize_filter_layer( $layer );
994 break;
995
996 case 'rectangle':
997 $layers[] = $this->sanitize_rectangle_layer( $layer );
998 break;
999 }
1000 }
1001
1002 $sanitized['layers'] = $layers;
1003 }
1004
1005 /**
1006 * Filters template editor sanitized fields.
1007 *
1008 * @param array $sanitized List of sanitized editor fields.
1009 * @param array $editor List of editor fields before sanitization.
1010 */
1011 return apply_filters( 'sharing_image_sanitize_editor', $sanitized, $editor );
1012 }
1013
1014 /**
1015 * Sanitize template editor text layer.
1016 *
1017 * @param array $layer Layer settings.
1018 *
1019 * @return array Sanitized layer settings.
1020 */
1021 private function sanitize_text_layer( $layer ) {
1022 $sanitized = array();
1023
1024 // No need to sanitize after switch.
1025 $sanitized['type'] = $layer['type'];
1026
1027 if ( ! empty( $layer['dynamic'] ) ) {
1028 $sanitized['dynamic'] = 'dynamic';
1029 }
1030
1031 if ( isset( $layer['title'] ) ) {
1032 $sanitized['title'] = sanitize_text_field( $layer['title'] );
1033 }
1034
1035 if ( isset( $layer['content'] ) ) {
1036 $sanitized['content'] = sanitize_textarea_field( $layer['content'] );
1037 }
1038
1039 if ( isset( $layer['sample'] ) ) {
1040 $sanitized['sample'] = sanitize_textarea_field( $layer['sample'] );
1041 }
1042
1043 $sanitized['preset'] = 'none';
1044
1045 if ( isset( $layer['preset'] ) ) {
1046 $preset = array( 'title', 'excerpt' );
1047
1048 if ( in_array( $layer['preset'], $preset, true ) ) {
1049 $sanitized['preset'] = $layer['preset'];
1050 }
1051 }
1052
1053 $sanitized['color'] = '#ffffff';
1054
1055 if ( ! empty( $layer['color'] ) ) {
1056 $sanitized['color'] = sanitize_hex_color( $layer['color'] );
1057 }
1058
1059 $sanitized['horizontal'] = 'left';
1060
1061 if ( isset( $layer['horizontal'] ) ) {
1062 $horizontal = array( 'center', 'right' );
1063
1064 if ( in_array( $layer['horizontal'], $horizontal, true ) ) {
1065 $sanitized['horizontal'] = $layer['horizontal'];
1066 }
1067 }
1068
1069 $sanitized['vertical'] = 'top';
1070
1071 if ( isset( $layer['vertical'] ) ) {
1072 $vertical = array( 'center', 'bottom' );
1073
1074 if ( in_array( $layer['vertical'], $vertical, true ) ) {
1075 $sanitized['vertical'] = $layer['vertical'];
1076 }
1077 }
1078
1079 if ( isset( $layer['fontsize'] ) ) {
1080 $sanitized['fontsize'] = absint( $layer['fontsize'] );
1081 }
1082
1083 if ( isset( $layer['lineheight'] ) ) {
1084 $sanitized['lineheight'] = (float) $layer['lineheight'];
1085 }
1086
1087 if ( isset( $layer['fontname'] ) ) {
1088 $sanitized['fontname'] = sanitize_text_field( $layer['fontname'] );
1089 }
1090
1091 if ( ! empty( $layer['fontfile'] ) ) {
1092 $sanitized['fontfile'] = absint( $layer['fontfile'] );
1093 }
1094
1095 $sizes = array( 'x', 'y', 'width', 'height' );
1096
1097 foreach ( $sizes as $size ) {
1098 if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) {
1099 continue;
1100 }
1101
1102 $sanitized[ $size ] = absint( $layer[ $size ] );
1103 }
1104
1105 return $sanitized;
1106 }
1107
1108 /**
1109 * Sanitize template editor image layer.
1110 *
1111 * @param array $layer Layer settings.
1112 *
1113 * @return array Sanitized image layer settings.
1114 */
1115 private function sanitize_image_layer( $layer ) {
1116 $sanitized = array();
1117
1118 // No need to sanitize after switch.
1119 $sanitized['type'] = $layer['type'];
1120
1121 if ( ! empty( $layer['attachment'] ) ) {
1122 $sanitized['attachment'] = absint( $layer['attachment'] );
1123 }
1124
1125 $sizes = array( 'x', 'y', 'width', 'height' );
1126
1127 foreach ( $sizes as $size ) {
1128 if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) {
1129 continue;
1130 }
1131
1132 $sanitized[ $size ] = absint( $layer[ $size ] );
1133 }
1134
1135 return $sanitized;
1136 }
1137
1138 /**
1139 * Sanitize template editor filter layer.
1140 *
1141 * @param array $layer Layer settings.
1142 *
1143 * @return array Sanitized filter layer settings.
1144 */
1145 private function sanitize_filter_layer( $layer ) {
1146 $sanitized = array();
1147
1148 // No need to sanitize after switch.
1149 $sanitized['type'] = $layer['type'];
1150
1151 if ( ! empty( $layer['grayscale'] ) ) {
1152 $sanitized['grayscale'] = 'grayscale';
1153 }
1154
1155 if ( ! empty( $layer['blur'] ) ) {
1156 $sanitized['blur'] = 'blur';
1157 }
1158
1159 $sanitized['brightness'] = 0;
1160
1161 if ( isset( $layer['brightness'] ) ) {
1162 $brightness = (int) $layer['brightness'];
1163
1164 if ( $brightness >= -100 && $brightness <= 100 ) {
1165 $sanitized['brightness'] = $brightness;
1166 }
1167 }
1168
1169 $sanitized['contrast'] = 0;
1170
1171 if ( isset( $layer['contrast'] ) ) {
1172 $contrast = (int) $layer['contrast'];
1173
1174 if ( $contrast >= -100 && $contrast <= 100 ) {
1175 $sanitized['contrast'] = $contrast;
1176 }
1177 }
1178
1179 $sanitized['blackout'] = 0;
1180
1181 if ( isset( $layer['blackout'] ) ) {
1182 $blackout = (int) $layer['blackout'];
1183
1184 if ( $blackout >= 0 && $blackout <= 100 ) {
1185 $sanitized['blackout'] = $blackout;
1186 }
1187 }
1188
1189 return $sanitized;
1190 }
1191
1192 /**
1193 * Sanitize template editor rectagle layer.
1194 *
1195 * @param array $layer Layer settings.
1196 *
1197 * @return array Sanitized rectangle layer settings.
1198 */
1199 private function sanitize_rectangle_layer( $layer ) {
1200 $sanitized = array();
1201
1202 // No need to sanitize after switch.
1203 $sanitized['type'] = $layer['type'];
1204
1205 if ( ! empty( $layer['outline'] ) ) {
1206 $sanitized['outline'] = 'outline';
1207 }
1208
1209 $sanitized['color'] = '#ffffff';
1210
1211 if ( ! empty( $layer['color'] ) ) {
1212 $sanitized['color'] = sanitize_hex_color( $layer['color'] );
1213 }
1214
1215 $sanitized['opacity'] = 0;
1216
1217 if ( isset( $layer['opacity'] ) ) {
1218 $opacity = (float) $layer['opacity'];
1219
1220 if ( $opacity >= 0 && $opacity <= 100 ) {
1221 $sanitized['opacity'] = $opacity;
1222 }
1223 }
1224
1225 $sanitized['thickness'] = 0;
1226
1227 if ( isset( $layer['thickness'] ) ) {
1228 $thickness = (int) $layer['thickness'];
1229
1230 if ( $thickness >= 0 && $thickness <= 50 ) {
1231 $sanitized['thickness'] = $thickness;
1232 }
1233 }
1234
1235 $sizes = array( 'x', 'y', 'width', 'height' );
1236
1237 foreach ( $sizes as $size ) {
1238 if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) {
1239 continue;
1240 }
1241
1242 $sanitized[ $size ] = absint( $layer[ $size ] );
1243 }
1244
1245 return $sanitized;
1246 }
1247
1248 /**
1249 * Sanitize config settings.
1250 *
1251 * @param array $config Config settings.
1252 *
1253 * @return array Sanitized config settings.
1254 */
1255 private function sanitize_config( $config ) {
1256 $sanitized = array();
1257
1258 if ( ! empty( $config['default'] ) ) {
1259 $sanitized['default'] = absint( $config['default'] );
1260 }
1261
1262 $sanitized['format'] = 'jpg';
1263
1264 if ( isset( $config['format'] ) ) {
1265 $format = $config['format'];
1266
1267 if ( in_array( $format, array( 'jpg', 'png' ), true ) ) {
1268 $sanitized['format'] = $config['format'];
1269 }
1270 }
1271
1272 if ( isset( $config['quality'] ) ) {
1273 $quality = (int) $config['quality'];
1274
1275 if ( $quality >= 1 && $quality <= 100 ) {
1276 $sanitized['quality'] = $quality;
1277 }
1278 }
1279
1280 $sanitized['uploads'] = 'default';
1281
1282 if ( isset( $config['uploads'] ) ) {
1283 $uploads = $config['uploads'];
1284
1285 if ( in_array( $uploads, array( 'custom', 'default' ), true ) ) {
1286 $sanitized['uploads'] = $config['uploads'];
1287 }
1288 }
1289
1290 if ( isset( $config['storage'] ) ) {
1291 $sanitized['storage'] = sanitize_text_field( $config['storage'] );
1292 }
1293
1294 $sanitized['autogenerate'] = 'manual';
1295
1296 if ( isset( $config['autogenerate'] ) && is_numeric( $config['autogenerate'] ) ) {
1297 $autogenerate = absint( $config['autogenerate'] );
1298
1299 if ( count( $this->get_templates() ) > $autogenerate ) {
1300 $sanitized['autogenerate'] = $autogenerate;
1301 }
1302 }
1303
1304 /**
1305 * Filters template editor sanitized fields.
1306 *
1307 * @param array $sanitized List of sanitized config fields.
1308 * @param array $config List of config fields before sanitization.
1309 */
1310 return apply_filters( 'sharing_image_sanitize_config', $sanitized, $config );
1311 }
1312
1313 /**
1314 * Show settings tab template.
1315 */
1316 private function show_settings_section() {
1317 $tab = $this->get_current_tab();
1318
1319 if ( null === $tab ) {
1320 return;
1321 }
1322
1323 include_once SHARING_IMAGE_DIR . "templates/{$tab}.php";
1324 }
1325
1326 /**
1327 * Show settings messages and errors after post actions.
1328 */
1329 private function show_settings_message() {
1330 // phpcs:ignore WordPress.Security.NonceVerification
1331 $message = isset( $_GET['message'] ) ? absint( $_GET['message'] ) : 0;
1332
1333 switch ( $message ) {
1334 case 1:
1335 add_settings_error( 'sharing-image', 'sharing-image', __( 'Settings successfully updated.', 'sharing-image' ), 'updated' );
1336 break;
1337
1338 case 2:
1339 add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to save template settings.', 'sharing-image' ) );
1340 break;
1341
1342 case 3:
1343 add_settings_error( 'sharing-image', 'sharing-image', __( 'Template successfully deleted.', 'sharing-image' ), 'updated' );
1344 break;
1345
1346 case 4:
1347 add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to delete template.', 'sharing-image' ) );
1348 break;
1349
1350 case 5:
1351 add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to save configuration settings.', 'sharing-image' ) );
1352 break;
1353 }
1354
1355 settings_errors( 'sharing-image' );
1356 }
1357
1358 /**
1359 * Set list of settings page tabs.
1360 */
1361 private function init_tabs() {
1362 $tabs = array(
1363 'templates' => array(
1364 'label' => __( 'Templates', 'sharing-image' ),
1365 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ),
1366 'default' => true,
1367 ),
1368 'config' => array(
1369 'label' => __( 'Configuration', 'sharing-image' ),
1370 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG . '&tab=config' ),
1371 ),
1372 'premium' => array(
1373 'label' => __( 'Premium', 'sharing-image' ),
1374 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG . '&tab=premium' ),
1375 ),
1376 );
1377
1378 /**
1379 * Filters tabs in settings page.
1380 *
1381 * @param array $tabs List of settings tabs.
1382 */
1383 $this->tabs = apply_filters( 'sharing_image_settings_tabs', $tabs );
1384 }
1385
1386 /**
1387 * Print menu on settings page.
1388 */
1389 private function show_settings_menu() {
1390 $current = $this->get_current_tab();
1391
1392 foreach ( $this->tabs as $tab => $args ) {
1393 $classes = array(
1394 'sharing-image-tab',
1395 );
1396
1397 if ( $current === $tab ) {
1398 $classes[] = 'active';
1399 }
1400
1401 if ( null === $current && ! empty( $args['default'] ) ) {
1402 $classes[] = 'active';
1403 }
1404
1405 printf(
1406 '<a href="%2$s" class="%1$s">%3$s</a>',
1407 esc_attr( implode( ' ', $classes ) ),
1408 esc_url( $args['link'] ),
1409 esc_html( $args['label'] )
1410 );
1411 }
1412 }
1413
1414 /**
1415 * Get availible fonts.
1416 *
1417 * @return array List of availible poster fonts.
1418 */
1419 private function get_fonts() {
1420 $fonts = array(
1421 'open-sans' => 'Open Sans',
1422 'merriweather' => 'Merriweather',
1423 'roboto-slab' => 'Roboto Slab',
1424 'ubuntu' => 'Ubuntu',
1425 'rubik-bold' => 'Rubik Bold',
1426 'montserrat' => 'Montserrat',
1427 );
1428
1429 /**
1430 * Filters poster fonts.
1431 *
1432 * @param array List of availible poster fonts.
1433 */
1434 return apply_filters( 'sharing_image_poster_fonts', $fonts );
1435 }
1436
1437 /**
1438 * Get tab link by slug.
1439 *
1440 * @param string $tab Tab name.
1441 *
1442 * @return string|null Tab link.
1443 */
1444 private function get_tab_link( $tab ) {
1445 if ( empty( $this->tabs[ $tab ]['link'] ) ) {
1446 return null;
1447 }
1448
1449 return $this->tabs[ $tab ]['link'];
1450 }
1451
1452 /**
1453 * Get current tab.
1454 *
1455 * @return string|null Current tab name.
1456 */
1457 private function get_current_tab() {
1458 // phpcs:disable WordPress.Security.NonceVerification
1459 if ( ! empty( $_GET['tab'] ) ) {
1460 $tab = sanitize_file_name( wp_unslash( $_GET['tab'] ) );
1461
1462 if ( array_key_exists( $tab, $this->tabs ) ) {
1463 return $tab;
1464 }
1465 }
1466 // phpcs:enable WordPress.Security.NonceVerification
1467
1468 return null;
1469 }
1470
1471 /**
1472 * Create upload directory and return its path and url
1473 *
1474 * @param string $storage Relative directory path.
1475 *
1476 * @return array Path and url to upload directory.
1477 */
1478 private function create_upload_dir( $storage ) {
1479 $storage = trim( $storage, '/' );
1480
1481 /**
1482 * Change permissions when creating new folders.
1483 *
1484 * @param int $permissions New directory access permissions. By default 0755.
1485 */
1486 $permissions = apply_filters( 'sharing_image_directory_permissions', 0755 );
1487
1488 // We do not pay attention to the possible error.
1489 mkdir( ABSPATH . $storage, $permissions, true );
1490
1491 return array( ABSPATH . $storage, site_url( $storage ) );
1492 }
1493
1494 /**
1495 * Add message id to the back link and redirect
1496 *
1497 * @param string $return Redirect link.
1498 * @param int $message Settings error message id.
1499 */
1500 private function redirect_with_message( $return, $message ) {
1501 $return = add_query_arg( array( 'message' => $message ), $return );
1502
1503 wp_safe_redirect( $return );
1504 exit;
1505 }
1506
1507 /**
1508 * Is current admin screen the plugin options screen.
1509 *
1510 * @return bool Whether the settings screen of this plugin is displayed or not.
1511 */
1512 private function is_settings_screen() {
1513 $current_screen = get_current_screen();
1514
1515 if ( $current_screen && self::SCREEN_ID === $current_screen->id ) {
1516 return true;
1517 }
1518
1519 return false;
1520 }
1521 }
1522