Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 5
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 register_routes
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 permission_callback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_dismissed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 set_dismissed
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * REST API endpoint for the Content Guidelines AI empty-state banner.
4 *
5 * Stores a per-user flag (so it persists across the user's devices/browsers)
6 * for whether the banner has been dismissed, instead of relying on per-browser
7 * localStorage. Modeled on the wpcom block-editor "recommended tags modal
8 * dismissed" flow, but scoped to the user via user meta.
9 *
10 * @package automattic/jetpack
11 */
12
13if ( ! defined( 'ABSPATH' ) ) {
14    exit( 0 );
15}
16
17/**
18 * Class WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed
19 *
20 * @since 16.0
21 */
22class WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed extends WP_REST_Controller {
23    /**
24     * User meta key storing the dismissed flag.
25     *
26     * @var string
27     */
28    const META_KEY = 'jetpack_content_guidelines_ai_banner_dismissed';
29
30    /**
31     * Namespace prefix.
32     *
33     * @var string
34     */
35    public $namespace = 'wpcom/v2';
36
37    /**
38     * Endpoint base route.
39     *
40     * @var string
41     */
42    public $rest_base = 'jetpack-ai/guidelines-banner-dismissed';
43
44    /**
45     * Constructor.
46     */
47    public function __construct() {
48        $this->is_wpcom                     = true;
49        $this->wpcom_is_wpcom_only_endpoint = true;
50
51        if ( ! class_exists( 'Jetpack_AI_Helper' ) ) {
52            require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-ai-helper.php';
53        }
54
55        // Match the suggest-guidelines endpoint: register on Simple/Atomic only.
56        if ( ! \Jetpack_AI_Helper::is_enabled() ) {
57            return;
58        }
59
60        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
61    }
62
63    /**
64     * Register routes.
65     */
66    public function register_routes() {
67        register_rest_route(
68            $this->namespace,
69            '/' . $this->rest_base,
70            array(
71                array(
72                    'methods'             => WP_REST_Server::EDITABLE,
73                    'callback'            => array( $this, 'set_dismissed' ),
74                    'permission_callback' => array( $this, 'permission_callback' ),
75                ),
76            )
77        );
78    }
79
80    /**
81     * Permission check.
82     *
83     * Gated to the same capability as the Content Guidelines page (and the
84     * suggest-guidelines endpoint): only admins ever see the banner, so only
85     * they need to dismiss it.
86     *
87     * @return bool
88     */
89    public function permission_callback() {
90        return current_user_can( 'manage_options' );
91    }
92
93    /**
94     * Whether the current user has dismissed the banner.
95     *
96     * Also used to preload the initial value into the page (see
97     * _inc/content-guidelines-ai.php) so the banner does not flash on load.
98     *
99     * @return bool
100     */
101    public static function is_dismissed() {
102        return (bool) get_user_meta( get_current_user_id(), self::META_KEY, true );
103    }
104
105    /**
106     * Mark the banner as dismissed for the current user.
107     *
108     * Dismissal is one-way — the banner has no "show again" control — so this
109     * only ever sets the flag.
110     *
111     * @return WP_REST_Response
112     */
113    public function set_dismissed() {
114        update_user_meta( get_current_user_id(), self::META_KEY, '1' );
115
116        // Just set above — return it directly instead of re-reading the meta.
117        return rest_ensure_response( array( 'dismissed' => true ) );
118    }
119}
120
121wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed' );