PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.3.0
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.3.0
1.7.7 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 All 33 releases
image-optimization / modules / oauth / components / connect.php

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

380 lines 9.6 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 Data::reset();
186
187 do_action( Checkpoint::ON_DISCONNECT );
188
189 try {
190 return wp_remote_request( self::API_URL . '/disconnect', [
191 'method' => 'POST',
192 'body' => [
193 'app' => 'library',
194 'home_url' => trailingslashit( home_url() ),
195 'client_id' => Data::get_client_id(),
196 'access_token' => Data::get_access_token(),
197 ],
198 ] );
199 } catch ( Throwable $t ) {
200 Logger::log( Logger::LEVEL_ERROR, 'Error while sending disconnection request: ' . $t->getMessage() );
201
202 throw new Auth_Error( $t->getMessage() );
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 Data::delete_activation_state();
252
253 do_action( Checkpoint::ON_DEACTIVATE, $license_key );
254
255 try {
256 $response = Utils::get_api_client()->make_request(
257 'POST',
258 'activation/deactivate',
259 [
260 'key' => $license_key,
261 ]
262 );
263 } catch ( Throwable $t ) {
264 Logger::log( Logger::LEVEL_ERROR, 'Error while sending deactivation request: ' . $t->getMessage() );
265
266 throw new Auth_Error( $t->getMessage() );
267 }
268
269 if ( ! isset( $response->id ) ) {
270 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
271
272 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
273 }
274
275 return $response;
276 }
277
278 /**
279 * Fetches and returns a list of available licenses for a specific user.
280 *
281 * @return array Available subscriptions or an empty array
282 *
283 * @throws Auth_Error
284 */
285 public static function get_subscriptions(): array {
286 try {
287 $response = Utils::get_api_client()->make_request(
288 'POST',
289 'activation/get-subscriptions'
290 );
291 } catch ( Throwable $t ) {
292 Logger::log( Logger::LEVEL_ERROR, 'Error while fetching subscriptions: ' . $t->getMessage() );
293
294 throw new Auth_Error( $t->getMessage() );
295 }
296
297 if ( ! isset( $response->subscriptions ) ) {
298 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
299
300 throw new Auth_Error( esc_html__( 'Invalid response from server', 'image-optimization' ) );
301 }
302
303 return $response->subscriptions;
304 }
305
306 public static function get_connect_status() {
307 if ( ! self::is_connected() ) {
308 Logger::log( Logger::LEVEL_INFO, 'Status getting error. Reason: User is not connected' );
309
310 return null;
311 }
312
313 $cached_status = get_transient( self::STATUS_CHECK_TRANSIENT );
314
315 if ( $cached_status ) {
316 return $cached_status;
317 }
318
319 $status = self::check_connect_status();
320
321 set_transient( self::STATUS_CHECK_TRANSIENT, $status, MINUTE_IN_SECONDS * 5 );
322
323 return $status;
324 }
325
326 private static function check_connect_status() {
327 if ( ! self::is_connected() ) {
328 Logger::log( Logger::LEVEL_INFO, 'Status check error. Reason: User is not connected' );
329
330 return null;
331 }
332
333 try {
334 $response = Utils::get_api_client()->make_request(
335 'POST',
336 'status/check'
337 );
338 } catch ( Throwable $t ) {
339 Logger::log(
340 Logger::LEVEL_ERROR,
341 'Status check error. Reason: ' . $t->getMessage()
342 );
343
344 return null;
345 }
346
347 if ( ! isset( $response->status ) ) {
348 Logger::log( Logger::LEVEL_ERROR, 'Invalid response from server' );
349
350 return null;
351 }
352
353 return $response;
354 }
355
356 public static function update_usage_data( stdClass $new_usage_data ) {
357 $connect_status = self::get_connect_status();
358
359 if ( ! isset( $new_usage_data->allowed ) || ! isset( $new_usage_data->used ) ) {
360 return;
361 }
362
363 if ( 0 === $new_usage_data->allowed - $new_usage_data->used ) {
364 $connect_status->status = 'expired';
365 }
366
367 $connect_status->quota = $new_usage_data->allowed;
368 $connect_status->used_quota = $new_usage_data->used;
369
370 set_transient( self::STATUS_CHECK_TRANSIENT, $connect_status, MINUTE_IN_SECONDS * 5 );
371 }
372
373 public function __construct() {
374 // handle connect if elementor is active
375 add_action( 'load-elementor_page_elementor-connect', [ $this, 'handle_elementor_connect_admin' ], 9 );
376 // handle connect if elementor is not active
377 add_action( '_admin_menu', [ $this, 'handle_elementor_connect_admin' ] );
378 }
379 }
380