id !== $this->hook_suffix ) { return; } if ( '' !== self::get_api_key() ) { return; } ?>

' . esc_html__( 'Sign up at rss.dynamiapps.com', 'dynamirss' ) . '' ); ?>

hook_suffix = add_menu_page( __( 'DynamiRSS', 'dynamirss' ), __( 'DynamiRSS', 'dynamirss' ), 'manage_options', 'dynamirss', array( $this, 'render_page' ), 'dashicons-rss' ); } public function register_settings(): void { register_setting( 'dynamirss', self::OPTION_KEY, array( $this, 'sanitize' ) ); } public function sanitize( $input ): array { $existing = self::get_options(); return array( 'api_key' => isset( $input['api_key'] ) ? sanitize_text_field( (string) $input['api_key'] ) : $existing['api_key'], 'cache_ttl' => isset( $input['cache_ttl'] ) ? max( 0, (int) $input['cache_ttl'] ) : $existing['cache_ttl'], 'default_limit' => isset( $input['default_limit'] ) ? max( 1, min( 200, (int) $input['default_limit'] ) ) : $existing['default_limit'], 'fetch_extras' => ! empty( $input['fetch_extras'] ), ); } /** * @return array{api_key:string,cache_ttl:int,default_limit:int,fetch_extras:bool} */ public static function get_options(): array { $defaults = array( 'api_key' => defined( 'DYNAMIRSS_API_KEY' ) ? DYNAMIRSS_API_KEY : '', 'cache_ttl' => 120, 'default_limit' => 12, 'fetch_extras' => true, ); $saved = get_option( self::OPTION_KEY, array() ); if ( ! is_array( $saved ) || empty( $saved ) ) { // Fall back to the pre-rename option so upgrading sites keep their // saved API key instead of silently reverting to defaults. $legacy = get_option( self::LEGACY_OPTION_KEY, array() ); $saved = is_array( $legacy ) ? $legacy : array(); } // The constant wins only when the saved option is empty, so the UI can // still override a constant if an admin fills the field in later. if ( empty( $saved['api_key'] ) && defined( 'DYNAMIRSS_API_KEY' ) ) { $saved['api_key'] = DYNAMIRSS_API_KEY; } return array_merge( $defaults, $saved ); } public static function get_api_url(): string { return self::API_SCAN_URL; } public static function get_categories_url(): string { return self::API_CATEGORIES_URL; } public static function get_channels_url(): string { return self::API_CHANNELS_URL; } public static function get_api_key(): string { return self::get_options()['api_key']; } public static function get_cache_ttl(): int { return self::get_options()['cache_ttl']; } public static function get_default_limit(): int { return self::get_options()['default_limit']; } public static function get_default_fetch_extras(): bool { return self::get_options()['fetch_extras']; } /** * Category vocabulary used to populate the Category dropdown in the block * editor and the frontend filter bar. Pulled live from the dynamiRSS API's * /categories endpoint (cached for an hour) rather than hand-maintained, * so it can never drift from what the /scan endpoint's category filter * actually accepts. * * @return string[] */ public static function get_categories(): array { $cached = get_transient( self::CATEGORIES_TRANSIENT ); if ( is_array( $cached ) ) { return $cached; } $result = DynamiRSS_Api_Client::instance()->fetch_categories(); if ( $result['error'] ) { // Live fetch failed — serve the last known-good list rather than // blanking out the category filter on a transient API outage. $fallback = get_option( self::CATEGORIES_CACHE_OPTION, array() ); return is_array( $fallback ) ? $fallback : array(); } set_transient( self::CATEGORIES_TRANSIENT, $result['categories'], HOUR_IN_SECONDS ); update_option( self::CATEGORIES_CACHE_OPTION, $result['categories'], false ); return $result['categories']; } /** * Channel list used to populate the block editor's channel pickers, pulled * from the dynamiRSS API's /channels endpoint and cached exactly like * get_categories() above: an hour-long transient, with a last-known-good * copy to fall back on if the live fetch fails. * * @return array */ public static function get_channels(): array { $cached = get_transient( self::CHANNELS_TRANSIENT ); if ( is_array( $cached ) ) { return $cached; } $result = DynamiRSS_Api_Client::instance()->fetch_channels(); if ( $result['error'] ) { $fallback = get_option( self::CHANNELS_CACHE_OPTION, array() ); return is_array( $fallback ) ? $fallback : array(); } set_transient( self::CHANNELS_TRANSIENT, $result['channels'], HOUR_IN_SECONDS ); update_option( self::CHANNELS_CACHE_OPTION, $result['channels'], false ); return $result['channels']; } /** * The languages the API has channels in, taken from the channel list (the * API has no endpoint of its own for them). * * @return array Language code => display name, sorted by name. */ public static function get_languages(): array { $languages = array(); foreach ( self::get_channels() as $channel ) { $code = strtolower( trim( (string) ( $channel['language'] ?? '' ) ) ); if ( '' !== $code && ! isset( $languages[ $code ] ) ) { $languages[ $code ] = self::get_language_label( $code ); } } asort( $languages ); return $languages; } /** A readable name for a language code in the site's language, or the code itself if PHP's intl extension is missing. */ public static function get_language_label( string $code ): string { $label = class_exists( 'Locale' ) ? (string) Locale::getDisplayLanguage( $code, get_locale() ) : ''; return '' !== $label && $label !== $code ? $label : strtoupper( $code ); } public function render_page(): void { if ( ! current_user_can( 'manage_options' ) ) { return; } $o = self::get_options(); ?>