jetpack
/
jetpack_vendor
/
automattic
/
jetpack-premium-analytics
/
src
/
class-enablement-setting.php
class-enablement-setting.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-premium-analytics/src/class-enablement-setting.php
| 1 | <?php |
| 2 | /** |
| 3 | * Public entry point for hosts that expose the dashboard's opt-in setting. |
| 4 | * |
| 5 | * @package automattic/jetpack-premium-analytics |
| 6 | */ |
| 7 | |
| 8 | namespace Automattic\Jetpack\PremiumAnalytics; |
| 9 | |
| 10 | /** |
| 11 | * Exposes the site's dashboard opt-in through core's `wp/v2/settings` route. |
| 12 | * |
| 13 | * This is the one thing the package has to serve while the dashboard is switched off, so hosts |
| 14 | * register it outside their enablement checks. Everything else lives behind |
| 15 | * {@see Dashboard_Support_Routes}, which only boots once the dashboard is already on. |
| 16 | * |
| 17 | * Reads and writes both address the stored opt-in, so this route reports the same value as any |
| 18 | * other reader of the option. An override such as our rollout sticker turns the dashboard on |
| 19 | * without touching the opt-in and deliberately does not surface here: whether the dashboard |
| 20 | * actually booted is `analytics.enabled` in script data. |
| 21 | * |
| 22 | * Writing cannot take effect in the request that writes it: Jetpack resolves the flag once, on |
| 23 | * `plugins_loaded`. Clients are expected to reload. |
| 24 | * |
| 25 | * @since 0.6.0 |
| 26 | */ |
| 27 | class Enablement_Setting { |
| 28 | |
| 29 | /** |
| 30 | * Site option holding the customer's own opt-in to the dashboard. |
| 31 | * |
| 32 | * Lives here rather than on {@see Analytics} so that registering the setting does not pull the |
| 33 | * whole dashboard class in behind it: this runs on REST requests of every kind, including ones |
| 34 | * with the dashboard switched off. Jetpack::is_premium_analytics_enabled() spells the name a |
| 35 | * third time, since it has to answer before this package is known to be loadable at all. |
| 36 | * |
| 37 | * @since 0.6.0 |
| 38 | */ |
| 39 | const ENABLED_OPTION = 'jetpack_premium_analytics_enabled'; |
| 40 | |
| 41 | /** |
| 42 | * Preferences scope the dashboard stores its per-user state under. Mirrors |
| 43 | * `routes/dashboard/hooks/constants.ts`. |
| 44 | * |
| 45 | * @since 0.6.0 |
| 46 | */ |
| 47 | const PREFERENCES_SCOPE = 'jetpack-premium-analytics/dashboard'; |
| 48 | |
| 49 | /** |
| 50 | * Preferences key the dashboard sets once a reader has been through the onboarding. Mirrors |
| 51 | * `routes/dashboard/hooks/constants.ts`. |
| 52 | * |
| 53 | * @since 0.6.0 |
| 54 | */ |
| 55 | const ONBOARDING_KEY = 'onboardingCompletedAt'; |
| 56 | |
| 57 | /** |
| 58 | * Declare the setting so core's settings route exposes it. |
| 59 | * |
| 60 | * Call on `rest_api_init`: core builds the settings route from the registered settings at |
| 61 | * priority 99, so anything later would leave the option off the route's write schema. Both |
| 62 | * hosts may call this; a repeat call re-declares the same setting and is harmless. |
| 63 | * |
| 64 | * @since 0.6.0 |
| 65 | * |
| 66 | * @return void |
| 67 | */ |
| 68 | public static function register() { |
| 69 | register_setting( |
| 70 | 'general', |
| 71 | self::ENABLED_OPTION, |
| 72 | array( |
| 73 | 'type' => 'boolean', |
| 74 | 'default' => false, |
| 75 | 'show_in_rest' => true, |
| 76 | 'sanitize_callback' => array( __CLASS__, 'sanitize_enabled' ), |
| 77 | 'description' => __( 'Whether the Premium Analytics dashboard is enabled for this site.', 'jetpack-premium-analytics-pkg' ), |
| 78 | ) |
| 79 | ); |
| 80 | add_action( 'update_option_' . self::ENABLED_OPTION, array( __CLASS__, 'reset_onboarding_on_reactivation' ), 10, 2 ); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * Store the opt-in as 0 or 1. |
| 85 | * |
| 86 | * Without this, switching the dashboard off stores `''`: core's settings controller hands |
| 87 | * `update_option()` a bare `false`, which reaches the options table as an empty string. The |
| 88 | * REST schema does not accept that as a boolean, so the next read reports `null` rather than |
| 89 | * `false`, and a later `null` write answers 500 `rest_invalid_stored_value`. |
| 90 | * |
| 91 | * @since 0.6.0 |
| 92 | * |
| 93 | * @param mixed $value Value being written. |
| 94 | * @return int |
| 95 | */ |
| 96 | public static function sanitize_enabled( $value ) { |
| 97 | return (int) rest_sanitize_boolean( $value ); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Forget that the reader switching the dashboard back on has seen the onboarding. |
| 102 | * |
| 103 | * Someone who switched off and came back gets the tour again; a first activation, a |
| 104 | * switch-off, and everyone else's preference are left alone. Riding on the option write means |
| 105 | * only writes made while the setting is registered count, which is every product path: the |
| 106 | * classic Stats banner and menu, and the dashboard's own switch-off, all go through REST. |
| 107 | * |
| 108 | * The preference lives in core's persisted preferences user meta, whose client-side layer |
| 109 | * keeps a localStorage copy and takes whichever is newer, so `_modified` moves with the change. |
| 110 | * |
| 111 | * @since 0.6.0 |
| 112 | * |
| 113 | * @param mixed $old_value Previous option value. |
| 114 | * @param mixed $value New option value. |
| 115 | * @return void |
| 116 | */ |
| 117 | public static function reset_onboarding_on_reactivation( $old_value, $value ) { |
| 118 | if ( (bool) $old_value || ! (bool) $value ) { |
| 119 | return; |
| 120 | } |
| 121 | |
| 122 | $user_id = get_current_user_id(); |
| 123 | if ( ! $user_id ) { |
| 124 | return; |
| 125 | } |
| 126 | |
| 127 | global $wpdb; |
| 128 | $meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences'; |
| 129 | $preferences = get_user_meta( $user_id, $meta_key, true ); |
| 130 | |
| 131 | if ( ! is_array( $preferences ) || ! isset( $preferences[ self::PREFERENCES_SCOPE ][ self::ONBOARDING_KEY ] ) ) { |
| 132 | return; |
| 133 | } |
| 134 | |
| 135 | unset( $preferences[ self::PREFERENCES_SCOPE ][ self::ONBOARDING_KEY ] ); |
| 136 | $preferences['_modified'] = gmdate( 'Y-m-d\TH:i:s\Z' ); |
| 137 | |
| 138 | update_user_meta( $user_id, $meta_key, $preferences ); |
| 139 | } |
| 140 | } |
| 141 |