PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.7.3
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.7.3
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 3.1.10 All 111 releases
← All changes | includes/API/Login.php +81 -18 3.6.23.7.3 View file →
@@ -47,21 +47,23 @@
47 47 ];
48 48 }
49 49
50 50 public function pricing(){
51 - $data = get_transient( "templately_subscriptions" );
51 + $data = get_transient( "templately_subscriptions_v2" );
52 52
53 53 if( is_array( $data ) && ! empty( $data ) ) {
54 54 return $data;
55 55 }
56 56
57 - $query = 'id, price, name, discounted_price, type, sites, coupon';
57 + // Field set drives the Subscription screen's plan comparison grid, so it
58 + // carries the per-plan limits too, not just price/sites.
59 + $query = 'id, price, name, slug, discounted_price, type, sites, coupon, my_cloud_items, pro_items, workspace, fsi_limit, ai_credit, description';
58 60 $response = $this->http()->query(
59 61 'subscriptionPlans',
60 62 $query
61 63 )->post();
62 64
63 - set_transient( "templately_subscriptions", $response, WEEK_IN_SECONDS );
65 + set_transient( "templately_subscriptions_v2", $response, WEEK_IN_SECONDS );
64 66
65 67 return $response;
66 68 }
67 69
@@ -105,9 +107,13 @@
105 107 if ( ! empty( $errors ) ) {
106 108 return $this->error( 'login_error', $errors, 'login', 400 );
107 109 }
108 110
109 - $query = 'status, message, user{ id, name, first_name, last_name, display_name, email, profile_photo, joined, is_verified, is_company_user, api_key, plan, plan_expire_at, my_cloud{ limit, usages, last_pushed }, favourites{ id, type }, show_notice, reviews{ type, type_id, rating }, subscription { id, name, sites } }';
111 + // `ends_at`, `plan_type` and `cancel_at_period_end` drive the Subscription
112 + // screen's renewal line. They are asked for instead of leaning on
113 + // `plan_expire_at`, which the API resolves with `empty($subscription->ends_at)
114 + // ?? …` — a boolean that never falls through, so it never carries a date.
115 + $query = 'status, message, user{ id, name, first_name, last_name, display_name, email, profile_photo, joined, is_verified, is_company_user, is_restricted_company_user, api_key, plan, plan_expire_at, my_cloud{ limit, usages, last_pushed }, favourites{ id, type }, show_notice, reviews{ type, type_id, rating }, subscription { id, name, sites, subscription_plan_id, ends_at, plan_type, cancel_at_period_end } }';
110 116
111 117 $response = $this->http()->mutation(
112 118 $viaAPI ? 'connectWithApiKey' : 'connect',
113 119 $query,
@@ -121,8 +127,30 @@
121 127 if ( empty( $response['user']['api_key'] ) ) {
122 128 return $this->error( 'login_error', $response['message'] ?? __('Invalid API key.', 'templately'), 'login', 400 );
123 129 }
124 130
131 + $options = $this->utils( 'options' );
132 + $options->use_current_user( true );
133 +
134 + try {
135 + return $this->store_connection( $response, $global_signin, $_ip, $_site_url );
136 + } finally {
137 + $options->use_current_user( false );
138 + }
139 + }
140 +
141 + /**
142 + * Persist an authenticated connection against the acting user.
143 + *
144 + * @param array $response Cloud response, already validated.
145 + * @param bool $global_signin Whether the user asked to sign in globally.
146 + * @param string $_ip Request IP, echoed back into the profile.
147 + * @param string $_site_url Site URL, echoed back into the profile.
148 + *
149 + * @return array
150 + */
151 + private function store_connection( $response, $global_signin, $_ip, $_site_url ) {
152 +
125 153 if ( $global_signin && ! Login::is_globally_signed() ) {
126 154 Options::set_global_login();
127 155 }
128 156
@@ -174,13 +202,28 @@
174 202 return $response;
175 203 }
176 204
177 205 public function logout() {
206 + // Read the key off the acting user's own record. Options::get() falls back to
207 + // the global-login administrator when no target is given, so $this->api_key
208 + // resolves to the administrator's key for any linked user — disconnecting the
209 + // administrator's account on the cloud as well as locally.
210 + $api_key = $this->utils( 'options' )->get( 'api_key', '', get_current_user_id() );
211 +
212 + if ( empty( $api_key ) ) {
213 + return $this->error(
214 + 'logout_error',
215 + __( 'You are not connected to Templately.', 'templately' ),
216 + 'logout',
217 + 403
218 + );
219 + }
220 +
178 221 $response = $this->http()->mutation(
179 222 'disconnect',
180 223 'status, message, data',
181 224 [
182 - 'api_key' => $this->api_key,
225 + 'api_key' => $api_key,
183 226 "site_url" => home_url( '/' )
184 227 ]
185 228 )->post();
186 229
@@ -207,22 +250,33 @@
207 250 return $response;
208 251 }
209 252
210 253 public function delete(){
211 - $this->utils( 'options' )
212 - ->remove( 'user' )
213 - ->remove( 'favourites' )
214 - ->remove( 'reviews' )
215 - ->remove( 'cloud_activity' )
216 - ->remove( 'api_key' )
217 - ->remove( 'global_login' )
218 - ->remove( 'total_download_counts' )
219 - ->remove( 'templates_in_clouds' );
254 + $options = $this->utils( 'options' );
220 255
221 - if ( $this->utils( 'options' )->who_am_i() === 'global' ) {
222 - $this->utils( 'options' )->remove_global_login();
223 - }
256 + // Pin the removals to the acting user. Without the pin, Options::user_id()
257 + // resolves a linked user to the global-login administrator and the delete
258 + // path wipes the administrator's connection instead of the caller's.
259 + $options->use_current_user( true );
224 260
261 + try {
262 + $options
263 + ->remove( 'user' )
264 + ->remove( 'favourites' )
265 + ->remove( 'reviews' )
266 + ->remove( 'cloud_activity' )
267 + ->remove( 'api_key' )
268 + ->remove( 'global_login' )
269 + ->remove( 'total_download_counts' )
270 + ->remove( 'templates_in_clouds' );
271 +
272 + if ( $options->who_am_i() === 'global' ) {
273 + $options->remove_global_login();
274 + }
275 + } finally {
276 + $options->use_current_user( false );
277 + }
278 +
225 279 $global_user_id = $this->utils( 'options' )->is_global();
226 280 $global_user = null;
227 281
228 282 if ( $global_user_id !== $this->utils( 'options' )->current_user_id() ) {
@@ -228,8 +282,12 @@
228 282 if ( $global_user_id !== $this->utils( 'options' )->current_user_id() ) {
229 283 $global_user = $this->utils( 'options' )->get( 'user', false, $global_user_id );
230 284
231 285 if ( ! empty( $global_user ) ) {
286 + if ( is_array( $global_user ) ) {
287 + unset( $global_user['api_key'] );
288 + }
289 +
232 290 $global_user['meta'] = $this->user_meta();
233 291 }
234 292 }
235 293
@@ -243,8 +301,13 @@
243 301
244 302 $_user = ( new static )->utils( 'options' )->get( 'user', null );
245 303
246 304 if ( ! is_null( $_user ) ) {
305 + // Profiles stored before 3.7.1 may still carry the cloud API key.
306 + if ( is_array( $_user ) ) {
307 + unset( $_user['api_key'] );
308 + }
309 +
247 310 $_user['meta'] = self::get_instance()->user_meta();
248 311 }
249 312
250 313 if ( empty( $_user ) ) {
@@ -277,5 +340,5 @@
277 340
278 341 public static function signed_as_global(): bool {
279 342 return rest_sanitize_boolean( ( new static )->utils( 'options' )->signed_as_global() );
280 343 }
281 -}
344 +}