true ) ); wp_localize_script( 'fammro-admin', 'fammroAdmin', array( 'confirmDisconnect' => __( 'Disconnect this website from FAMMRO? The chat window disappears right away', 'fammro' ) ) ); } /** * Link to the settings in the plugin list. * * @param string[] $links Links. * @return string[] */ public static function action_links( array $links ): array { array_unshift( $links, sprintf( '%s', esc_url( self::page_url() ), esc_html__( 'Settings', 'fammro' ) ) ); return $links; } /** * Hint on the plugin list until the website is connected. */ public static function plugins_notice(): void { $screen = get_current_screen(); if ( null === $screen || 'plugins' !== $screen->id || ! current_user_can( self::CAPABILITY ) || null !== Store::connection() ) { return; } printf( '

%s %s

', esc_html__( 'FAMMRO is installed. Connect your website to show the chat window', 'fammro' ), esc_url( self::page_url() ), esc_html__( 'Connect now', 'fammro' ) ); } /** * Start the connection: redirect to the approval in the FAMMRO console. */ public static function handle_connect(): void { self::guard( 'fammro_connect' ); $url = Connection::authorize_url(); if ( '' === $url ) { self::back( Connection::blocker() ); } wp_safe_redirect( $url ); exit; } /** * Return from the console with the one time code. */ public static function handle_callback(): void { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to manage FAMMRO on this website', 'fammro' ), 403 ); } // No nonce possible on a redirect from FAMMRO: the single use state, bound to this user, protects the request. $code = isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended self::forget_remote(); self::back( Connection::complete( $code, $state ) ); } /** * Save settings. */ public static function handle_save(): void { self::guard( 'fammro_save' ); $before = Store::settings(); $enabled = isset( $_POST['fammro_enabled'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Checked in guard(). $shop = isset( $_POST['fammro_shop_data'] ) && WooCommerce::available(); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Checked in guard(). $excluded = isset( $_POST['fammro_excluded'] ) ? Store::ids( (array) wp_unslash( $_POST['fammro_excluded'] ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Checked in guard(), sanitized by Store::ids(). Store::save_settings( array( 'enabled' => $enabled, 'excluded' => $excluded, 'shop_data' => $shop, ) ); if ( $before['enabled'] !== $enabled ) { Connection::report_enabled( $enabled ); } $notice = 'saved'; if ( $shop && ! WooCommerce::shared() ) { $notice = WooCommerce::share(); if ( 'shop_connected' !== $notice ) { Store::save_settings( array( 'shop_data' => false ) ); } } elseif ( ! $shop && WooCommerce::shared() ) { WooCommerce::revoke_key(); } self::forget_remote(); self::back( $notice ); } /** * Disconnect. */ public static function handle_disconnect(): void { self::guard( 'fammro_disconnect' ); Connection::disconnect(); self::forget_remote(); self::back( 'disconnected' ); } /** * Render the settings page. */ public static function render(): void { if ( ! current_user_can( self::CAPABILITY ) ) { return; } $connection = Store::connection(); $settings = Store::settings(); $remote = null === $connection ? null : self::remote(); $revoked = is_array( $remote ) && ( $remote['status'] ?? 'active' ) !== 'active'; $domain = (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ); ?>

FAMMRO

%s

', esc_html( self::message( $blocker ) ) ); return; } ?>
%s

', $quiet ? '' : ' fammro-notice-warn', esc_html( $text ) ); } /** * Fixed messages. * * @param string $code Code. */ private static function message( string $code ): string { $messages = array( 'connected' => __( 'Your website is connected. The chat window is live', 'fammro' ), 'saved' => __( 'Settings saved', 'fammro' ), 'disconnected' => __( 'Your website is disconnected. The chat window has been removed', 'fammro' ), 'expired' => __( 'The approval has expired. Please connect again', 'fammro' ), 'invalid' => __( 'The response from FAMMRO was incomplete. Please connect again', 'fammro' ), 'failed' => __( 'FAMMRO could not confirm the connection. Please try again', 'fammro' ), 'unreachable' => __( 'FAMMRO could not be reached. Please try again in a moment', 'fammro' ), 'limit' => __( 'Your FAMMRO license has reached its website limit', 'fammro' ), 'shop_connected' => __( 'Products and orders are now available to the bot', 'fammro' ), 'shop_failed' => __( 'Products and orders could not be shared. Please try again', 'fammro' ), 'shop_unavailable' => __( 'WooCommerce is not active on this website', 'fammro' ), 'https' => __( 'Your website needs HTTPS to connect with FAMMRO', 'fammro' ), 'origin' => __( 'The website address and the WordPress address use different hosts. Please connect this website from the FAMMRO console', 'fammro' ), 'config' => __( 'The FAMMRO address in wp-config.php must start with https', 'fammro' ), ); return $messages[ $code ] ?? ''; } /** * Published pages for the exclusion list, plus already excluded entries of other types. * * @param int[] $excluded Excluded ids. * @return array */ private static function pages( array $excluded ): array { $list = array(); $pages = get_pages( array( 'post_status' => 'publish', 'sort_column' => 'post_title', 'number' => 500, ) ); foreach ( is_array( $pages ) ? $pages : array() as $page ) { $list[ (int) $page->ID ] = '' !== $page->post_title ? $page->post_title : '#' . $page->ID; } foreach ( $excluded as $post_id ) { if ( ! isset( $list[ $post_id ] ) ) { $title = get_the_title( $post_id ); $list[ $post_id ] = '' !== $title ? $title : '#' . $post_id; } } return $list; } /** * Remote state, cached for a minute so the page stays fast. * * @return array|null */ private static function remote(): ?array { $cached = get_transient( 'fammro_remote_state' ); if ( is_array( $cached ) ) { return $cached; } $state = Connection::remote_state(); if ( null !== $state ) { set_transient( 'fammro_remote_state', $state, self::REMOTE_TTL ); } return $state; } /** * Drop the cached remote state after a change. */ private static function forget_remote(): void { delete_transient( 'fammro_remote_state' ); } /** * Legal page in the language of the admin. * * @param string $page privacy or terms. */ private static function legal_url( string $page ): string { $german = str_starts_with( get_user_locale(), 'de' ); $paths = array( 'privacy' => $german ? '/datenschutz' : '/privacy', 'terms' => $german ? '/agb' : '/terms', ); return Api::console_base() . ( $paths[ $page ] ?? '/' ); } /** * Capability and nonce check for POST actions. * * @param string $action Nonce action. */ private static function guard( string $action ): void { if ( ! current_user_can( self::CAPABILITY ) ) { wp_die( esc_html__( 'You are not allowed to manage FAMMRO on this website', 'fammro' ), 403 ); } check_admin_referer( $action ); } /** * Back to the settings page with a result code. * * @param string $notice Result code. */ private static function back( string $notice ): void { wp_safe_redirect( add_query_arg( 'fammro_notice', rawurlencode( $notice ), self::page_url() ) ); exit; } /** * Settings page URL. */ private static function page_url(): string { return admin_url( 'options-general.php?page=' . self::PAGE ); } }