PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-agents-manager / src / class-open-state-store.php
jetpack / jetpack_vendor / automattic / jetpack-agents-manager / src Last commit date
js 2 weeks ago class-agents-manager.php 3 days ago class-open-state-store.php 3 days ago class-sidebar-open-preservation.php 2 weeks ago class-wp-rest-agents-manager-persisted-open-state.php 1 month ago class-wp-rest-jetpack-ai-jwt.php 2 months ago
class-open-state-store.php
214 lines
1 <?php
2 /**
3 * Open_State_Store file.
4 *
5 * @package automattic/jetpack-agents-manager
6 */
7
8 namespace Automattic\Jetpack\Agents_Manager;
9
10 use Automattic\Jetpack\Connection\Client;
11 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12 use Automattic\Jetpack\Status\Host;
13
14 /**
15 * Reads and writes the Agents Manager open state.
16 *
17 * The state is a global, per-user wpcom preference behind the
18 * `/agents-manager/state` endpoint. How the server reads it depends on the site:
19 *
20 * - wpcom Simple: the preference is local, so read `calypso_preferences` directly.
21 * - WoA / self-hosted: the preference is remote, so reads/writes go through this
22 * store's local REST route, which calls wpcom over the Jetpack Connection and
23 * caches the result in a per-user transient. Latency-sensitive readers (the
24 * server-side pre-render) use that transient to skip the round-trip.
25 */
26 class Open_State_Store {
27
28 /**
29 * Transient key prefix for the cached per-user open state.
30 *
31 * @var string
32 */
33 private const TRANSIENT_PREFIX = 'agents_manager_open_state_';
34
35 /**
36 * Default state values.
37 *
38 * @var array
39 */
40 public const DEFAULTS = array(
41 'agents_manager_open' => false,
42 'agents_manager_docked' => false,
43 'agents_manager_minimized' => false,
44 'agents_manager_floating_position' => 'right',
45 'agents_manager_router_history' => null,
46 'agents_manager_last_activity' => null,
47 );
48
49 /**
50 * Fetch the open state from wpcom and refresh the cache.
51 *
52 * @return array|\WP_Error Normalized state, or WP_Error when the request fails.
53 */
54 public static function fetch() {
55 $body = Client::wpcom_json_api_request_as_user(
56 '/agents-manager/state',
57 '2',
58 array( 'method' => 'GET' )
59 );
60
61 if ( is_wp_error( $body ) ) {
62 return $body;
63 }
64
65 $response = json_decode( wp_remote_retrieve_body( $body ), true );
66 $state = self::normalize( is_array( $response ) ? $response : array() );
67
68 self::cache( $state );
69
70 return $state;
71 }
72
73 /**
74 * Persist the open state to wpcom and refresh the cache.
75 *
76 * @param array $state Partial state to update (subset of DEFAULTS keys).
77 * @return array|\WP_Error Normalized state, or WP_Error when the request fails.
78 */
79 public static function update( array $state ) {
80 $body = Client::wpcom_json_api_request_as_user(
81 '/agents-manager/state',
82 '2',
83 array( 'method' => 'POST' ),
84 array( 'state' => $state )
85 );
86
87 if ( is_wp_error( $body ) ) {
88 return $body;
89 }
90
91 $response = json_decode( wp_remote_retrieve_body( $body ), true );
92
93 if ( ! is_array( $response ) ) {
94 return new \WP_Error(
95 'invalid_response',
96 'Invalid response from WPCOM endpoint',
97 array( 'status' => 500 )
98 );
99 }
100
101 $normalized = self::normalize( $response );
102
103 self::cache( $normalized );
104
105 return $normalized;
106 }
107
108 /**
109 * Read the current user's open state from the fastest local source.
110 *
111 * For latency-sensitive callers like the server-side pre-render: Simple sites
112 * read `calypso_preferences` directly, everywhere else uses the cached
113 * transient (see the class docblock). Returns null when nothing is known yet,
114 * so callers can skip pre-rendering until the frontend sets the real state.
115 *
116 * @return array|null `{ agents_manager_open, agents_manager_docked }` or null.
117 */
118 public static function get_cached() {
119 $user_id = get_current_user_id();
120 if ( ! $user_id ) {
121 return null;
122 }
123
124 // Simple sites have the preference locally, so read it directly (the
125 // transient is never primed there).
126 if ( ( new Host() )->is_wpcom_simple() && function_exists( '\get_user_attribute' ) ) {
127 $calypso_prefs = \get_user_attribute( $user_id, 'calypso_preferences' );
128 if ( ! is_array( $calypso_prefs ) ) {
129 return null;
130 }
131
132 return array(
133 'agents_manager_open' => (bool) ( $calypso_prefs['agents_manager_open'] ?? false ),
134 'agents_manager_docked' => (bool) ( $calypso_prefs['agents_manager_docked'] ?? false ),
135 );
136 }
137
138 // The cached state is only as good as the connection that produced it.
139 // Without a user connection the frontend cannot fetch or refresh state
140 // (the app never mounts), so a pre-render from a stale transient would
141 // flash a shell nothing ever takes down.
142 if ( ! ( new Connection_Manager() )->is_user_connected( $user_id ) ) {
143 return null;
144 }
145
146 $cached = get_transient( self::cache_key( $user_id ) );
147
148 return is_array( $cached ) ? $cached : null;
149 }
150
151 /**
152 * Normalize a raw endpoint response into the full state shape.
153 *
154 * @param array $response Raw decoded response.
155 * @return array Normalized state with all DEFAULTS keys present.
156 */
157 private static function normalize( array $response ): array {
158 return array(
159 'agents_manager_open' => (bool) ( $response['agents_manager_open'] ?? self::DEFAULTS['agents_manager_open'] ),
160 'agents_manager_docked' => (bool) ( $response['agents_manager_docked'] ?? self::DEFAULTS['agents_manager_docked'] ),
161 'agents_manager_minimized' => (bool) ( $response['agents_manager_minimized'] ?? self::DEFAULTS['agents_manager_minimized'] ),
162 'agents_manager_floating_position' => $response['agents_manager_floating_position'] ?? self::DEFAULTS['agents_manager_floating_position'],
163 'agents_manager_router_history' => $response['agents_manager_router_history'] ?? self::DEFAULTS['agents_manager_router_history'],
164 'agents_manager_last_activity' => $response['agents_manager_last_activity'] ?? self::DEFAULTS['agents_manager_last_activity'],
165 );
166 }
167
168 /**
169 * Cache the open/docked bits in a per-user transient.
170 *
171 * Only used on the remote (WoA / self-hosted) path — it's what get_cached()
172 * reads there. Simple sites read `calypso_preferences` directly and skip this.
173 *
174 * @param array $state Normalized state.
175 */
176 private static function cache( array $state ): void {
177 $user_id = get_current_user_id();
178 if ( ! $user_id ) {
179 return;
180 }
181
182 /**
183 * Filter how long the cached open state lives.
184 *
185 * It's refreshed on every read/write through this store, so the TTL mainly
186 * caps how long a value changed elsewhere (e.g. in Calypso) stays stale.
187 *
188 * @since 0.4.0
189 *
190 * @param int $ttl Cache lifetime in seconds.
191 */
192 $ttl = (int) apply_filters( 'agents_manager_open_state_cache_ttl', WEEK_IN_SECONDS );
193
194 set_transient(
195 self::cache_key( $user_id ),
196 array(
197 'agents_manager_open' => (bool) ( $state['agents_manager_open'] ?? false ),
198 'agents_manager_docked' => (bool) ( $state['agents_manager_docked'] ?? false ),
199 ),
200 $ttl
201 );
202 }
203
204 /**
205 * Build the per-user transient key.
206 *
207 * @param int $user_id User ID.
208 * @return string
209 */
210 private static function cache_key( int $user_id ): string {
211 return self::TRANSIENT_PREFIX . $user_id;
212 }
213 }
214