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,
64        // and WordPress VIP sites only.
65        if ( ! \Jetpack_AI_Helper::is_enabled_for_content_guidelines() ) {
66            return;
67        }
68
69        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
70    }
71
72    /**
73     * Register routes.
74     */
75    public function register_routes() {
76        register_rest_route(
77            $this->namespace,
78            '/' . $this->rest_base,
79            array(
80                array(
81                    'methods'             => WP_REST_Server::EDITABLE,
82                    'callback'            => array( $this, 'set_dismissed' ),
83                    'permission_callback' => array( $this, 'permission_callback' ),
84                ),
85            )
86        );
87    }
88
89    /**
90     * Permission check.
91     *
92     * Gated to the same capability as the Content Guidelines page (and the
93     * suggest-guidelines endpoint): only admins ever see the banner, so only
94     * they need to dismiss it.
95     *
96     * @return bool
97     */
98    public function permission_callback() {
99        return current_user_can( 'manage_options' );
100    }
101
102    /**
103     * Whether the current user has dismissed the banner.
104     *
105     * Back-compat alias: the admin-page preload reads the flag via
106     * Jetpack_AI_Helper::is_guidelines_banner_dismissed() instead, because
107     * this class is not loaded during admin page loads on Simple sites.
108     *
109     * @return bool
110     */
111    public static function is_dismissed() {
112        return Jetpack_AI_Helper::is_guidelines_banner_dismissed();
113    }
114
115    /**
116     * Mark the banner as dismissed for the current user.
117     *
118     * Dismissal is one-way — the banner has no "show again" control — so this
119     * only ever sets the flag.
120     *
121     * @return WP_REST_Response
122     */
123    public function set_dismissed() {
124        update_user_meta( get_current_user_id(), self::META_KEY, '1' );
125
126        // Just set above — return it directly instead of re-reading the meta.
127        return rest_ensure_response( array( 'dismissed' => true ) );
128    }
129}
130
131wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Guidelines_Banner_Dismissed' );