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(
'
',
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 );
?>
%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 );
}
}