init_tabs(); } /** * Init class actions and filters. */ public function init() { add_action( 'admin_menu', array( $this, 'add_menu' ) ); // Handle settings POST requests. add_action( 'admin_init', array( $this, 'handle_post_requests' ) ); // Handle settings AJAX requests. add_action( 'admin_init', array( $this, 'handle_ajax_requests' ) ); // Allow uploading custom fonts for templates editor. add_action( 'admin_init', array( $this, 'allow_custom_fonts' ) ); // Add settings link to plugins list. add_filter( 'plugin_action_links', array( $this, 'add_settings_link' ), 10, 2 ); // Update admin title for different tabs. add_action( 'admin_title', array( $this, 'update_settings_title' ) ); // Schedule Premium license verification. add_action( self::EVENT_PREMIUM, array( $this, 'launch_verification_event' ), 10, 1 ); } /** * Add plugin settings page in WordPress menu. */ public function add_menu() { /** * Easy way to hide settings page. * * @param bool $hide_settings Set true to hide settings page. */ $hide_settings = apply_filters( 'sharing_image_hide_settings', false ); if ( $hide_settings ) { return; } add_options_page( esc_html__( 'Sharing Image settings', 'sharing-image' ), esc_html__( 'Sharing Image', 'sharing-image' ), 'manage_options', self::SETTINGS_SLUG, array( $this, 'display_settings' ) ); // Add required assets and objects. add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); } /** * Handle settings POST requests. */ public function handle_post_requests() { $actions = array( 'config' => 'save_settings_config', 'editor' => 'save_settings_template', 'delete' => 'delete_settings_template', ); foreach ( $actions as $key => $method ) { $action = 'sharing_image_' . $key; if ( method_exists( $this, $method ) ) { add_action( 'admin_post_' . $action, array( $this, $method ) ); } } } /** * Handle settings AJAX requests. */ public function handle_ajax_requests() { $actions = array( 'show' => 'show_template_preview', 'save' => 'save_template_preview', 'verify' => 'verify_premium_key', 'revoke' => 'revoke_premium_access', ); foreach ( $actions as $key => $method ) { $action = 'sharing_image_' . $key; if ( method_exists( $this, $method ) ) { add_action( 'wp_ajax_' . $action, array( $this, $method ) ); } } } /** * Allow uploading custom fonts for templates editor. * This function may affect the security of the site. * Disable font uploading if you are not going to use it. */ public function allow_custom_fonts() { /** * Easy way to disable custom font uploading. * * @param bool $disable_fonts Set true to disable fonts uploading. */ $disable_fonts = apply_filters( 'sharing_image_disable_custom_fonts', false ); if ( $disable_fonts ) { return; } // Allow True Type fonts uploading. add_filter( 'wp_check_filetype_and_ext', array( $this, 'fix_ttf_mime_type' ), 10, 3 ); // Add new .ttf font mime type. add_filter( 'upload_mimes', array( $this, 'add_ttf_mime_type' ) ); } /** * Add settings link to plugins list. * * @param array $actions An array of plugin action links. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @return array Array of settings actions. */ public function add_settings_link( $actions, $plugin_file ) { $actions = (array) $actions; if ( plugin_basename( SHARING_IMAGE_FILE ) === $plugin_file ) { $actions[] = sprintf( '%s', admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ), __( 'Settings', 'sharing-image' ) ); } return $actions; } /** * Save settings config fields. */ public function save_settings_config() { check_admin_referer( basename( __FILE__ ), 'sharing_image_nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'sharing-image' ) ); } $return = $this->get_tab_link( 'config' ); if ( null === $return ) { $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ); } if ( ! isset( $_POST['sharing_image_config'] ) ) { $this->redirect_with_message( $return, 5 ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $config = $this->sanitize_config( wp_unslash( $_POST['sharing_image_config'] ) ); $this->update_config( $config ); // Redirect with success message. $this->redirect_with_message( $return, 1 ); } /** * Save template editor fields. */ public function save_settings_template() { check_admin_referer( basename( __FILE__ ), 'sharing_image_nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to manage options for this site.', 'sharing-image' ) ); } $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ); if ( ! isset( $_POST['sharing_image_index'] ) ) { $this->redirect_with_message( $return, 2 ); } $index = absint( wp_unslash( $_POST['sharing_image_index'] ) ); if ( ! isset( $_POST['sharing_image_editor'] ) ) { $this->redirect_with_message( $return, 2 ); } // Skip 2nd+ templates if the Premium is not active. if ( $index > 0 && ! $this->is_premium_features() ) { $this->redirect_with_message( $return, 2 ); } $return = add_query_arg( array( 'template' => $index + 1 ), $return ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) ); $this->update_templates( $index, $editor ); // Redirect with success message. $this->redirect_with_message( $return, 1 ); } /** * Action to delete template from editor page. */ public function delete_settings_template() { check_admin_referer( basename( __FILE__ ), 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { return; } $return = admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ); if ( ! isset( $_REQUEST['template'] ) ) { $this->redirect_with_message( $return, 4 ); } // Get index from template ID. $index = absint( $_REQUEST['template'] ) - 1; if ( ! $this->update_templates( $index ) ) { $this->redirect_with_message( $return, 4 ); } $this->redirect_with_message( $return, 3 ); } /** * Show generated template from AJAX request. */ public function show_template_preview() { $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false ); if ( false === $check ) { wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 ); } if ( ! isset( $_POST['sharing_image_index'] ) ) { wp_send_json_error( __( 'Poster index undefined.', 'sharing-image' ), 400 ); } $index = absint( wp_unslash( $_POST['sharing_image_index'] ) ); if ( ! isset( $_POST['sharing_image_editor'] ) ) { wp_send_json_error( __( 'Editor settings are not set.', 'sharing-image' ), 400 ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) ); // Show poster using generator class. $poster = ( new Generator() )->show( $editor, $index ); if ( is_wp_error( $poster ) ) { wp_send_json_error( $poster->get_error_message(), 400 ); } } /** * Show generated template from AJAX request. */ public function save_template_preview() { $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false ); if ( false === $check ) { wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 ); } if ( ! isset( $_POST['sharing_image_index'] ) ) { wp_send_json_error( __( 'Poster index undefined.', 'sharing-image' ), 400 ); } $index = absint( wp_unslash( $_POST['sharing_image_index'] ) ); if ( ! isset( $_POST['sharing_image_editor'] ) ) { wp_send_json_error( __( 'Editor settings are not set.', 'sharing-image' ), 400 ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $editor = $this->sanitize_editor( wp_unslash( $_POST['sharing_image_editor'] ) ); // Save poster using generator class. $poster = ( new Generator() )->save( $editor, $index ); if ( is_wp_error( $poster ) ) { wp_send_json_error( $poster->get_error_message(), 400 ); } wp_send_json_success( $poster ); } /** * Verify Premium key from AJAX request. */ public function verify_premium_key() { $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false ); if ( false === $check ) { wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 ); } if ( empty( $_POST['sharing_image_key'] ) ) { wp_send_json_error( __( 'Premium key undefined.', 'sharing-image' ), 400 ); } $key = sanitize_text_field( wp_unslash( $_POST['sharing_image_key'] ) ); $args = array( 'body' => array( 'key' => $key, 'domain' => wp_parse_url( site_url(), PHP_URL_HOST ), ), ); $response = wp_remote_post( self::REMOTE_LICENSES, $args ); if ( is_wp_error( $response ) ) { wp_send_json_error( __( 'Remote request error: ', 'sharing-image' ) . $response->get_error_message(), 400 ); } $answer = json_decode( $response['body'], true ); if ( ! isset( $answer['success'] ) ) { wp_send_json_error( __( 'Invalid response received from the verification server.', 'sharing-image' ), 400 ); } // Remove license verification event. wp_unschedule_hook( self::EVENT_PREMIUM ); if ( true === $answer['success'] ) { $license = $this->update_license( true, $key ); // Schedule license verification twice daily. $this->schedule_verification( array( $key ) ); wp_send_json_success( $license ); } $error = array( 'success' => false, 'data' => __( 'Verification failed.', 'sharing-image' ), ); if ( isset( $answer['result'] ) ) { $error['code'] = $answer['result']; } $this->update_license( false, $key ); wp_send_json( $error, 403 ); } /** * Revoke Premium access from AJAX request. */ public function revoke_premium_access() { $check = check_ajax_referer( basename( __FILE__ ), 'sharing_image_nonce', false ); if ( false === $check ) { wp_send_json_error( __( 'Invalid security token. Reload the page and retry.', 'sharing-image' ), 403 ); } // Remove license verification event. wp_unschedule_hook( self::EVENT_PREMIUM ); // Disable Premium license. $license = $this->update_license( false ); wp_send_json_success( $license ); } /** * Show plugin settings. */ public function display_settings() { if ( ! $this->is_settings_screen() ) { return; } include_once SHARING_IMAGE_DIR . 'templates/settings.php'; /** * Fires on settings template including. */ do_action( 'sharing_image_settings' ); } /** * Fix .ttf files mime. * * @param array $types Values for the extension, mime type, and corrected filename. * @param string $file Full path to the file. * @param string $filename The name of the file (may differ from $file due to. * * @return array List of file types. */ public function fix_ttf_mime_type( $types, $file, $filename ) { $extension = pathinfo( $filename, PATHINFO_EXTENSION ); if ( 'ttf' === $extension ) { $types['ext'] = false; if ( current_user_can( 'manage_options' ) ) { $types['ext'] = 'ttf'; $types['type'] = 'application/x-font-ttf'; } } return $types; } /** * Add new .ttf font mime type. * * @param array $types Allowed file types to upload. * * @return array Allowed file types to upload. */ public function add_ttf_mime_type( $types ) { $types['ttf'] = 'application/x-font-ttf'; return $types; } /** * Enqueue settings styles. */ public function enqueue_styles() { if ( ! $this->is_settings_screen() ) { return; } wp_enqueue_style( 'sharing-image-settings', SHARING_IMAGE_URL . 'assets/styles/settings.css', array(), SHARING_IMAGE_VERSION, 'all' ); } /** * Enqueue settings scripts. */ public function enqueue_scripts() { if ( ! $this->is_settings_screen() ) { return; } wp_enqueue_script( 'sharing-image-settings', SHARING_IMAGE_URL . 'assets/scripts/settings.js', array( 'wp-i18n', 'wp-polyfill-url', 'wp-polyfill-formdata' ), SHARING_IMAGE_VERSION, true ); wp_enqueue_media(); // Translations availible only for WP 5.0+. wp_set_script_translations( 'sharing-image-settings', 'sharing-image' ); $object = $this->create_script_object(); // Add settings script object. wp_localize_script( 'sharing-image-settings', 'sharingImageSettings', $object ); } /** * Get templates list from options. * * @return array List of templates. */ public function get_templates() { $templates = get_option( self::OPTION_TEMPLATES, array() ); if ( ! $this->is_premium_features() ) { $templates = array_slice( $templates, 0, 1 ); } /** * Filters list of templates. * * @param array $templates List of templates. */ return apply_filters( 'sharing_image_get_templates', $templates ); } /** * Update templates using index. * * @param int $index Template index to update. * @param array $editor New template data. */ public function update_templates( $index, $editor = null ) { // Method get_templates() is not used to save old templates during Premium switching. $templates = get_option( self::OPTION_TEMPLATES, array() ); $templates[ $index ] = $editor; if ( null === $editor ) { unset( $templates[ $index ] ); } /** * Filters list of templates before update in database. * * @param array $templates List of reindexed templates. */ $templates = apply_filters( 'sharing_image_update_templates', array_values( $templates ) ); return update_option( self::OPTION_TEMPLATES, $templates ); } /** * Get plugin config settings. * * @return array List of plugin config settings. */ public function get_config() { $config = get_option( self::OPTION_CONFIG, array() ); /** * Filters settigns config. * * @param array List of plugin config settings. */ return apply_filters( 'sharing_image_get_config', $config ); } /** * Update config settings. * * @param array $config License settings config data. */ public function update_config( $config ) { /** * Filters config options before their update in database. * * @param array $config Settings config data. */ $config = apply_filters( 'sharing_image_update_config', $config ); update_option( self::OPTION_CONFIG, $config ); } /** * Get plugin license settings. * * @return array List of plugin license settings. */ public function get_license() { $license = get_option( self::OPTION_LICENSE, array() ); /** * Check if the plugin in development mode. * * @param bool Current development state. Disabled by default. */ $develop = apply_filters( 'sharing_image_develop', false ); if ( $develop ) { $license['develop'] = true; } /** * Filters license settings. * * @param array List of plugin license settings. */ return apply_filters( 'sharing_image_get_license', $license ); } /** * Set license options. * * @param bool $premium Premium status. * @param string $key License key. * @param string $error Verification error code. * @return array License options. */ public function update_license( $premium, $key = '', $error = '' ) { $license = get_option( self::OPTION_LICENSE, array() ); $license['premium'] = $premium; if ( ! empty( $key ) ) { $license['key'] = $key; } unset( $license['error'] ); if ( ! empty( $error ) ) { $license['error'] = $error; } // Save updated license settings in database. update_option( self::OPTION_LICENSE, $license ); return $license; } /** * Get directory to uploaded posters. * * @return array Path and url to upload directory. */ public function get_upload_dir() { $config = $this->get_config(); if ( ! isset( $config['uploads'] ) ) { $config['uploads'] = 'default'; } // Create custom upload directory. if ( isset( $config['storage'] ) && 'custom' === $config['uploads'] ) { return $this->create_upload_dir( $config['storage'] ); } $uploads = wp_upload_dir(); /** * Filters upload directory. * * @param array $dir Path and url to upload directory. */ return apply_filters( 'sharing_image_upload_dir', array( $uploads['path'], $uploads['url'] ) ); } /** * Get generated image file format. * * @param string $format Optional. Default image format. * @return string Image file format. */ public function get_file_format( $format = 'jpg' ) { $config = $this->get_config(); if ( isset( $config['format'] ) ) { $format = $config['format']; } /** * Filters Image file format. * * @param string $format Image file format. */ return apply_filters( 'sharing_image_file_format', $format ); } /** * Get quality of generated poster. * * @param int $quality Optional. Default image quality. * * @return int Image quality. */ public function get_quality( $quality = 90 ) { $config = $this->get_config(); if ( isset( $config['quality'] ) ) { $quality = $config['quality']; } /** * Filters poster image quality. * * @param string $quality Image quality. */ return apply_filters( 'sharing_image_poster_quality', $quality ); } /** * Try to get default poster image data. * Returns array with image url, width and height. * * @see https://developer.wordpress.org/reference/functions/wp_get_attachment_image_src/ * * @return array|false Array of image data, or boolean false if no image is available. */ public function get_default_poster_src() { $config = $this->get_config(); if ( empty( $config['default'] ) ) { return false; } $poster = wp_get_attachment_image_src( $config['default'], 'full' ); if ( is_array( $poster ) ) { $poster = array_slice( $poster, 0, 3 ); } /** * Filters default poster data. * * @param array|false Array of image data, or boolean false if no image is available. */ return apply_filters( 'sharing_image_default_poster_src', $poster ); } /** * Update settings page title. * * @param string $title Plugin settings page title. * * @return string Plugin settings title */ public function update_settings_title( $title ) { if ( ! $this->is_settings_screen() ) { return $title; } $tab = $this->get_current_tab(); if ( null === $tab ) { return $title; } if ( empty( $this->tabs[ $tab ]['label'] ) ) { return $title; } $label = esc_html( $this->tabs[ $tab ]['label'] ); return sprintf( '%s – %s', $label, $title ); } /** * Launch scheduled license verification event. * Do not disable Premium if the verification server does not respond. * * @param string $key License key. */ public function launch_verification_event( $key ) { $args = array( 'body' => array( 'key' => $key, 'domain' => wp_parse_url( site_url(), PHP_URL_HOST ), ), ); $response = wp_remote_post( self::REMOTE_LICENSES, $args ); if ( is_wp_error( $response ) ) { return; } $answer = json_decode( $response['body'], true ); if ( ! isset( $answer['success'] ) ) { return; } if ( true === $answer['success'] ) { return $this->update_license( true, $key ); } if ( ! isset( $answer['result'] ) ) { return $this->update_license( false, $key ); } $this->update_license( false, $key, $answer['result'] ); } /** * Check if Premium features availible. * * @return bool Whether premium featured enabled. */ public function is_premium_features() { $license = $this->get_license(); if ( ! empty( $license['premium'] ) || ! empty( $license['develop'] ) ) { return true; } return false; } /** * Schedule license verification. * * @param array $args List of event arguments. License key by default. */ public function schedule_verification( $args = array() ) { if ( wp_next_scheduled( self::EVENT_PREMIUM, $args ) ) { return; } wp_schedule_event( time() + DAY_IN_SECONDS / 2, 'twicedaily', self::EVENT_PREMIUM, $args ); } /** * Create script object to inject with settings. * * @return array Filtered script settings object. */ private function create_script_object() { $uploads = wp_get_upload_dir(); // Get uploads directory path from WordPress root. $basedir = str_replace( ABSPATH, '', $uploads['basedir'] ); $object = array( 'nonce' => wp_create_nonce( basename( __FILE__ ) ), 'links' => array( 'uploads' => esc_url( admin_url( 'upload.php' ) ), 'action' => esc_url( admin_url( 'admin-post.php' ) ), 'premium' => esc_url_raw( $this->get_tab_link( 'premium' ) ), 'storage' => path_join( $basedir, 'sharing-image' ), ), 'templates' => $this->get_templates(), 'config' => $this->get_config(), 'license' => $this->get_license(), 'fonts' => $this->get_fonts(), ); /** * Filters settings script object. * * @param array $object Array of settings script object. */ return apply_filters( 'sharing_image_settings_object', $object ); } /** * Sanitize editor template settings. * * @param array $editor Template editor settings. * * @return array */ private function sanitize_editor( $editor ) { $sanitized = array(); if ( ! empty( $editor['preview'] ) ) { $sanitized['preview'] = sanitize_text_field( $editor['preview'] ); } if ( ! empty( $editor['title'] ) ) { $sanitized['title'] = sanitize_text_field( $editor['title'] ); } if ( ! empty( $editor['attachment'] ) ) { $sanitized['attachment'] = absint( $editor['attachment'] ); } if ( ! empty( $editor['suspend'] ) ) { $sanitized['suspend'] = 'suspend'; } $sanitized['fill'] = '#000000'; if ( ! empty( $editor['fill'] ) ) { $sanitized['fill'] = sanitize_hex_color( $editor['fill'] ); } $sanitized['background'] = 'blank'; if ( isset( $editor['background'] ) ) { $background = array( 'dynamic', 'blank', 'permanent' ); // Set default background for permanent option without attachment. if ( empty( $sanitized['attachment'] ) ) { $background = array_diff( $background, array( 'permanent' ) ); } if ( in_array( $editor['background'], $background, true ) ) { $sanitized['background'] = $editor['background']; } } $sanitized['width'] = 1200; if ( ! empty( $editor['width'] ) ) { $sanitized['width'] = absint( $editor['width'] ); } $sanitized['height'] = 630; if ( ! empty( $editor['height'] ) ) { $sanitized['height'] = absint( $editor['height'] ); } if ( isset( $editor['layers'] ) && is_array( $editor['layers'] ) ) { $layers = array(); foreach ( $editor['layers'] as $layer ) { if ( empty( $layer['type'] ) ) { continue; } switch ( $layer['type'] ) { case 'text': $layers[] = $this->sanitize_text_layer( $layer ); break; case 'image': $layers[] = $this->sanitize_image_layer( $layer ); break; case 'filter': $layers[] = $this->sanitize_filter_layer( $layer ); break; case 'rectangle': $layers[] = $this->sanitize_rectangle_layer( $layer ); break; } } $sanitized['layers'] = $layers; } /** * Filters template editor sanitized fields. * * @param array $sanitized List of sanitized editor fields. * @param array $editor List of editor fields before sanitization. */ return apply_filters( 'sharing_image_sanitize_editor', $sanitized, $editor ); } /** * Sanitize template editor text layer. * * @param array $layer Layer settings. * * @return array Sanitized layer settings. */ private function sanitize_text_layer( $layer ) { $sanitized = array(); // No need to sanitize after switch. $sanitized['type'] = $layer['type']; if ( ! empty( $layer['dynamic'] ) ) { $sanitized['dynamic'] = 'dynamic'; } if ( isset( $layer['title'] ) ) { $sanitized['title'] = sanitize_text_field( $layer['title'] ); } if ( isset( $layer['content'] ) ) { $sanitized['content'] = sanitize_textarea_field( $layer['content'] ); } if ( isset( $layer['sample'] ) ) { $sanitized['sample'] = sanitize_textarea_field( $layer['sample'] ); } $sanitized['preset'] = 'none'; if ( isset( $layer['preset'] ) ) { $preset = array( 'title', 'excerpt' ); if ( in_array( $layer['preset'], $preset, true ) ) { $sanitized['preset'] = $layer['preset']; } } $sanitized['color'] = '#ffffff'; if ( ! empty( $layer['color'] ) ) { $sanitized['color'] = sanitize_hex_color( $layer['color'] ); } $sanitized['horizontal'] = 'left'; if ( isset( $layer['horizontal'] ) ) { $horizontal = array( 'center', 'right' ); if ( in_array( $layer['horizontal'], $horizontal, true ) ) { $sanitized['horizontal'] = $layer['horizontal']; } } $sanitized['vertical'] = 'top'; if ( isset( $layer['vertical'] ) ) { $vertical = array( 'center', 'bottom' ); if ( in_array( $layer['vertical'], $vertical, true ) ) { $sanitized['vertical'] = $layer['vertical']; } } if ( isset( $layer['fontsize'] ) ) { $sanitized['fontsize'] = absint( $layer['fontsize'] ); } if ( isset( $layer['lineheight'] ) ) { $sanitized['lineheight'] = (float) $layer['lineheight']; } if ( isset( $layer['fontname'] ) ) { $sanitized['fontname'] = sanitize_text_field( $layer['fontname'] ); } if ( ! empty( $layer['fontfile'] ) ) { $sanitized['fontfile'] = absint( $layer['fontfile'] ); } $sizes = array( 'x', 'y', 'width', 'height' ); foreach ( $sizes as $size ) { if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) { continue; } $sanitized[ $size ] = absint( $layer[ $size ] ); } return $sanitized; } /** * Sanitize template editor image layer. * * @param array $layer Layer settings. * * @return array Sanitized image layer settings. */ private function sanitize_image_layer( $layer ) { $sanitized = array(); // No need to sanitize after switch. $sanitized['type'] = $layer['type']; if ( ! empty( $layer['attachment'] ) ) { $sanitized['attachment'] = absint( $layer['attachment'] ); } $sizes = array( 'x', 'y', 'width', 'height' ); foreach ( $sizes as $size ) { if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) { continue; } $sanitized[ $size ] = absint( $layer[ $size ] ); } return $sanitized; } /** * Sanitize template editor filter layer. * * @param array $layer Layer settings. * * @return array Sanitized filter layer settings. */ private function sanitize_filter_layer( $layer ) { $sanitized = array(); // No need to sanitize after switch. $sanitized['type'] = $layer['type']; if ( ! empty( $layer['grayscale'] ) ) { $sanitized['grayscale'] = 'grayscale'; } if ( ! empty( $layer['blur'] ) ) { $sanitized['blur'] = 'blur'; } $sanitized['brightness'] = 0; if ( isset( $layer['brightness'] ) ) { $brightness = (int) $layer['brightness']; if ( $brightness >= -100 && $brightness <= 100 ) { $sanitized['brightness'] = $brightness; } } $sanitized['contrast'] = 0; if ( isset( $layer['contrast'] ) ) { $contrast = (int) $layer['contrast']; if ( $contrast >= -100 && $contrast <= 100 ) { $sanitized['contrast'] = $contrast; } } $sanitized['blackout'] = 0; if ( isset( $layer['blackout'] ) ) { $blackout = (int) $layer['blackout']; if ( $blackout >= 0 && $blackout <= 100 ) { $sanitized['blackout'] = $blackout; } } return $sanitized; } /** * Sanitize template editor rectagle layer. * * @param array $layer Layer settings. * * @return array Sanitized rectangle layer settings. */ private function sanitize_rectangle_layer( $layer ) { $sanitized = array(); // No need to sanitize after switch. $sanitized['type'] = $layer['type']; if ( ! empty( $layer['outline'] ) ) { $sanitized['outline'] = 'outline'; } $sanitized['color'] = '#ffffff'; if ( ! empty( $layer['color'] ) ) { $sanitized['color'] = sanitize_hex_color( $layer['color'] ); } $sanitized['opacity'] = 0; if ( isset( $layer['opacity'] ) ) { $opacity = (float) $layer['opacity']; if ( $opacity >= 0 && $opacity <= 100 ) { $sanitized['opacity'] = $opacity; } } $sanitized['thickness'] = 0; if ( isset( $layer['thickness'] ) ) { $thickness = (int) $layer['thickness']; if ( $thickness >= 0 && $thickness <= 50 ) { $sanitized['thickness'] = $thickness; } } $sizes = array( 'x', 'y', 'width', 'height' ); foreach ( $sizes as $size ) { if ( ! isset( $layer[ $size ] ) || '' === $layer[ $size ] ) { continue; } $sanitized[ $size ] = absint( $layer[ $size ] ); } return $sanitized; } /** * Sanitize config settings. * * @param array $config Config settings. * * @return array Sanitized config settings. */ private function sanitize_config( $config ) { $sanitized = array(); if ( ! empty( $config['default'] ) ) { $sanitized['default'] = absint( $config['default'] ); } $sanitized['format'] = 'jpg'; if ( isset( $config['format'] ) ) { $format = $config['format']; if ( in_array( $format, array( 'jpg', 'png' ), true ) ) { $sanitized['format'] = $config['format']; } } if ( isset( $config['quality'] ) ) { $quality = (int) $config['quality']; if ( $quality >= 1 && $quality <= 100 ) { $sanitized['quality'] = $quality; } } $sanitized['uploads'] = 'default'; if ( isset( $config['uploads'] ) ) { $uploads = $config['uploads']; if ( in_array( $uploads, array( 'custom', 'default' ), true ) ) { $sanitized['uploads'] = $config['uploads']; } } if ( isset( $config['storage'] ) ) { $sanitized['storage'] = sanitize_text_field( $config['storage'] ); } /** * Filters template editor sanitized fields. * * @param array $sanitized List of sanitized config fields. * @param array $config List of config fields before sanitization. */ return apply_filters( 'sharing_image_sanitize_config', $sanitized, $config ); } /** * Show settings tab template. */ private function show_settings_section() { $tab = $this->get_current_tab(); if ( null === $tab ) { return; } include_once SHARING_IMAGE_DIR . "templates/{$tab}.php"; } /** * Show settings messages and errors after post actions. */ private function show_settings_message() { // phpcs:ignore WordPress.Security.NonceVerification $message = isset( $_GET['message'] ) ? absint( $_GET['message'] ) : 0; switch ( $message ) { case 1: add_settings_error( 'sharing-image', 'sharing-image', __( 'Settings successfully updated.', 'sharing-image' ), 'updated' ); break; case 2: add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to save template settings.', 'sharing-image' ) ); break; case 3: add_settings_error( 'sharing-image', 'sharing-image', __( 'Template successfully deleted.', 'sharing-image' ), 'updated' ); break; case 4: add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to delete template.', 'sharing-image' ) ); break; case 5: add_settings_error( 'sharing-image', 'sharing-image', __( 'Failed to save configuration settings.', 'sharing-image' ) ); break; } settings_errors( 'sharing-image' ); } /** * Set list of settings page tabs. */ private function init_tabs() { $tabs = array( 'templates' => array( 'label' => __( 'Templates', 'sharing-image' ), 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG ), 'default' => true, ), 'config' => array( 'label' => __( 'Configuration', 'sharing-image' ), 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG . '&tab=config' ), ), 'premium' => array( 'label' => __( 'Premium', 'sharing-image' ), 'link' => admin_url( 'options-general.php?page=' . self::SETTINGS_SLUG . '&tab=premium' ), ), ); /** * Filters tabs in settings page. * * @param array $tabs List of settings tabs. */ $this->tabs = apply_filters( 'sharing_image_settings_tabs', $tabs ); } /** * Print menu on settings page. */ private function show_settings_menu() { $current = $this->get_current_tab(); foreach ( $this->tabs as $tab => $args ) { $classes = array( 'sharing-image-tab', ); if ( $current === $tab ) { $classes[] = 'active'; } if ( null === $current && ! empty( $args['default'] ) ) { $classes[] = 'active'; } printf( '%3$s', esc_attr( implode( ' ', $classes ) ), esc_url( $args['link'] ), esc_html( $args['label'] ) ); } } /** * Get availible fonts. * * @return array List of availible poster fonts. */ private function get_fonts() { $fonts = array( 'open-sans' => 'Open Sans', 'merriweather' => 'Merriweather', 'roboto-slab' => 'Roboto Slab', 'ubuntu' => 'Ubuntu', 'rubik-bold' => 'Rubik Bold', 'montserrat' => 'Montserrat', ); /** * Filters poster fonts. * * @param array List of availible poster fonts. */ return apply_filters( 'sharing_image_poster_fonts', $fonts ); } /** * Get tab link by slug. * * @param string $tab Tab name. * * @return string|null Tab link. */ private function get_tab_link( $tab ) { if ( empty( $this->tabs[ $tab ]['link'] ) ) { return null; } return $this->tabs[ $tab ]['link']; } /** * Get current tab. * * @return string|null Current tab name. */ private function get_current_tab() { // phpcs:disable WordPress.Security.NonceVerification if ( ! empty( $_GET['tab'] ) ) { $tab = sanitize_file_name( wp_unslash( $_GET['tab'] ) ); if ( array_key_exists( $tab, $this->tabs ) ) { return $tab; } } // phpcs:enable WordPress.Security.NonceVerification return null; } /** * Create upload directory and return its path and url * * @param string $storage Relative directory path. * * @return array Path and url to upload directory. */ private function create_upload_dir( $storage ) { $storage = trim( $storage, '/' ); /** * Change permissions when creating new folders. * * @param int $permissions New directory access permissions. By default 0755. */ $permissions = apply_filters( 'sharing_image_directory_permissions', 0755 ); // We do not pay attention to the possible error. mkdir( ABSPATH . $storage, $permissions, true ); return array( ABSPATH . $storage, site_url( $storage ) ); } /** * Add message id to the back link and redirect * * @param string $return Redirect link. * @param int $message Settings error message id. */ private function redirect_with_message( $return, $message ) { $return = add_query_arg( array( 'message' => $message ), $return ); wp_safe_redirect( $return ); exit; } /** * Is current admin screen the plugin options screen. * * @return bool Whether the settings screen of this plugin is displayed or not. */ private function is_settings_screen() { $current_screen = get_current_screen(); if ( $current_screen && self::SCREEN_ID === $current_screen->id ) { return true; } return false; } }