%s', esc_url( admin_url( 'options-general.php?page=' . self::PAGE_SLUG ) ), esc_html__( 'Settings', 'speechkit' ) ); array_unshift( $links, $settings_link ); return $links; } /** * Show a banner pointing to the settings page until creds are entered. */ public static function maybe_print_missing_creds_warning(): void { if ( Utils::has_api_creds() ) { return; } ?>

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

id ) { return; } $dismissed = get_option( 'beyondwords_notice_review_dismissed', '' ); if ( ! empty( $dismissed ) ) { return; } $activated_at = strtotime( (string) get_option( 'beyondwords_date_activated', '2025-03-01' ) ); if ( false === $activated_at || $activated_at >= strtotime( self::REVIEW_NOTICE_OFFSET ) ) { return; } ?>

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

id ) { return; } $errors = get_transient( 'beyondwords_settings_errors' ); if ( ! is_array( $errors ) || empty( $errors ) ) { return; } delete_transient( 'beyondwords_settings_errors' ); $allowed = [ 'a' => [ 'href' => [], 'target' => [] ], 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'br' => [], 'code' => [], ]; ?>
\WP_REST_Server::READABLE, 'callback' => [ self::class, 'rest_settings_response' ], 'permission_callback' => static fn() => current_user_can( 'edit_posts' ), ] ); register_rest_route( 'beyondwords/v1', '/settings/notices/review/dismiss', [ 'methods' => \WP_REST_Server::CREATABLE, 'callback' => [ self::class, 'rest_dismiss_review_notice' ], 'permission_callback' => static fn() => current_user_can( 'manage_options' ), ] ); register_rest_route( 'beyondwords/v1', '/projects/(?P[0-9]+)/video-settings', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ self::class, 'rest_video_settings_response' ], 'permission_callback' => static fn() => current_user_can( 'edit_posts' ), ] ); register_rest_route( 'beyondwords/v1', '/projects/(?P[0-9]+)', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ self::class, 'rest_project_response' ], 'permission_callback' => static fn() => current_user_can( 'edit_posts' ), ] ); register_rest_route( 'beyondwords/v1', '/summarization-settings-templates', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ self::class, 'rest_summarization_settings_templates_response' ], 'permission_callback' => static fn() => current_user_can( 'edit_posts' ), ] ); register_rest_route( 'beyondwords/v1', '/video-settings-templates', [ 'methods' => \WP_REST_Server::READABLE, 'callback' => [ self::class, 'rest_video_settings_templates_response' ], 'permission_callback' => static fn() => current_user_can( 'edit_posts' ), ] ); } /** * Settings payload for editor scripts. * * Never include the API key in this response — editor scripts run in the * browser and the key must stay server-side. */ public static function rest_settings_response(): \WP_REST_Response { global $wp_version; return new \WP_REST_Response( [ 'inspectMetaKeys' => self::inspect_meta_keys(), 'integrationMethod' => Fields::get_integration_method(), 'pluginVersion' => BEYONDWORDS__PLUGIN_VERSION, 'projectId' => (string) get_option( Fields::OPTION_PROJECT_ID, '' ), 'preselect' => Preselect::get(), 'restUrl' => get_rest_url(), 'wpVersion' => $wp_version, ] ); } /** * Meta keys surfaced in the editor Inspect panel. * * Sourced from the canonical lists in \BeyondWords\Core\Utils, minus * internal-only keys that have never been shown in the panel. * * @since 7.0.0 * * @return array{current: string[], deprecated: string[]} */ private static function inspect_meta_keys(): array { $internal_only = [ 'beyondwords_player_content', 'beyondwords_player_style', 'beyondwords_title_voice_id', 'beyondwords_summary_voice_id', 'beyondwords_disabled', 'beyondwords_hash', 'speechkit_hash', 'speechkit_updated_at', ]; return [ 'current' => \BeyondWords\Core\Utils::get_post_meta_keys( 'current' ), 'deprecated' => array_values( array_diff( \BeyondWords\Core\Utils::get_post_meta_keys( 'deprecated' ), $internal_only ) ), ]; } /** * Mark the review notice as dismissed. */ public static function rest_dismiss_review_notice(): \WP_REST_Response { $saved = update_option( 'beyondwords_notice_review_dismissed', gmdate( \DateTime::ATOM ) ); return new \WP_REST_Response( [ 'success' => $saved ], $saved ? 200 : 500 ); } /** * Proxy for the BeyondWords video settings endpoint. * * Editor scripts read the `sizes` array to populate the "Video size" dropdown. * * @since 7.0.0 * * @param \WP_REST_Request $request The REST request. */ public static function rest_video_settings_response( \WP_REST_Request $request ): \WP_REST_Response { $project_id = (int) $request->get_param( 'projectId' ); $response = \BeyondWords\Api\Client::get_video_settings( $project_id ); return new \WP_REST_Response( $response ); } /** * Proxy for the BeyondWords project endpoint. * * Editor scripts read the project's default `language` for the Language dropdown. * * @since 7.0.0 * * @param \WP_REST_Request $request The REST request. */ public static function rest_project_response( \WP_REST_Request $request ): \WP_REST_Response { $project_id = (int) $request->get_param( 'projectId' ); $response = \BeyondWords\Api\Client::get_project( $project_id ); return new \WP_REST_Response( $response ); } /** * Proxy for the BeyondWords summarization settings templates endpoint. * * Editor scripts use this list to populate the "Script template" dropdown. * * @since 7.0.0 */ public static function rest_summarization_settings_templates_response(): \WP_REST_Response { $response = \BeyondWords\Api\Client::get_summarization_settings_templates(); return new \WP_REST_Response( $response ); } /** * Proxy for the BeyondWords video settings templates endpoint. * * Editor scripts use this list to populate the "Video template" dropdown. * * @since 7.0.0 */ public static function rest_video_settings_templates_response(): \WP_REST_Response { $response = \BeyondWords\Api\Client::get_video_settings_templates(); return new \WP_REST_Response( $response ); } }