' . esc_html__( 'Settings', 'ariconsent' ) . '' ); return $links; } /** * Registers the option with a merge-then-sanitize callback (each tab posts only its own fields). */ public static function register_setting() { register_setting( 'ariconsent', Settings::OPTION, array( 'type' => 'array', 'sanitize_callback' => array( self::class, 'sanitize_option' ), 'default' => array(), ) ); } /** * Merges a tab's fields over the current settings, then sanitizes. * * @param mixed $input Posted fields. * @return array */ public static function sanitize_option( $input ) { $input = is_array( $input ) ? $input : array(); $current = Settings::get(); if ( isset( $input['custom_services'] ) && is_array( $input['custom_services'] ) ) { $input['custom_services'] = array_values( array_filter( $input['custom_services'], static function ( $row ) { return is_array( $row ) && empty( $row['delete'] ) && ! empty( $row['name'] ); } ) ); } $merged = array_replace( $current, $input ); if ( isset( $input['texts'] ) && is_array( $input['texts'] ) ) { $merged['texts'] = array_replace( $current['texts'], $input['texts'] ); } Settings::flush(); return Settings::sanitize( $merged ); } /** * Admin assets for our page only. * * @param string $hook Screen hook. */ public static function assets( $hook ) { if ( 'toplevel_page_' . self::SLUG !== $hook ) { return; } wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_style( 'ariconsent-admin', ARICONSENT_URL . 'assets/css/admin.css', array(), ARICONSENT_VERSION ); wp_enqueue_script( 'ariconsent-admin', ARICONSENT_URL . 'assets/js/admin.js', array( 'wp-color-picker' ), ARICONSENT_VERSION, true ); } /** * Current tab. * * @return string */ private static function tab() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Navigation only. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'general'; return in_array( $tab, self::TABS, true ) ? $tab : 'general'; } /** * Renders the page. */ public static function render() { if ( ! current_user_can( Plugin::capability() ) ) { wp_die( esc_html__( 'You do not have permission to manage AriConsent.', 'ariconsent' ) ); } $tab = self::tab(); $labels = array( 'general' => __( 'General', 'ariconsent' ), 'banner' => __( 'Banner', 'ariconsent' ), 'services' => __( 'Services', 'ariconsent' ), 'google' => __( 'Google & Microsoft', 'ariconsent' ), 'log' => __( 'Consent log', 'ariconsent' ), ); ?>

__( 'Scan finished. Services found on your site are marked "Found".', 'ariconsent' ), 'scan_failed' => __( 'The scan could not load your site. Check that the site is reachable from the server.', 'ariconsent' ), 'reconsent' => __( 'Done. Every visitor will be asked for consent again on their next visit.', 'ariconsent' ), 'policy_failed' => __( 'The cookie policy page could not be created. Try again, or add the [ariconsent_declaration] shortcode to a page yourself.', 'ariconsent' ), 'imported' => __( 'Settings imported. Check the banner and services before you rely on them.', 'ariconsent' ), 'import_failed' => __( 'The file could not be imported. Choose a settings file exported from AriConsent.', 'ariconsent' ), ); if ( isset( $messages[ $notice ] ) ) { $class = in_array( $notice, array( 'scan_failed', 'policy_failed', 'import_failed' ), true ) ? 'notice-error' : 'notice-success'; echo '

' . esc_html( $messages[ $notice ] ) . '

'; } settings_errors( Settings::OPTION ); } /** * Renders a settings tab. * * @param string $tab Tab. */ private static function render_form( $tab ) { $s = Settings::get(); $name = Settings::OPTION; if ( 'general' === $tab ) { self::health_box(); } ?>
', esc_attr( $name ), esc_attr( $id ), checked( $checked, true, false ), esc_html( $label ) ); } /** * General tab. * * @param array $s Settings. * @param string $name Option name. */ private static function tab_general( array $s, $name ) { ?>

' . esc_html__( 'string translations', 'ariconsent' ) . '' ); ?>

__( 'Heading', 'ariconsent' ), 'message' => __( 'Message', 'ariconsent' ), 'accept' => __( 'Accept button', 'ariconsent' ), 'reject' => __( 'Reject button', 'ariconsent' ), 'customize' => __( 'Settings button', 'ariconsent' ), 'save' => __( 'Save button', 'ariconsent' ), ); foreach ( $fields as $key => $label ) : $id = 'ariconsent-text-' . $key; ?>

$service ) : $state = isset( $s['services'][ $id ] ) ? $s['services'][ $id ] : array(); $enabled = ! isset( $state['enabled'] ) || $state['enabled']; $category = ! empty( $state['category'] ) ? $state['category'] : $service['category']; $field = $name . '[services][' . $id . ']'; ?>
>

$row ) : $field = $name . '[custom_services][' . $i . ']'; $get = static function ( $key ) use ( $row ) { return isset( $row[ $key ] ) ? $row[ $key ] : ''; }; $cookies = ''; foreach ( is_array( $get( 'cookies' ) ) ? $get( 'cookies' ) : array() as $cookie ) { $cookies .= implode( ' | ', (array) $cookie ) . "\n"; } $is_new = empty( $row ); ?>

__( 'OK', 'ariconsent' ), 'recommended' => __( 'Recommended', 'ariconsent' ), 'critical' => __( 'Fix now', 'ariconsent' ), ); $checks = \AriConsent\Health::checks(); usort( $checks, static function ( $a, $b ) { $order = array( 'critical' => 0, 'recommended' => 1, 'good' => 2, ); return $order[ $a['status'] ] - $order[ $b['status'] ]; } ); ?>

0 && $size <= 512 * KB_IN_BYTES ) { $json = file_get_contents( $tmp ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local uploaded file. if ( false !== $json && ! is_wp_error( Settings::import_data( json_decode( $json, true ) ) ) ) { $notice = 'imported'; } } wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG . '&ariconsent_notice=' . $notice ) ); exit; } /** * "Ask everyone again" box. * * @param array $s Settings. */ private static function reconsent_box( array $s ) { ?>

[ariconsent_declaration] –

[ariconsent_settings] –

$page, 'per_page' => 50, 'consent_id' => $search, ) ); $stats = ConsentLog::stats( 30 ); $total = array_sum( $stats ); $shares = self::percentages( $stats ); $labels = array( 'accept_all' => __( 'Accepted all', 'ariconsent' ), 'reject_all' => __( 'Rejected all', 'ariconsent' ), 'custom' => __( 'Chose categories', 'ariconsent' ), ); ?>

1 ) { echo '
'; echo wp_kses_post( paginate_links( array( 'base' => add_query_arg( 'paged', '%#%' ), 'format' => '', 'current' => $page, 'total' => $pages, ) ) ); echo '
'; } } /** * Whole-number percentages that always add up to 100 (largest remainder method). * * @param array $counts Counts by key. * @return array */ public static function percentages( array $counts ) { $total = array_sum( $counts ); $out = array_fill_keys( array_keys( $counts ), 0 ); if ( $total <= 0 ) { return $out; } $rest = array(); foreach ( $counts as $key => $count ) { $exact = 100 * $count / $total; $out[ $key ] = (int) floor( $exact ); $rest[ $key ] = $exact - $out[ $key ]; } arsort( $rest ); $left = 100 - array_sum( $out ); foreach ( array_keys( $rest ) as $key ) { if ( $left <= 0 ) { break; } ++$out[ $key ]; --$left; } return $out; } /** * Shows a stored snapshot: the exact banner a visitor saw. * * @param array $snap Snapshot from Snapshots::get(). */ private static function render_snapshot( array $snap ) { $c = $snap['content']; ?>


    $cat ) : ?>
  • – ' . esc_html( implode( ', ', $names ) ) . ''; } ?>
' . esc_html( 'publish' === $status ? __( 'Edit your cookie policy page', 'ariconsent' ) : __( 'Review and publish your cookie policy page', 'ariconsent' ) ) . '

'; return; } if ( current_user_can( 'publish_pages' ) ) { echo '

' . esc_html__( 'Create a cookie policy page', 'ariconsent' ) . ' ' . esc_html__( 'A draft in your site language with the list of services and a button to change the choice. You review it before it is published.', 'ariconsent' ) . '

'; } } /** * Link that creates the draft cookie policy page. * * @return string */ public static function create_policy_url() { return wp_nonce_url( admin_url( 'admin-post.php?action=ariconsent_create_policy' ), 'ariconsent_create_policy' ); } /** * Creates the draft cookie policy page and opens it in the editor. */ public static function handle_create_policy() { if ( ! current_user_can( Plugin::capability() ) || ! current_user_can( 'publish_pages' ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'ariconsent' ), 403 ); } check_admin_referer( 'ariconsent_create_policy' ); $id = \AriConsent\PolicyPage::create(); if ( is_wp_error( $id ) ) { wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG . '&ariconsent_notice=policy_failed' ) ); exit; } wp_safe_redirect( add_query_arg( 'ariconsent_notice', 'policy_created', get_edit_post_link( $id, 'raw' ) ) ); exit; } /** * Reminds the owner, in the editor, to review the generated page before publishing. */ public static function policy_draft_notice() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Display only. if ( ! isset( $_GET['ariconsent_notice'] ) || 'policy_created' !== $_GET['ariconsent_notice'] ) { return; } echo '

' . esc_html__( 'AriConsent created this cookie policy as a draft. Read it, adjust it to your website and publish it. The list of services updates itself.', 'ariconsent' ) . '

'; } /** * Bumps the policy revision so every visitor is asked again. */ public static function handle_reconsent() { if ( ! current_user_can( Plugin::capability() ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'ariconsent' ), 403 ); } check_admin_referer( 'ariconsent_reconsent' ); $settings = Settings::get(); $settings['revision'] = (int) $settings['revision'] + 1; Settings::update( $settings ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG . '&ariconsent_notice=reconsent' ) ); exit; } /** * Streams the consent log as CSV. */ public static function handle_export() { if ( ! current_user_can( Plugin::capability() ) ) { wp_die( esc_html__( 'You do not have permission to do this.', 'ariconsent' ), 403 ); } check_admin_referer( 'ariconsent_export' ); nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=ariconsent-log-' . gmdate( 'Y-m-d' ) . '.csv' ); $out = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- php://output stream. if ( false === $out ) { wp_die( esc_html__( 'The export could not be started.', 'ariconsent' ), 500 ); } $page = 1; $head = true; do { $batch = ConsentLog::query( array( 'page' => $page, 'per_page' => 1000, ) ); $rows = ConsentLog::csv_rows( $batch['items'] ); if ( ! $head ) { array_shift( $rows ); } foreach ( $rows as $row ) { fputcsv( $out, $row, ',', '"', '\\' ); } $head = false; $count = count( $batch['items'] ); ++$page; } while ( 1000 === $count ); fclose( $out ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- php://output stream. exit; } /** * WCAG contrast ratio between two hex colours. * * @param string $a Colour. * @param string $b Colour. * @return float */ public static function contrast( $a, $b ) { return \AriConsent\Banner::contrast( $a, $b ); } /** * A category cell in the consent log: "Yes", "No", or "Yes, except Hotjar" when the visitor * turned single services off. * * @param array $item Log item. * @param string $category Category slug. * @return string */ public static function log_cell( array $item, $category ) { if ( empty( $item['categories'][ $category ] ) ) { return __( 'No', 'ariconsent' ); } $active = Services::active(); $names = array(); foreach ( isset( $item['services_off'] ) ? (array) $item['services_off'] : array() as $id ) { if ( isset( $active[ $id ] ) && $category === $active[ $id ]['category'] ) { $names[] = $active[ $id ]['name']; } } if ( ! $names ) { return __( 'Yes', 'ariconsent' ); } /* translators: %s: comma-separated service names the visitor turned off, e.g. "Hotjar, Microsoft Clarity". */ return sprintf( __( 'Yes, except %s', 'ariconsent' ), implode( ', ', $names ) ); } }