* @since 3.0.0 */ namespace Beyondwords\Wordpress\Component\Settings; use Beyondwords\Wordpress\Component\Settings\Fields\IntegrationMethod\IntegrationMethod; use Beyondwords\Wordpress\Component\Settings\Fields\Languages\Languages; use Beyondwords\Wordpress\Component\Settings\Fields\PreselectGenerateAudio\PreselectGenerateAudio; use Beyondwords\Wordpress\Component\Settings\Tabs\Content\Content; use Beyondwords\Wordpress\Component\Settings\Tabs\Credentials\Credentials; use Beyondwords\Wordpress\Component\Settings\Tabs\Player\Player; use Beyondwords\Wordpress\Component\Settings\Tabs\Pronunciations\Pronunciations; use Beyondwords\Wordpress\Component\Settings\Tabs\Summarization\Summarization; use Beyondwords\Wordpress\Component\Settings\Tabs\Voices\Voices; use Beyondwords\Wordpress\Component\Settings\SettingsUtils; use Beyondwords\Wordpress\Component\Settings\Sync; use Beyondwords\Wordpress\Core\Environment; /** * Settings * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * * @since 3.0.0 */ defined('ABSPATH') || exit; class Settings { public const REVIEW_NOTICE_TIME_FORMAT = '-14 days'; /** * Init * * @since 3.0.0 Introduced. * @since 5.4.0 Add plugin review notice. * @since 6.0.0 Make static. */ public static function init() { (new Credentials())::init(); (new Sync())::init(); if (SettingsUtils::hasValidApiConnection()) { (new Voices())::init(); (new Content())::init(); (new Player())::init(); (new Summarization())::init(); (new Pronunciations())::init(); } add_action('admin_menu', [self::class, 'addOptionsPage'], 1); add_action('admin_notices', [self::class, 'printMissingApiCredsWarning'], 100); add_action('admin_notices', [self::class, 'printSettingsErrors'], 200); add_action('admin_notices', [self::class, 'maybePrintPluginReviewNotice']); add_action('admin_enqueue_scripts', [self::class, 'enqueueScripts']); add_action('load-settings_page_beyondwords', [self::class, 'maybeValidateApiCreds']); add_action('rest_api_init', [self::class, 'restApiInit']); add_filter('plugin_action_links_speechkit/speechkit.php', [self::class, 'addSettingsLinkToPluginPage']); } /** * Add items to the WordPress admin menu. * * @since 3.0.0 * @since 6.0.0 Make static. */ public static function addOptionsPage(): void { // Settings > BeyondWords add_options_page( __('BeyondWords Settings', 'speechkit'), __('BeyondWords', 'speechkit'), 'manage_options', 'beyondwords', [self::class, 'createAdminInterface'] ); } /** * Validate API creds if we are on the credentials tab. * * @since 5.4.0 * @since 6.0.0 Make static. */ public static function maybeValidateApiCreds(): void { $activeTab = self::getActiveTab(); if ($activeTab === 'credentials') { SettingsUtils::validateApiConnection(); } } /** * Prints the admin interface for plugin settings. * * @since 3.0.0 * @since 4.7.0 Added tabs. * @since 6.0.0 Make static. */ public static function createAdminInterface(): void { $tabs = self::getTabs(); $activeTab = self::getActiveTab(); ?>


' . __('Settings', 'speechkit') . ''; array_unshift($links, $settingsLink); return $links; } /** * Get tabs. * * @since 4.7.0 * @since 5.2.0 Make static. * * @return array Tabs */ public static function getTabs(): array { $tabs = [ 'credentials' => __('Credentials', 'speechkit'), 'content' => __('Content', 'speechkit'), 'voices' => __('Voices', 'speechkit'), 'player' => __('Player', 'speechkit'), 'summarization' => __('Summarization', 'speechkit'), 'pronunciations' => __('Pronunciations', 'speechkit'), ]; if (! SettingsUtils::hasValidApiConnection()) { $tabs = array_splice($tabs, 0, 1); } return $tabs; } /** * Get active tab. * * @since 4.7.0 * @since 5.2.0 Make static. * * @return string Active tab */ public static function getActiveTab(): string { $tabs = self::getTabs(); if (! count($tabs)) { return ''; } $defaultTab = array_key_first($tabs); // phpcs:disable WordPress.Security.NonceVerification.Recommended if (isset($_GET['tab'])) { $tab = sanitize_text_field(wp_unslash($_GET['tab'])); } else { $tab = $defaultTab; } // phpcs:enable WordPress.Security.NonceVerification.Recommended if (!empty($tab) && array_key_exists($tab, $tabs)) { $activeTab = $tab; } else { $activeTab = $defaultTab; } return $activeTab; } /** * Print missing API creds warning. * * @since 5.2.0 * @since 6.0.0 Make static. */ public static function printMissingApiCredsWarning(): void { if (! SettingsUtils::hasApiCreds()) : ?>

%s', esc_url(admin_url('options-general.php?page=beyondwords')), esc_html__('plugin settings', 'speechkit') ) ); ?>

id) { return; } $dateActivated = get_option('beyondwords_date_activated', '2025-03-01'); $dateNoticeDismissed = get_option('beyondwords_notice_review_dismissed', ''); $showNotice = false; if (empty($dateNoticeDismissed)) { $dateActivated = strtotime($dateActivated); if ($dateActivated < strtotime(self::REVIEW_NOTICE_TIME_FORMAT)) { $showNotice = true; } } if ($showNotice) : ?>

%s', 'https://wordpress.org/support/plugin/speechkit/reviews/', esc_html__('WordPress Plugin Repo', 'speechkit') ) ); ?>

\WP_REST_Server::READABLE, 'callback' => [self::class, 'restApiResponse'], 'permission_callback' => fn() => current_user_can('edit_posts'), ]); // settings endpoint register_rest_route('beyondwords/v1', '/settings', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [self::class, 'restApiResponse'], 'permission_callback' => fn() => current_user_can('edit_posts'), ]); // dismiss review notice endpoint register_rest_route('beyondwords/v1', '/settings/notices/review/dismiss', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [self::class, 'dismissReviewNotice'], 'permission_callback' => fn() => current_user_can('manage_options'), ]); } /** * WP REST API response (required for the Gutenberg editor). * * DO NOT expose ALL settings e.g. be sure to never expose the API key. * * @since 3.0.0 * @since 3.4.0 Add pluginVersion and wpVersion. * @since 6.0.0 Make static and add integrationMethod. * * @return \WP_REST_Response */ public static function restApiResponse() { global $wp_version; return new \WP_REST_Response([ 'apiKey' => get_option('beyondwords_api_key', ''), 'integrationMethod' => IntegrationMethod::getIntegrationMethod(), 'pluginVersion' => BEYONDWORDS__PLUGIN_VERSION, 'projectId' => get_option('beyondwords_project_id', ''), 'preselect' => get_option(PreselectGenerateAudio::OPTION_NAME, PreselectGenerateAudio::DEFAULT_PRESELECT), // phpcs:ignore Generic.Files.LineLength.TooLong 'projectLanguageCode' => get_option('beyondwords_project_language_code', ''), 'projectBodyVoiceId' => get_option('beyondwords_project_body_voice_id', ''), 'restUrl' => get_rest_url(), 'wpVersion' => $wp_version, ]); } /** * Dismiss review notice. * * @since 5.4.0 * @since 6.0.0 Make static. * * @return \WP_REST_Response */ public static function dismissReviewNotice() { $success = update_option('beyondwords_notice_review_dismissed', gmdate(\DateTime::ATOM)); return new \WP_REST_Response( [ 'success' => $success ], $success ? 200 : 500 ); } /** * Register the settings script. * * @since 5.0.0 * @since 6.0.0 Make static. * * @param string $hook Page hook */ public static function enqueueScripts($hook) { if ($hook === 'settings_page_beyondwords') { // jQuery UI JS wp_enqueue_script('jquery-ui-core');// enqueue jQuery UI Core wp_enqueue_script('jquery-ui-tabs');// enqueue jQuery UI Tabs // Plugin settings JS wp_register_script( 'beyondwords-settings', BEYONDWORDS__PLUGIN_URI . 'build/settings.js', ['jquery', 'jquery-ui-core', 'jquery-ui-tabs', 'underscore', 'tom-select'], BEYONDWORDS__PLUGIN_VERSION, true ); // Tom Select JS wp_enqueue_script( 'tom-select', 'https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js', // phpcs:ignore [], '2.2.2', true ); // Plugin settings CSS wp_enqueue_style( 'beyondwords-settings', BEYONDWORDS__PLUGIN_URI . 'src/Component/Settings/settings.css', 'forms', BEYONDWORDS__PLUGIN_VERSION ); // Tom Select CSS wp_enqueue_style( 'tom-select', 'https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.css', // phpcs:ignore false, BEYONDWORDS__PLUGIN_VERSION ); /** * Localize the script to handle ajax requests */ wp_add_inline_script( 'beyondwords-settings', ' var beyondwordsData = beyondwordsData || {}; beyondwordsData.nonce = "' . wp_create_nonce('wp_rest') . '"; beyondwordsData.root = "' . esc_url_raw(rest_url()) . '"; ', 'before', ); wp_enqueue_script('beyondwords-settings'); } } }