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 / 20
0.00% covered (danger)
0.00%
0 / 5
42
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 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// Load before the class definition, not in the constructor: META_KEY below
18// delegates to a Jetpack_AI_Helper constant, and PHP resolves class constant
19// expressions at first instantiation, before the constructor body runs.
20if ( ! class_exists( 'Jetpack_AI_Helper' ) ) {
21    require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-ai-helper.php';
22}
23
24/**
25 * Class WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed
26 *
27 * @since 16.0
28 */
29class WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed extends WP_REST_Controller {
30    /**
31     * User meta key storing the dismissed flag.
32     *
33     * The canonical key lives on Jetpack_AI_Helper (required at the top of
34     * this file) because this class is not loaded during admin page loads on
35     * Simple sites, while the admin-page preload in
36     * _inc/content-guidelines-ai.php needs the key there.
37     *
38     * @var string
39     */
40    const META_KEY = Jetpack_AI_Helper::GUIDELINES_BANNER_DISMISSED_META_KEY;
41
42    /**
43     * Namespace prefix.
44     *
45     * @var string
46     */
47    public $namespace = 'wpcom/v2';
48
49    /**
50     * Endpoint base route.
51     *
52     * @var string
53     */
54    public $rest_base = 'jetpack-ai/guidelines-banner-dismissed';
55
56    /**
57     * Constructor.
58     */
59    public function __construct() {
60        $this->is_wpcom                     = true;
61        $this->wpcom_is_wpcom_only_endpoint = true;
62
63        // Match the suggest-guidelines endpoint: register on Simple/Atomic only.
64        if ( ! \Jetpack_AI_Helper::is_enabled() ) {
65            return;
66        }
67
68        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
69    }
70
71    /**
72     * Register routes.
73     */
74    public function register_routes() {
75        register_rest_route(
76            $this->namespace,
77            '/' . $this->rest_base,
78            array(
79                array(
80                    'methods'             => WP_REST_Server::EDITABLE,
81                    'callback'            => array( $this, 'set_dismissed' ),
82                    'permission_callback' => array( $this, 'permission_callback' ),
83                ),
84            )
85        );
86    }
87
88    /**
89     * Permission check.
90     *
91     * Gated to the same capability as the Content Guidelines page (and the
92     * suggest-guidelines endpoint): only admins ever see the banner, so only
93     * they need to dismiss it.
94     *
95     * @return bool
96     */
97    public function permission_callback() {
98        return current_user_can( 'manage_options' );
99    }
100
101    /**
102     * Whether the current user has dismissed the banner.
103     *
104     * Back-compat alias: the admin-page preload reads the flag via
105     * Jetpack_AI_Helper::is_guidelines_banner_dismissed() instead, because
106     * this class is not loaded during admin page loads on Simple sites.
107     *
108     * @return bool
109     */
110    public static function is_dismissed() {
111        return Jetpack_AI_Helper::is_guidelines_banner_dismissed();
112    }
113
114    /**
115     * Mark the banner as dismissed for the current user.
116     *
117     * Dismissal is one-way — the banner has no "show again" control — so this
118     * only ever sets the flag.
119     *
120     * @return WP_REST_Response
121     */
122    public function set_dismissed() {
123        update_user_meta( get_current_user_id(), self::META_KEY, '1' );
124
125        // Just set above — return it directly instead of re-reading the meta.
126        return rest_ensure_response( array( 'dismissed' => true ) );
127    }
128}
129
130wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed' );