PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / trunk
Image Optimizer – Compress Images and Convert to WebP or AVIF vtrunk
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
← All changes | modules/oauth/components/connect.php +243 -36 1.0.2 → trunk View file →
@@ -1,18 +1,22 @@
1 1 <?php
2 2
3 -namespace ImageOptimizer\Modules\Oauth\Components;
3 +namespace ImageOptimization\Modules\Oauth\Components;
4 4
5 -use ImageOptimizer\Classes\{
6 - Logger,
7 - Utils
5 +use ImageOptimization\Modules\Oauth\{
6 + Classes\Route_Base,
7 + Components\Exceptions\Auth_Error,
8 + Classes\Data,
8 9 };
10 +use ImageOptimization\Classes\Logger;
11 +use ImageOptimization\Classes\Utils;
12 +use ImageOptimization\Modules\Settings\Module as Settings_Module;
9 13
10 -use ImageOptimizer\Modules\Oauth\Classes\Data;
11 -use ImageOptimizer\Modules\Settings\Module as Settings_Module;
14 +use stdClass;
15 +use Throwable;
12 16
13 17 if ( ! defined( 'ABSPATH' ) ) {
14 - exit; // Exit if accessed directly
18 + exit; // Exit if accessed directly.
15 19 }
16 20
17 21 /**
18 22 * Class Connect
@@ -18,8 +22,9 @@
18 22 * Class Connect
19 23 */
20 24 class Connect {
21 25 const API_URL = 'https://my.elementor.com/api/connect/v1';
26 + const STATUS_CHECK_TRANSIENT = 'image_optimizer_status_check';
22 27
23 28 /**
24 29 * is_connected
25 30 * @return bool
@@ -40,9 +45,9 @@
40 45 * maybe_handle_admin_connect_page
41 46 * @return bool
42 47 */
43 48 public static function maybe_handle_admin_connect_page(): bool {
44 - if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'nonce_actionget_token' ) ) {
49 + if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'get_token' ) ) {
45 50 return false;
46 51 }
47 52
48 53 $args = [
@@ -74,10 +79,10 @@
74 79 return;
75 80 }
76 81
77 82 // validate nonce
78 - if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'nonce_actionget_token' ) ) {
79 - wp_die( 'Nonce verification failed', 'image-optimizer' );
83 + if ( empty( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'get_token' ) ) {
84 + wp_die( 'Nonce verification failed', 'image-optimization' );
80 85 }
81 86
82 87 $token_response = wp_remote_request( self::API_URL . '/get_token', [
83 88 'method' => 'POST',
@@ -84,14 +89,14 @@
84 89 'body' => [
85 90 'app' => 'library',
86 91 'grant_type' => 'authorization_code',
87 92 'client_id' => Data::get_client_id(),
88 - 'code' => sanitize_text_field( $_GET['code'] ),
93 + 'code' => isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : null,
89 94 ],
90 95 ] );
91 96
92 97 if ( is_wp_error( $token_response ) ) {
93 - wp_die( $token_response->get_error_message(), 'image-optimizer' );
98 + wp_die( esc_html( $token_response->get_error_message() ), 'image-optimization' );
94 99 }
95 100
96 101 $data = json_decode( wp_remote_retrieve_body( $token_response ), true );
97 102 Data::set_connect_data( $data );
@@ -108,45 +113,234 @@
108 113 die();
109 114 }
110 115
111 116 /**
112 - * disconnect
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
113 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( esc_html( $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( 'get_token' ),
175 + ], admin_url( 'admin.php' ) ) ),
176 + 'may_share_data' => 0,
177 + 'reconnect_nonce' => wp_create_nonce( 'reconnect' ),
178 + ], Route_Base::SITE_URL . 'library' );
179 + }
180 +
181 + /**
182 + * Disconnects a user and removes connection data from the DB.
183 + */
114 184 public static function disconnect() {
115 - $response = wp_remote_request( self::API_URL . '/disconnect', [
116 - 'method' => 'POST',
117 - 'body' => [
118 - 'app' => 'library',
119 - 'home_url' => trailingslashit( home_url() ),
120 - 'client_id' => Data::get_client_id(),
121 - 'access_token' => Data::get_access_token(),
122 - ],
123 - ] );
185 + do_action( Checkpoint::ON_DISCONNECT );
124 186
125 - if ( is_wp_error( $response ) ) {
126 - wp_die( $response->get_error_message(), 'image-optimizer' );
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( esc_html( $t->getMessage() ) );
201 + } finally {
202 + Data::reset();
127 203 }
204 + }
128 205
129 - Data::reset();
130 - do_action( Checkpoint::ON_DISCONNECT );
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( esc_html( $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;
131 239 }
132 240
133 - public static function check_connect_status() {
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( esc_html( $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( esc_html( $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() {
134 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() ) {
135 329 Logger::log( Logger::LEVEL_INFO, 'Status check error. Reason: User is not connected' );
136 330
137 331 return null;
138 332 }
139 333
140 - $response = Utils::get_api_client()->make_request(
141 - 'POST',
142 - 'status/check'
143 - );
144 -
145 - if ( is_wp_error( $response ) ) {
334 + try {
335 + $response = Utils::get_api_client()->make_request(
336 + 'POST',
337 + 'status/check'
338 + );
339 + } catch ( Throwable $t ) {
146 340 Logger::log(
147 341 Logger::LEVEL_ERROR,
148 - 'Status check error. Reason: ' . $response->get_error_message()
342 + 'Status check error. Reason: ' . $t->getMessage()
149 343 );
150 344
151 345 return null;
152 346 }
@@ -159,10 +353,23 @@
159 353
160 354 return $response;
161 355 }
162 356
163 - public static function get_connect_route_url( $endpoint ): string {
164 - return rest_url( 'image-optimizer/v1/connect/' . $endpoint );
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 );
165 372 }
166 373
167 374 public function __construct() {
168 375 // handle connect if elementor is active