*/ public static function all(): array { $stored = get_option( self::OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } return array_merge( self::defaults(), $stored ); } /** * Die Vorgabewerte. * * @return array */ public static function defaults(): array { return array( 'api_key' => '', 'default_author_id' => 0, 'weekly_limit' => self::DEFAULT_WEEKLY_LIMIT, 'ai_notice_mode' => self::AI_NOTICE_AUTO, 'ai_notice_text' => '', 'purge_on_uninstall' => false, 'last_run' => 0, 'last_run_status' => '', 'activated_at' => 0, 'next_job_offset' => 0, 'limit_logged_at' => 0, ); } /** * Liefert einen einzelnen Wert. * * @param string $key Schluessel. * @param mixed $default_value Rueckfallwert, wenn der Schluessel unbekannt ist. * @return mixed */ public static function get( string $key, $default_value = null ) { $settings = self::all(); return array_key_exists( $key, $settings ) ? $settings[ $key ] : $default_value; } /** * Schreibt einzelne Werte zurueck; nicht genannte bleiben unveraendert. * * @param array $values Zu setzende Werte. * @return void */ public static function update( array $values ): void { // Frisch aus der Datenbank: get_option() cached je Prozess. Ein Lauf, der // Minuten dauert, schriebe sonst am Ende die Einstellungen von seinem // Beginn zurueck und verwuerfe, was ein Admin inzwischen geaendert hat // (Review 2026-10-20, im Test nachgestellt). wp_cache_delete( self::OPTION, 'options' ); update_option( self::OPTION, array_merge( self::all(), $values ), false ); } /** * Der API-Key, vorrangig aus der Konstante AIVOR_API_KEY. * * @return string */ public static function api_key(): string { if ( self::api_key_is_constant() ) { return AIVOR_API_KEY; } return (string) self::get( 'api_key', '' ); } /** * Ob der Key aus wp-config.php stammt und deshalb nicht editierbar ist. * * @return bool */ public static function api_key_is_constant(): bool { return defined( 'AIVOR_API_KEY' ) && is_string( AIVOR_API_KEY ) && '' !== AIVOR_API_KEY; } /** * Maskierte Fassung fuer die Oberflaeche. Der Key steht nie im Klartext * in der Oberflaeche und nie im Protokoll. * * @return string */ public static function masked_api_key(): string { $key = self::api_key(); if ( '' === $key ) { return ''; } if ( strlen( $key ) <= 8 ) { return str_repeat( '•', strlen( $key ) ); } return substr( $key, 0, 4 ) . str_repeat( '•', 12 ) . substr( $key, -4 ); } /** * Basis-URL der Aivor-API. * * Ueber die Konstante AIVOR_API_URL in der wp-config.php laesst sie sich * fuer Tests gegen ein lokales Backend umbiegen. Es gibt dafuer bewusst * kein Feld in der Oberflaeche: Ein Betreiber soll den Key nicht * versehentlich an einen fremden Server schicken koennen. * * @return string Immer mit abschliessendem Schraegstrich. */ public static function api_url(): string { if ( defined( 'AIVOR_API_URL' ) && is_string( AIVOR_API_URL ) && '' !== AIVOR_API_URL ) { return trailingslashit( AIVOR_API_URL ); } return self::DEFAULT_API_URL; } /** * Der Standard-Autor fuer Jobs ohne eigenen Autor. * * 0, solange keiner gewaehlt ist oder der gewaehlte Benutzer geloescht * wurde; adopt_default_author() setzt dann den naechsten Administrator, * der das Plugin aktiviert oder einen Job speichert. * * @return int */ public static function default_author_id(): int { $user_id = (int) self::get( 'default_author_id', 0 ); return $user_id > 0 && get_userdata( $user_id ) instanceof \WP_User ? $user_id : 0; } /** * Hoechstzahl neuer Importe je sieben Tage fuer die ganze Website. * * @return int */ public static function weekly_limit(): int { $limit = (int) self::get( 'weekly_limit', self::DEFAULT_WEEKLY_LIMIT ); return max( 1, min( self::MAX_WEEKLY_LIMIT, $limit ) ); } /** * Wann der sichtbare KI-Hinweis erscheint. * * @return string Einer der AI_NOTICE_-Werte. */ public static function ai_notice_mode(): string { $mode = (string) self::get( 'ai_notice_mode', self::AI_NOTICE_AUTO ); return in_array( $mode, array( self::AI_NOTICE_AUTO, self::AI_NOTICE_ALWAYS, self::AI_NOTICE_NEVER ), true ) ? $mode : self::AI_NOTICE_AUTO; } /** * Der vom Betreiber hinterlegte Hinweistext, leer fuer die Vorgabe. * * @return string */ public static function ai_notice_text(): string { return (string) self::get( 'ai_notice_text', '' ); } /** * Der Vorgabetext des KI-Hinweises in der Sprache der Website. * * Bewusst ohne Link und ohne Markennamen: Ein „Powered by" auf der * Website darf es laut den Regeln von WordPress.org nur auf ausdrueckliche * Zustimmung geben. * * @return string */ public static function default_ai_notice_text(): string { return __( 'This article was created with the help of AI.', 'aivor-content-import' ); } /** * Zeitstempel des letzten Laufs (0 = noch nie gelaufen). * * @return int */ public static function last_run(): int { return (int) self::get( 'last_run', 0 ); } /** * Ergebnis des letzten Laufs. * * @return string */ public static function last_run_status(): string { return (string) self::get( 'last_run_status', '' ); } /** * Haelt Zeitpunkt und Ergebnis des Laufs fest. * * @param string $status Einer der STATUS_-Werte. * @return void */ public static function record_run( string $status ): void { self::update( array( 'last_run' => time(), 'last_run_status' => $status, ) ); } /** * Merkt sich den Aktivierungszeitpunkt. * * Ohne ihn liesse sich ein toter WP-Cron nicht von einer frisch * aktivierten Installation unterscheiden. * * @return void */ public static function mark_activated(): void { if ( 0 === (int) self::get( 'activated_at', 0 ) ) { self::update( array( 'activated_at' => time() ) ); } } /** * Setzt den Standard-Autor, solange noch keiner gewaehlt ist. * * Der Benutzer, der das Plugin aktiviert oder einen Content-Job * speichert. Eine Wahl in den Einstellungen bleibt unberuehrt; die * Einstellung kennt bewusst kein „kein Autor", damit 0 nur „noch keiner * gewaehlt" bedeutet. * * @param int $user_id Benutzer. * @return void */ public static function adopt_default_author( int $user_id ): void { if ( $user_id > 0 && 0 === self::default_author_id() ) { self::update( array( 'default_author_id' => $user_id ) ); } } /** * Zeitpunkt der Aktivierung. * * @return int */ public static function activated_at(): int { return (int) self::get( 'activated_at', 0 ); } }