PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.8.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.8.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
← All changes | includes/Utils/Options.php +45 -50 3.7.43.8.0 View file →
@@ -9,20 +9,8 @@
9 9 use function get_current_user_id;
10 10
11 11 class Options extends Base {
12 12 /**
13 - * Current User Id
14 - * @var integer
15 - */
16 - private $current_user;
17 -
18 - /**
19 - * User has any api?
20 - * @var boolean
21 - */
22 - private $has_api;
23 -
24 - /**
25 13 * Pin every derived read/write to the acting user, bypassing the
26 14 * global-login fallback in user_id().
27 15 *
28 16 * @var bool
@@ -29,33 +17,38 @@
29 17 */
30 18 private $force_current_user = false;
31 19
32 20 /**
33 - * Automatically invoked and set up the properties.
34 - */
35 - public function __construct(){
36 - $this->current_user = get_current_user_id();
37 - $this->has_api = ! empty( $this->get( 'api_key', '', $this->current_user ) );
38 - }
39 -
40 - /**
41 21 * Get the current user ID.
42 22 *
43 - * `get_current_user_id()` can still be 0 when this singleton is built — a
44 - * wp-cli run before `wp_set_current_user()`, or a request whose auth has not
45 - * been resolved yet. Caching that 0 for the rest of the request made every
46 - * derived write bail in `can_write()`, so a connect could answer "Site
47 - * connected successfully" and persist nothing. Re-read while it is still
48 - * unknown; a resolved id is never overwritten.
23 + * Resolved live on every call rather than cached at construction time:
24 + * `Options` is a request-lifetime singleton (`Base::get_instance()`), and
25 + * something in Templately's own bootstrap (e.g. `Admin`/`Settings`)
26 + * constructs it during `plugins_loaded` — before WordPress resolves the
27 + * REST Application-Password current user (which only happens later, when
28 + * the REST server actually dispatches the route). Caching `
29 + * get_current_user_id()` at construction froze it at `0` for the rest of
30 + * the request on any headless (non-cookie) REST/MCP call, so every later
31 + * `Options` read looked up user `0`'s meta instead of the real acting
32 + * user's — reporting "session expired" even for a genuinely connected
33 + * account. `get_current_user_id()` is itself cheap (it just reads WP
34 + * core's own already-resolved `$current_user` global), so there is no
35 + * reason to cache it a second time here.
49 36 *
50 37 * @return int
51 38 */
52 39 public function current_user_id(): int {
53 - if ( $this->current_user <= 0 ) {
54 - $this->current_user = get_current_user_id();
55 - }
40 + return get_current_user_id();
41 + }
56 42
57 - return $this->current_user;
43 + /**
44 + * Whether the current user has an API key set — computed live for the
45 + * same reason as {@see current_user_id()}, not cached.
46 + *
47 + * @return boolean
48 + */
49 + private function has_api(): bool {
50 + return ! empty( $this->get( 'api_key', '', $this->current_user_id() ) );
58 51 }
59 52
60 53 /**
61 54 * Can a user link another templately account in a setup?.
@@ -61,13 +54,13 @@
61 54 * Can a user link another templately account in a setup?.
62 55 * @return boolean
63 56 */
64 57 public function link_account(): bool {
65 - return $this->who_am_i() === 'link' && ! $this->has_api;
58 + return $this->who_am_i() === 'link' && ! $this->has_api();
66 59 }
67 60
68 61 public function unlink_account(): bool {
69 - return ( $this->who_am_i() === 'link' || $this->who_am_i() === 'local' ) && $this->has_api;
62 + return ( $this->who_am_i() === 'link' || $this->who_am_i() === 'local' ) && $this->has_api();
70 63 }
71 64
72 65 /**
73 66 * Get determined who am I.
@@ -74,14 +67,15 @@
74 67 * @return string
75 68 */
76 69 public function who_am_i(): string {
77 70 $_who_am_i = 'local';
71 + $current_user = $this->current_user_id();
78 72
79 - if( $this->is_global() > 0 && $this->is_global() === $this->current_user_id() ) {
73 + if( $this->is_global() > 0 && $this->is_global() === $current_user ) {
80 74 $_who_am_i = 'global';
81 75 }
82 76
83 - if( $this->is_global() > 0 && $this->is_global() !== $this->current_user_id() ) {
77 + if( $this->is_global() > 0 && $this->is_global() !== $current_user ) {
84 78 $_who_am_i = 'link';
85 79 }
86 80
87 81 if( $this->is_global() == 0 ) {
@@ -114,15 +108,23 @@
114 108
115 109 $_who_am_i = $this->who_am_i();
116 110
117 111 if( ! empty( $_SERVER['REQUEST_URI'] ) ) {
118 - $parse_uri = explode( '/', substr( $_SERVER['REQUEST_URI'], 0, strpos( $_SERVER['REQUEST_URI'], '?' ) ) );
119 - if( $_who_am_i === 'link' && array_pop( $parse_uri ) === 'login' ) {
112 + // FR-003: in 'link' mode the login-endpoint override targets the
113 + // current user so they can store their own credentials. Detect it
114 + // with a substring match on '/login' so REST paths such as
115 + // `/wp-json/templately/v1/login` are covered — including requests
116 + // with NO query string, which the former last-path-segment check
117 + // (substr up to '?', which collapses to '' when there is no '?')
118 + // never matched. `strpos(...) !== false` keeps the PHP 7.4 floor
119 + // (`str_contains()` is PHP 8.0+).
120 + $uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) );
121 + if( $_who_am_i === 'link' && strpos( $uri, '/login' ) !== false ) {
120 122 return $this->current_user_id();
121 123 }
122 124 }
123 125
124 - if( $_who_am_i === 'link' && $this->has_api ) {
126 + if( $_who_am_i === 'link' && $this->has_api() ) {
125 127 return $this->current_user_id();
126 128 }
127 129
128 130 return $_who_am_i === 'local' ? $this->current_user_id() : $this->is_global();
@@ -170,15 +172,9 @@
170 172 */
171 173 public function set( $key, $value, $user_id = null ): bool {
172 174 $key = '_templately_' . $key;
173 175
174 - $updated = $this->update_user_meta( $user_id, $key, $value );
175 -
176 - if( $key === '_templately_api_key' && $updated ) {
177 - $this->has_api = true;
178 - }
179 -
180 - return $updated;
176 + return $this->update_user_meta( $user_id, $key, $value );
181 177 }
182 178
183 179 /**
184 180 * Get optional user meta or option data
@@ -189,9 +185,12 @@
189 185 */
190 186 public function get( $key, $default = false, $user_id = null ){
191 187 $key = '_templately_' . $key;
192 188 $_user_meta = $this->get_user_meta( $user_id, $key, true );
193 - return ! empty( $_user_meta ) ? $_user_meta : $default;
189 + // '' (get_user_meta) and false (get_user_option) are the MISSING sentinels —
190 + // but 0, '0' and [] are legitimate stored values and must not collapse to
191 + // the default (the same falsy-swallow bug fixed in API::get_param()).
192 + return ( '' === $_user_meta || false === $_user_meta ) ? $default : $_user_meta;
194 193 }
195 194
196 195 /**
197 196 * Remove options data or user meta
@@ -200,13 +199,9 @@
200 199 * @return Options
201 200 */
202 201 public function remove( string $key ): Options {
203 202 $key = '_templately_' . $key;
204 - $updated = $this->delete_user_meta( $key );
205 -
206 - if( $key === '_templately_api_key' && $updated ) {
207 - $this->has_api = false;
208 - }
203 + $this->delete_user_meta( $key );
209 204
210 205 return $this;
211 206 }
212 207