PluginProbe
Image Optimization – Compress Images and Convert to WebP or AVIF / 1.6.6
Image Optimization – Compress Images and Convert to WebP or AVIF v1.6.6
1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 All 32 releases
image-optimization / modules / oauth / components / connect.php

connect.php in Image Optimization – Compress Images and Convert to WebP or AVIF 1.6.6, at modules/oauth/components/connect.php

381 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImageOptimization\Modules\Oauth\Components;
4
5 use ImageOptimization\Modules\Oauth\{
6 Classes\Route_Base,
7 Components\Exceptions\Auth_Error,
8 Classes\Data,
9 };
10 use ImageOptimization\Classes\Logger;
11 use ImageOptimization\Classes\Utils;
12 use ImageOptimization\Modules\Settings\Module as Settings_Module;
13
14 use stdClass;
15 use Throwable;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit; // Exit if accessed directly.
19 }
20
21 /**
22 * Class Connect
23 */
24 class Connect {
25 const API_URL = 'https://my.elementor.com/api/connect/v1';
26 const STATUS_CHECK_TRANSIENT = 'image_optimizer_status_check';
27
28 /**
29 * is_connected
30 * @return bool
31 */
32 public static function is_connected(): bool {
33 return ! empty( Data::get_connect_data()['access_token'] );
34 }
35
36 /**
37 * is_activated
38 * @return bool
39 */
40 public static function is_activated(): bool {
41 return ! empty( Data::get_activation_state() );
42 }
43
44 /**
45 * maybe_handle_admin_connect_page
46 * @return bool
47 */
48 public static function maybe_handle_admin_connect_page(): bool {
49 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'nonce_actionget_token' ) ) {
50 return false;
51 }
52
53 $args = [
54 'page' => 'elementor-connect',
55 'app' => 'library',
56 'action' => 'get_token',
57 'state' => Data::get_connect_state(),
58 ];
59
60 foreach ( $args as $key => $value ) {
61 if ( ! isset( $_GET[ $key ] ) || $_GET[ $key ] !== $value ) {
62 return false;
63 }
64 }
65
66 if ( ! isset( $_GET['nonce'] ) || ! isset( $_GET['code'] ) ) {
67 return false;
68 }
69
70 return true;
71 }
72
73 /**
74 * handle_elementor_connect_admin
75 */
76 public function handle_elementor_connect_admin(): void {
77 // validate args
78 if ( ! self::maybe_handle_admin_connect_page() ) {
79 return;
80 }
81
82 // validate nonce
83 if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'nonce_actionget_token' ) ) {
84 wp_die( 'Nonce verification failed', 'image-optimization' );
85 }
86
87 $token_response = wp_remote_request( self::API_URL . '/get_token', [
88 'method' => 'POST',
89 'body' => [
90 'app' => 'library',
91 'grant_type' => 'authorization_code',
92 'client_id' => Data::get_client_id(),
93 'code' => sanitize_text_field( $_GET['code'] ),
94 ],
95 ] );
96
97 if ( is_wp_error( $token_response ) ) {
98 wp_die( $token_response->get_error_message(), 'image-optimization' );
99 }
100
101 $data = json_decode( wp_remote_retrieve_body( $token_response ), true );
102 Data::set_connect_data( $data );
103
104 do_action( Checkpoint::ON_CONNECT );
105
106 // cleanup
107 Data::delete_connect_state();
108
109 wp_redirect( add_query_arg( [
110 'page' => Settings_Module::SETTING_BASE_SLUG,
111 'connected' => 'true',
112 ], admin_url( 'admin.php' ) ) );
113 die();
114 }
115
116 /**
117 * Gets required connection data from the service and generates a connect link.
118 *
119 * @return string User connect link
120 *
121 * @throws Auth_Error
122 */
123 public static function initialize_connect(): string {
124 try {
125 $response = wp_remote_request(
126 self::API_URL . '/library/get_client_id',
127 [
128 'method' => 'POST',
129 'body' => [
130 'local_id' => get_current_user_id(),
131 'site_key' => Data::get_site_key(),
132 'app' => 'library',
133 'home_url' => trailingslashit( home_url() ),
134 'source' => 'image-optimizer',
135 ],
136 ]
137 );
138 } catch ( Throwable $t ) {
139 Logger::log(
140 Logger::LEVEL_ERROR,
141 'Error while sending connection initialization request: ' . $t->getMessage()
142 );
143
144 throw new Auth_Error( $t->getMessage() );
145 }
146
147 $data = json_decode( wp_remote_retrieve_body( $response ) );
148
149 if ( ! isset( $data->client_id ) || ! isset( $data->auth_secret ) ) {
150 Logger::log(
151 Logger::LEVEL_ERROR,
152 'Invalid response from server: client id or auth secret are undefined'
153 );
154
155 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
156 }
157
158 Data::set_client_data( $data->client_id, $data->auth_secret );
159
160 return add_query_arg( [
161 'utm_source' => 'image-optimizer-panel',
162 'utm_campaign' => 'image-optimizer',
163 'utm_medium' => 'wp-dash',
164 'source' => 'generic',
165 'action' => 'authorize',
166 'response_type' => 'code',
167 'client_id' => $data->client_id,
168 'auth_secret' => $data->auth_secret,
169 'state' => Data::get_connect_state( true ),
170 'redirect_uri' => rawurlencode( add_query_arg( [
171 'page' => 'elementor-connect',
172 'app' => 'library',
173 'action' => 'get_token',
174 'nonce' => wp_create_nonce( 'nonce_action' . 'get_token' ),
175 ], admin_url( 'admin.php' ) ) ),
176 'may_share_data' => 0,
177 'reconnect_nonce' => wp_create_nonce( 'nonce_action' . 'reconnect' ),
178 ], Route_Base::SITE_URL . 'library' );
179 }
180
181 /**
182 * Disconnects a user and removes connection data from the DB.
183 */
184 public static function disconnect() {
185 do_action( Checkpoint::ON_DISCONNECT );
186
187 try {
188 return wp_remote_request( self::API_URL . '/disconnect', [
189 'method' => 'POST',
190 'body' => [
191 'app' => 'library',
192 'home_url' => trailingslashit( home_url() ),
193 'client_id' => Data::get_client_id(),
194 'access_token' => Data::get_access_token(),
195 ],
196 ] );
197 } catch ( Throwable $t ) {
198 Logger::log( Logger::LEVEL_ERROR, 'Error while sending disconnection request: ' . $t->getMessage() );
199
200 throw new Auth_Error( $t->getMessage() );
201 } finally {
202 Data::reset();
203 }
204 }
205
206 /**
207 * Sends an activation request and stores activation data in the DB.
208 *
209 * @param $license_key string License key to activate with.
210 * @return mixed
211 *
212 * @throws Auth_Error
213 */
214 public static function activate( string $license_key ) {
215 try {
216 $response = Utils::get_api_client()->make_request(
217 'POST',
218 'activation/activate',
219 [],
220 [ 'key' => $license_key ]
221 );
222 } catch ( Throwable $t ) {
223 Logger::log( Logger::LEVEL_ERROR, 'Error while sending activation request: ' . $t->getMessage() );
224
225 throw new Auth_Error( $t->getMessage() );
226 }
227
228 if ( ! isset( $response->id ) ) {
229 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
230
231 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
232 }
233
234 Data::set_activation_state( $license_key );
235
236 do_action( Checkpoint::ON_ACTIVATE, $license_key );
237
238 return $response;
239 }
240
241 /**
242 * Deactivate specific license and remove activation data from the DB.
243 *
244 * @param $license_key string License key to deactivate.
245 *
246 * @return mixed
247 *
248 * @throws Auth_Error
249 */
250 public static function deactivate( string $license_key ) {
251
252 do_action( Checkpoint::ON_DEACTIVATE, $license_key );
253
254 try {
255 $response = Utils::get_api_client()->make_request(
256 'POST',
257 'activation/deactivate',
258 [
259 'key' => $license_key,
260 ]
261 );
262 } catch ( Throwable $t ) {
263 Logger::log( Logger::LEVEL_ERROR, 'Error while sending deactivation request: ' . $t->getMessage() );
264
265 throw new Auth_Error( $t->getMessage() );
266 } finally {
267 Data::delete_activation_state();
268 }
269
270 if ( ! isset( $response->id ) ) {
271 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
272
273 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
274 }
275
276 return $response;
277 }
278
279 /**
280 * Fetches and returns a list of available licenses for a specific user.
281 *
282 * @return array Available subscriptions or an empty array
283 *
284 * @throws Auth_Error
285 */
286 public static function get_subscriptions(): array {
287 try {
288 $response = Utils::get_api_client()->make_request(
289 'POST',
290 'activation/get-subscriptions'
291 );
292 } catch ( Throwable $t ) {
293 Logger::log( Logger::LEVEL_ERROR, 'Error while fetching subscriptions: ' . $t->getMessage() );
294
295 throw new Auth_Error( $t->getMessage() );
296 }
297
298 if ( ! isset( $response->subscriptions ) ) {
299 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
300
301 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
302 }
303
304 return $response->subscriptions;
305 }
306
307 public static function get_connect_status() {
308 if ( ! self::is_connected() ) {
309 Logger::log( Logger::LEVEL_INFO, 'Status getting error. Reason: User is not connected' );
310
311 return null;
312 }
313
314 $cached_status = get_transient( self::STATUS_CHECK_TRANSIENT );
315
316 if ( $cached_status ) {
317 return $cached_status;
318 }
319
320 $status = self::check_connect_status();
321
322 set_transient( self::STATUS_CHECK_TRANSIENT, $status, MINUTE_IN_SECONDS * 5 );
323
324 return $status;
325 }
326
327 private static function check_connect_status() {
328 if ( ! self::is_connected() ) {
329 Logger::log( Logger::LEVEL_INFO, 'Status check error. Reason: User is not connected' );
330
331 return null;
332 }
333
334 try {
335 $response = Utils::get_api_client()->make_request(
336 'POST',
337 'status/check'
338 );
339 } catch ( Throwable $t ) {
340 Logger::log(
341 Logger::LEVEL_ERROR,
342 'Status check error. Reason: ' . $t->getMessage()
343 );
344
345 return null;
346 }
347
348 if ( ! isset( $response->status ) ) {
349 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
350
351 return null;
352 }
353
354 return $response;
355 }
356
357 public static function update_usage_data( stdClass $new_usage_data ) {
358 $connect_status = self::get_connect_status();
359
360 if ( ! isset( $new_usage_data->allowed ) || ! isset( $new_usage_data->used ) ) {
361 return;
362 }
363
364 if ( 0 === $new_usage_data->allowed - $new_usage_data->used ) {
365 $connect_status->status = 'expired';
366 }
367
368 $connect_status->quota = $new_usage_data->allowed;
369 $connect_status->used_quota = $new_usage_data->used;
370
371 set_transient( self::STATUS_CHECK_TRANSIENT, $connect_status, MINUTE_IN_SECONDS * 5 );
372 }
373
374 public function __construct() {
375 // handle connect if elementor is active
376 add_action( 'load-elementor_page_elementor-connect', [ $this, 'handle_elementor_connect_admin' ], 9 );
377 // handle connect if elementor is not active
378 add_action( '_admin_menu', [ $this, 'handle_elementor_connect_admin' ] );
379 }
380 }
381