← All changes
|
jetpack_vendor/automattic/jetpack-premium-analytics/src/class-enablement-setting.php
+140
-0
16.2-beta
→
16.3
View file →
| @@ -1,0 +1,140 @@ | ||
| 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 | +} | |