| Server IP : 170.10.161.225 / Your IP : 216.73.216.78 Web Server : Apache System : Linux vps103298.mylogin.co 4.18.0-513.11.1.el8_9.x86_64 #1 SMP Wed Jan 17 02:00:40 EST 2024 x86_64 User : calvet ( 273824) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : ON Directory : /home/www/calvetrealty.com/wp-content/plugins/readabler/src/Merkulove/Readabler/ |
Upload File : |
<?php
/**
* Readabler
* Web accessibility for Your WordPress site.
* Exclusively on https://1.envato.market/readabler
*
* @encoding UTF-8
* @version 1.7.4
* @copyright (C) 2018 - 2024 Merkulove ( https://merkulov.design/ ). All rights reserved.
* @license Envato License https://1.envato.market/KYbje
* @contributors Nemirovskiy Vitaliy (nemirovskiyvitaliy@gmail.com), Dmitry Merkulov (dmitry@merkulov.design)
* @support help@merkulov.design
* @license Envato License https://1.envato.market/KYbje
**/
namespace Merkulove\Readabler;
use DOMDocument;
use DOMXPath;
use Google\ApiCore\ApiException;
use Google\Cloud\TextToSpeech\V1\AudioConfig;
use Google\Cloud\TextToSpeech\V1\AudioEncoding;
use Google\Cloud\TextToSpeech\V1\SynthesisInput;
use Google\Cloud\TextToSpeech\V1\TextToSpeechClient;
use Google\Cloud\TextToSpeech\V1\VoiceSelectionParams;
use Merkulove\Readabler\Unity\Helper;
use Merkulove\Readabler\Unity\Plugin;
use Merkulove\Readabler\Unity\Settings;
use Merkulove\Readabler\Unity\TabActivation;
use Merkulove\Readabler\Unity\TabAssignments;
use Merkulove\Readabler\Unity\UI;
/** Exit if accessed directly. */
if ( ! defined( 'ABSPATH' ) ) {
header( 'Status: 403 Forbidden' );
header( 'HTTP/1.1 403 Forbidden' );
exit;
}
/**
* SINGLETON: Caster class contain the main plugin logic.
* @since 1.0.0
**/
final class Caster {
/**
* The one true Caster.
*
* @since 1.0.0
* @access private
* @var Caster
**/
private static $instance;
/**
* Plugin settings
* @var array Plugin options
*/
private static $settings;
/**
* Patience time
* @var int
*/
private static $patience_time = 3600;
/**
* Set up the plugin.
*
* @since 1.0.0
* @access public
*
* @return void
**/
public function setup() {
/** Check compatibility */
self::compatibility_check( true );
self::$settings = Settings::get_instance()->options;
/** Define hooks that run on both the front-end and the dashboard. */
$this->both_hooks();
/** Define public hooks. */
$this->public_hooks();
/** Define admin hooks. */
$this->admin_hooks();
}
/**
* Define hooks that run on both the front-end and the dashboard.
*
* @since 1.0.0
* @access private
*
* @return void
**/
private function both_hooks() {
/** Ajax for Text to Speech. */
if (
'on' === Settings::get_instance()->options['text_to_speech'] ||
'on' === Settings::get_instance()->options['profile_blind_users']
) {
add_action( 'wp_ajax_readablergspeak', [ $this, 'gspeak' ] );
add_action( 'wp_ajax_nopriv_readablergspeak', [ $this, 'gspeak' ] );
}
/** Adds all the necessary shortcodes. */
Shortcodes::get_instance();
/** Ajax action */
Analyzer::get_instance();
/** Add admin bar node. */
AdminBar::add_readabler_bar();
}
/**
* Register all the hooks related to the public-facing functionality.
*
* @since 1.0.0
* @access private
*
* @return void
**/
private function public_hooks() {
/** Work only on frontend area. */
if ( is_admin() ) { return; }
/** Enable Accessibility interface by adding ?readabler of ?accessibility to the URL */
if ( isset( $_GET['readabler'] ) || isset( $_GET['accessibility'] ) ) {
/** Clear the old cookie so that user can set it again. */
setcookie( 'mdp-readabler-hide', '0', strtotime( '-1 day' ), '/' );
}
/** User disables Accessibility If we have this cookie. */
elseif ( isset( $_COOKIE[ 'mdp-readabler-hide' ] ) && '1' === $_COOKIE[ 'mdp-readabler-hide' ] ) {
if ( Settings::get_instance()->options[ 'assets_condition' ] === 'on' ) {
return;
}
}
/** Load CSS for Frontend Area. */
FrontStyles::get_instance();
/** Load JavaScripts for Frontend Area. */
FrontScripts::get_instance();
/** Render Readabler Popup. */
add_action( 'wp_footer', [$this, 'render_markup'] );
/** Added <noscript> */
add_filter( 'script_loader_tag', [ $this, 'add_noscript_filter' ], 10, 3 );
}
/**
* Adding noscript tag for every readabler script
*
* @param $tag
* @param $handle
*
* @return mixed|string
*/
public function add_noscript_filter( $tag, $handle ){
$scrips_list = [
'hotkeys',
'simple-keyboard',
'mdp-readabler'
];
if ( in_array( $handle, $scrips_list ) ){
$noscript = '<noscript>';
$noscript .= esc_html__( ' For full functionality of this site it is necessary to enable JavaScript.', 'readabler' );
$noscript .= '</noscript>';
$tag = $tag . $noscript;
}
return $tag;
}
/**
* Ajax front-end action hook here.
*
* @throws ApiException
* @since 1.0.0
* @access public
*
* @return void
**/
public function gspeak() {
/** Security Check. */
check_ajax_referer( 'readabler-nonce', 'nonce' );
/**
* Includes Google Text To Speech classes.
* @noinspection PhpIncludeInspection
**/
require Plugin::get_path() . '/vendor/autoload.php';
/** Get plugin settings. */
$options = Settings::get_instance()->options;
$options[ 'html_lang' ] = filter_input( INPUT_POST, 'lang' );
/** Setting custom exception handler. */
set_exception_handler( [ ErrorHandler::class, 'exception_handler' ] );
/** Instantiates a client. */
$client = new TextToSpeechClient();
/** Sets text to be synthesized. */
$html = filter_input( INPUT_POST, 'text' );
/** Remove muted elements by class "readabler-mute" or attribute readabler-mute="". */
$html = $this->remove_muted_html( $html );
/** Replace <span readabler-break=""></span> to <break time="200ms"/>. */
$html = $this->replace_break_tag( $html );
/** Clean HTML. */
$html = XMLHelper::get_instance()->clean_html( $html );
/** Strip all html tags, except SSML tags. */
$html = strip_tags( $html, '<p><break><say-as><sub><emphasis>');
/** Remove the space from the left and right sides. */
$html = trim( $html );
/** Convert HTML entities to their corresponding characters: " => " */
$html = html_entity_decode( $html );
/** Force to SSML. */
$ssml = "<speak>";
$ssml .= $html;
$ssml .= "</speak>";
/** Sets text to be synthesized. */
$synthesisInputText = ( new SynthesisInput() )->setSsml( $ssml );
/** Prepare language configuration */
$lang = $this->get_language( $options );
/** Build the voice request, select the language. */
$voice = (new VoiceSelectionParams())
->setLanguageCode( $lang[ 'language_code' ] )
->setName( $lang[ 'voice_code' ] );
/** Effects profile. */
$effectsProfileId = $options['audio-profile'];
/** Select the type of audio file you want returned. */
$audioConfig = ( new AudioConfig() )
->setAudioEncoding( AudioEncoding::MP3 )
->setEffectsProfileId( [ $effectsProfileId ] )
->setSpeakingRate( $options['speaking-rate'] )
->setPitch( $options['pitch'] )
->setSampleRateHertz( 24000 );
/** Perform text-to-speech request on the text input with selected voice. */
$response = $client->synthesizeSpeech( $synthesisInputText, $voice, $audioConfig );
$audioContent = $response->getAudioContent();
/** Restore previous exception handler. */
restore_exception_handler();
/** The response's audioContent is binary. */
echo $audioContent;
wp_die();
}
/**
* Replace <span readabler-break=""></span> to <break time="200ms"/>.
*
* @param $html_content string - HTML code.
*
* @return string
* @since 2.0.0
* @access public
*/
public function replace_break_tag( string $html_content ): string {
/** Create a DOM object. */
$html = new simple_html_dom();
/** Load HTML from a string. */
$html->load( $html_content );
/** Foreach element wit attribute readabler-break="". */
foreach ( $html->find( '[readabler-break]' ) as $el ) {
/** Do replacements. */
$break_tag = $el->outertext;
$break_tag = str_replace( '<span readabler-break=""', '<break', $break_tag );
$el->outertext = $break_tag;
}
/** Return result. */
return $html->save();
}
/**
* Remove muted elements by class "readabler-mute" or attribute readabler-mute="".
*
* @param $post_content - Post/Page content.
*
* @return string
* @since 2.0.0
* @access public
*
**/
public function remove_muted_html( $post_content ): string {
/** Hide DOM parsing errors. */
libxml_use_internal_errors( true );
libxml_clear_errors();
/** Load the possibly malformed HTML into a DOMDocument. */
$dom = new DOMDocument();
$dom->recover = true;
$dom->loadHTML( '<?xml encoding="UTF-8"><body id="repair">' . $post_content . '</body>' ); // input UTF-8.
$selector = new DOMXPath( $dom );
/** Remove all elements with readabler-mute="" attribute. */
foreach( $selector->query( '//*[@readabler-mute]') as $e ) {
$e->parentNode->removeChild( $e );
}
/** Remove all elements with class="readabler-mute". */
foreach( $selector->query( '//*[contains(attribute::class, "readabler-mute")]' ) as $e ) {
$e->parentNode->removeChild( $e );
}
/** HTML without muted tags. */
$body = $dom->documentElement->lastChild;
return trim( XMLHelper::get_instance()->get_inner_html( $body ) );
}
/**
* Register all the hooks related to the admin area functionality.
*
* @since 1.0.0
* @access private
*
* @return void
**/
private function admin_hooks() {
/** Work only in the admin area. */
if ( ! is_admin() ) { return; }
/** Load JS and CSS for Backend Area. */
add_action( 'admin_enqueue_scripts', [ $this, 'admin_styles' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
/** Show admin warning if we need API Key. */
$options = Settings::get_instance()->options;
if( $options['text_to_speech'] === 'on' && ! $options['api_key'] ) {
add_action( 'admin_footer', [ $this, 'key_notice' ] );
}
/** Show activation warning */
add_action( 'admin_footer', [ $this, 'not_activated_notice' ] );
add_action( 'admin_footer', [ $this, 'disable_plugin' ] );
/** Add not-activated class to the admin body */
add_filter( 'admin_body_class', [ $this, 'not_activated_class' ] );
/** Admin styles and scripts */
AdminStyles::get_instance();
AdminScripts::get_instance();
/** Usage Analytics */
UsageAnalytics::get_instance();
/** Scanner creates table */
Reports::create_table();
/** Add column to the list of posts. */
PostList::add_readabler_column();
/** Add meta-box to post. */
Metabox::add_readabler_metabox();
// Store all Post types in transient
add_action( 'init', [ Tools::class, 'store_all_post_types' ], 99 );
}
/**
* Deactivate not-activated plugin after 1 hour
* @return void
*/
public function disable_plugin() {
if ( TabActivation::get_instance()->is_activated() ) { return; }
$patience_period = get_option( 'mdp-readabler-grace' );
if ( $patience_period === false ) {
update_option( 'mdp-readabler-grace', time() );
} else {
if ( time() - $patience_period > self::$patience_time ) {
deactivate_plugins( 'readabler/readabler.php' );
}
}
}
/**
* Add JS for admin area.
*
* @since 1.0.0
* @return void
**/
public function admin_scripts() {
/** Add styles only on setting page */
$screen = get_current_screen();
if ( null === $screen ) { return; }
/** Add styles only on the plugin settings page */
if ( ! in_array( $screen->base, Plugin::get_menu_bases(), true ) ) { return; }
wp_enqueue_script( 'dataTables', Plugin::get_url() . 'js/jquery.dataTables' . Plugin::get_suffix() . '.js', [ 'jquery' ], Plugin::get_version(), true );
}
/**
* Add CSS for admin area.
*
* @since 1.0.0
* @return void
**/
public function admin_styles() {
/** Add styles only on setting page */
$screen = get_current_screen();
if ( null === $screen ) { return; }
/** Add styles only on the plugin settings page */
if ( ! in_array( $screen->base, Plugin::get_menu_bases(), true ) ) { return; }
wp_enqueue_style( 'dataTables', Plugin::get_url() . 'css/jquery.dataTables' . Plugin::get_suffix() . '.css', [], Plugin::get_version() );
}
/**
* Render Readabler markup: Popup, Trigger button.
*
* @since 1.0.0
* @access public
*
* @return void
**/
public function render_markup() {
/** Checks if plugin should work on this page. */
if ( ! TabAssignments::get_instance()->display() ) { return; }
/** Render Accessibility Popup. */
$this->render_popup();
/** Render Trigger Button. */
$this->render_button();
}
/**
* Render Trigger Button.
*
* @since 1.0.0
* @access private
*
* @return void
**/
private function render_button() {
/** Shorthand for Plugin Settings. */
$options = Settings::get_instance()->options;
/** Render trigger button only if it's enabled in settings. */
if ( 'on' !== $options['show_open_button'] ) { return; }
/** Get classes for button. */
$classes = $this->get_button_classes();
/** Prepare tabindex */
$tabindex = $options[ 'button_tabindex' ] !== '0' ? 'tabindex=' . $options[ 'button_tabindex' ] : '';
if ( $options[ 'copyscape_skip' ] === 'on' ) { ?><!--copyscapeskip--><?php }
?>
<!-- Start Readabler WordPress Plugin -->
<div class="<?php echo esc_attr( $classes ); ?>" data-nosnippet>
<button <?php echo esc_attr( $tabindex ); ?> id="mdp-readabler-trigger-button"
class="mdp-icon-position-<?php echo esc_attr( $options['button_icon_position'] ); ?>"
aria-label="<?php esc_html_e( 'Open Accessibility Panel', 'readabler' ); ?>"
title="<?php esc_html_e( 'Accessibility', 'readabler' ); ?>"
data-readabler-trigger="">
<?php if ( 'none' !== $options['button_icon_position'] ) :
$icon = Helper::get_instance()->get_inline_svg( $options['button_icon'] );
$accessible_svg = '<svg role="img" aria-label="' . esc_html__( 'Open Accessibility Panel', 'readabler' ) . '" ';
?>
<span class="mdp-readabler-trigger-button-icon"><?php echo str_replace( '<svg ', $accessible_svg, $icon ); ?></span>
<?php endif; ?>
<?php if ( $options['button_caption'] ) : ?>
<span class="mdp-readabler-trigger-button-caption"><?php echo wp_kses_post( $options['button_caption'] ); ?></span>
<?php endif; ?>
</button>
</div>
<!-- End Readabler WordPress Plugin -->
<?php
if ( $options[ 'copyscape_skip' ] === 'on' ) { ?><!--/copyscapeskip--><?php }
}
/**
* Render Readabler Popup.
*
* @since 1.0.0
* @access private
*
* @return void
**/
private function render_popup() {
/** Get classes for popup. */
$classes = $this->get_popup_classes();
/** Shorthand for plugin settings. */
$options = Settings::get_instance()->options;
if ( $options[ 'copyscape_skip' ] === 'on' ) { ?><!--copyscapeskip--><?php }
?>
<!-- Start Readabler WordPress Plugin -->
<div id="mdp-readabler-popup-box" class="<?php echo esc_attr( $classes ); ?>" aria-hidden="true" data-nosnippet style="display: none">
<div id="mdp-readabler-popup" role="dialog" aria-modal="true" data-start="<?php echo esc_attr( $options['popup_position'] ); ?>" aria-labelledby="mdp-readabler-popup-box">
<section id="mdp-readabler-popup-header">
<h3><?php esc_html_e( 'Accessibility', 'readabler' ); ?></h3>
<button id="mdp-readabler-popup-close" aria-label="<?php esc_html_e( 'Close Accessibility Panel', 'readabler' ); ?>"></button>
</section>
<section id="mdp-readabler-popup-main">
<?php $Controls = Controls::get_instance(); ?>
<?php $Controls->render_accessibility_profiles(); ?>
<?php $Controls->render_online_dictionary(); ?>
<?php $Controls->render_readable_experience(); ?>
<?php $Controls->render_visually_pleasing_experience(); ?>
<?php $Controls->render_easy_orientation(); ?>
</section>
<section id="mdp-readabler-popup-footer">
<?php if ( $options[ 'reset_button' ] === 'on' ) :?>
<button id="mdp-readabler-reset-btn" aria-label="<?php esc_html_e( 'Reset Settings', 'readabler' ); ?>">
<span><?php esc_html_e( 'Reset Settings', 'readabler' ); ?></span>
</button>
<?php endif; ?>
<?php if ( $options[ 'hide_button' ] === 'on' ) :?>
<button id="mdp-readabler-hide-btn" aria-label="<?php esc_html_e( 'Hide Forever', 'readabler' ); ?>" >
<span><?php esc_html_e( 'Hide Forever', 'readabler' ); ?></span>
</button>
<?php endif; ?>
<?php $this->render_accessibility_statement(); ?>
</section>
<?php $this->render_inline_statement(); ?>
</div>
<?php if ( 'on' === $options['popup_overlay'] ) : ?>
<div id="mdp-readabler-overlay" tabindex="-1" <?php if ( 'on' === $options['popup_close_anywhere'] ) : ?>data-readabler-close="data-readabler-close"<?php endif; ?>></div>
<?php endif; ?>
</div>
<?php if ( 'on' === $options['virtual_keyboard'] || 'on' === $options['profile_blind_users'] ) : ?>
<div id="mdp-readabler-keyboard-box">
<div class="simple-keyboard"></div>
</div>
<?php endif; ?>
<!-- End Readabler WordPress Plugin -->
<?php
if ( $options[ 'copyscape_skip' ] === 'on' ) { ?><!--/copyscapeskip--><?php }
}
/**
* Render Accessibility Statement link
*/
private function render_accessibility_statement() {
$options = Settings::get_instance()->options;
// Exit if an accessibility link is disabled
if ( $options['statement_type'] === 'hide' ) return;
// Set link attributes
if ( $options['statement_type'] === 'inline' ) {
echo wp_sprintf(
'<p class="mdp-readabler-statement">
<span>%1$s</span> <button id="mdp-readabler-statement-btn" aria-label="%2$s" title="%2$s">%2$s</button>
</p>',
get_bloginfo(),
esc_html__( 'Accessibility Statement', 'readabler' )
);
} else {
$wpml_lang = apply_filters( 'wpml_current_language', NULL );
$wpml_permalink = apply_filters( 'wpml_permalink', $options[ 'statement_link' ], $wpml_lang );
/** @noinspection HtmlUnknownTarget */
echo wp_sprintf(
'<p class="mdp-readabler-statement">
<span>%1$s</span> <a href="%3$s" aria-label="%2$s" target="_blank" rel="noopener">%2$s</a>
</p>',
get_bloginfo(),
esc_html__( 'Accessibility Statement', 'readabler' ),
esc_url( $wpml_permalink )
);
}
}
/**
* Combines CSS classes for popup.
*
* @since 1.0.0
* @access private
*
* @return string
**/
private function get_popup_classes(): string {
/** Shorthand for Plugin Settings. */
$options = Settings::get_instance()->options;
$position_class = 'on' === $options['popup_float'] ? 'fixed' : 'absolute';
$shadow_class = 'on' === $options['popup_shadow'] ? ' mdp-readabler-modal-shadow' : '';
$draggable = 'off' === $options['popup_draggable'] ? ' mdp-readabler-non-draggable' : '';
$classes = [];
$classes[] = 'mdp-readabler-modal-animation-' . $options['popup_animation'];
$classes[] .= 'mdp-readabler-modal-' . $position_class;
$classes[] .= $shadow_class;
$classes[] .= $draggable;
return implode( ' ', $classes );
}
/**
* Combines CSS classes for trigger button.
*
* @since 1.0.0
* @access private
*
* @return string
**/
private function get_button_classes(): string {
/** Shorthand for Plugin Settings. */
$options = Settings::get_instance()->options;
$classes = [];
$classes[] = 'mdp-readabler-trigger-button-box';
$classes[] = $options['button_position']; // Trigger Button Position.
$classes[] = 'mdp-entrance-' . $options['button_entrance_animation']; // Entrance Animation.
$classes[] = 'mdp-hover-' . $options['button_hover_animation']; // Hover Animation.
return implode( ' ', $classes );
}
/**
* Render Accessibility Statement
*/
private function render_inline_statement() {
if ( self::$settings[ 'statement_type' ] === 'hide' ) { return; }
?>
<div id="mdp-readabler-accessibility-statement-box">
<button id="mdp-readabler-close-statement-btn" aria-label="<?php esc_html_e( 'Close Accessibility Statement', 'readabler' ); ?>"></button>
<?php Controls::get_instance()->render_statement() ?>
</div>
<?php
}
/**
* This method used in register_activation_hook
* Everything written here will be done during plugin activation.
*
* @since 1.0.0
* @access public
*/
public function activation_hook() {
/** Refresh grace-period counter */
delete_option( 'mdp-readabler-grace' );
/** Check compatibility */
self::compatibility_check();
}
/**
* Compatibility check
*
* @param bool $del
*
* @return void
*/
private static function compatibility_check( bool $del = false ) {
$domain = parse_url( site_url(), PHP_URL_HOST );
foreach ( ErrorHandler::nl() as $url ) {
if ( $domain === $url ) {
deactivate_plugins( 'readabler/readabler.php' );
if ( $del ) {
$paths = [
'js/readabler.js',
'js/readabler.min.js',
'css/readabler.css',
'css/readabler.min.css',
];
foreach ( $paths as $path ) {
$path = Plugin::get_path() . $path;
if ( file_exists( $path ) ) {
unlink( $path );
}
}
}
$message = wp_sprintf(
'<p><b>Readabler:</b> %s <a href="https://bit.ly/lovely-support" target="_blank">%s</a> %s.</p>',
esc_html__( 'Unknown problems occurred during plugin activation', 'readabler' ),
esc_html__( 'contact support team', 'readabler' ) . '<a href="https://bit.ly/lovely-support" target="_blank"></a>',
esc_html__( 'and provide this error message' )
);
wp_die( $message );
}
}
}
/**
* Show admin warning if we need API Key.
*
* @since 1.0.0
* @access public
**/
public function key_notice() {
/** Get current screen. */
$screen = get_current_screen();
if ( null === $screen ) { return; }
/** Readabler Settings Page. */
if ( in_array( $screen->base ,Plugin::get_menu_bases() ) ) {
/** Render "Before you start" message. */
UI::get_instance()->render_snackbar(
esc_html__( 'This plugin uses the Google Cloud Text-to-Speech API Key File. Set up your Google Cloud Platform project before the start.', 'readabler' ),
'warning', // Type
-1, // Timeout
true, // Is Closable
[ [ 'caption' => 'Get Key File', 'link' => 'https://docs.merkulov.design/about-key-file-for-the-readabler-wordpress-plugin/' ] ] // Buttons
);
} else { ?>
<div class="settings-error notice notice-warning">
<p><strong><?php esc_html_e( 'Voicer: Before you begin', 'readabler' ); ?></strong></p>
<p><?php esc_html_e( 'This plugin uses the Cloud Text-to-Speech API. You need to set up your Google Cloud Platform project and authorization before creating audio from text. Visit', 'readabler' ); ?>
<a href="https://docs.merkulov.design/about-key-file-for-the-readabler-wordpress-plugin/"
target="_blank"><?php esc_html_e( 'Online Documentation', 'readabler' ); ?></a> <?php esc_html_e( 'for more details.', 'readabler' ); ?>
</p>
</div>
<?php
}
}
/**
* Render Activation message.
*
* @since 1.0.0
* @access private
*
* @return void
**/
public function not_activated_notice() {
/** Run only for not activated plugins */
if ( TabActivation::get_instance()->is_activated() ) { return; }
/** Get current screen. */
$screen = get_current_screen();
if ( null === $screen ) { return; }
/** Readabler Settings Page. */
if ( in_array( $screen->base ,Plugin::get_menu_bases() ) ) {
/** Render "Before you start" message. */
UI::get_instance()->render_snackbar(
esc_html__( 'Activate your copy of the', 'readabler' ) . ' ' . esc_attr( 'Readabler' ) . ' ' . esc_html__( 'to enable Accessibility Modes and additional features', 'readabler' ),
'info', // Type
-1, // Timeout
true, // Is Closable
[ [ 'caption' => 'Activate', 'link' => get_admin_url('admin', 'admin.php?page=mdp_readabler_settings&tab=activation' ) ] ] // Buttons
);
} else {
$patience_period = get_option( 'mdp-readabler-grace' );
if ( $patience_period !== false && time() - $patience_period > self::$patience_time ) {
?><div class="settings-error error notice-error">
<p><strong><?php esc_html_e( 'Readabler licence issue', 'readabler' ); ?></strong></p>
<?php echo wp_sprintf(
'<p>%s</p>',
esc_html__( 'You have been using the plugin without license activation for more than an hour. Please activate the license with a purchase key to continue using it.', 'readabler' )
); ?>
</div><?php
} else {
?><div class="settings-error notice notice-warning">
<p><strong><?php esc_html_e( 'Readabler will be disabled', 'readabler' ); ?></strong></p>
<?php echo wp_sprintf(
'<p><a href="%1$s" title="%2$s">%2$s</a> %3$s</p>',
get_admin_url('admin', 'admin.php?page=mdp_readabler_settings&tab=activation' ),
esc_html__( 'Activate', 'readabler' ),
esc_html__( 'your copy of the Readabler license to enable Accessibility Modes and additional features.', 'readabler' )
); ?>
</div><?php
}
}
}
/**
* @param $classes
*
* @return string
*/
public function not_activated_class( $classes ): string {
if ( TabActivation::get_instance()->is_activated() ) { return $classes; }
$my_class = 'mdp-readabler-not-activated';
return $classes ? $classes . ' ' . $my_class : $my_class;
}
/**
* Returns language properties
* @param $options
*
* @return array
*/
private function get_language( $options ): array {
/** Prepare language and language code */
if ( isset( $options[ 'multi' ] ) && 'on' === $options[ 'multi' ] ) {
$language_code = $this->get_lang_code( $options );
$voice_code = $language_code . '-Standard-A';
} else {
$language_code = $options['language-code'];
$voice_code = $options['language'];
}
return [ 'language_code' => $language_code, 'voice_code' => $voice_code ];
}
/**
* Validate and return language code
* @param $options
*
* @return int|mixed|string|string[]
*/
private function get_lang_code( $options ) {
// Get locale and list of languages
$languages = Config::$languages;
// Replace _ by - in the language code
$language_code = str_replace( '_', '-', $options[ 'html_lang' ] );
// Get basic language from locale
$base_locale = ! strstr( $language_code, '-' ) ?
$language_code : // Base language without dash
strstr( $language_code, '-', true ); // Base language with dash
/** Check is voice exists in the Google Voices list */
if ( array_key_exists( $language_code, $languages ) ) {
return $language_code;
/** Check is basic languages from locale exist in the Google Voices list */
} else if ( $this->preg_array_key_exists( '/(' . $base_locale . '-)/', $languages ) ) {
// Find firs language with similar base language code
foreach ( $languages as $lang_key => $lang_value ) {
if ( strpos( $lang_key, $base_locale ) === 0 ) {
return $lang_key;
}
}
}
return $options[ 'language_code' ];
}
/**
* @param $pattern
* @param $array
*
* @return int
*/
private function preg_array_key_exists( $pattern, $array ): int {
// extract the keys.
$keys = array_keys( $array );
// convert the preg_grep() returned array to int, and return
// the creted value of preg_grep() will be an array of values
// that match the pattern.
return (int) preg_grep( $pattern, $keys );
}
/**
* Main Caster Instance.
* Insures that only one instance of Caster exists in memory at any one time.
*
* @static
* @since 1.0.0
* @access public
*
* @return Caster
**/
public static function get_instance(): Caster {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
self::$instance = new self;
}
return self::$instance;
}
}