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 +245 -35 1.0.0 → trunk View file →
@@ -1,15 +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\Logger;
6 -use ImageOptimizer\Classes\Utils;
7 -use ImageOptimizer\Modules\Oauth\Classes\Data;
8 -use ImageOptimizer\Modules\Settings\Module as Settings_Module;
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;
9 13
14 +use stdClass;
15 +use Throwable;
16 +
10 17 if ( ! defined( 'ABSPATH' ) ) {
11 - exit; // Exit if accessed directly
18 + exit; // Exit if accessed directly.
12 19 }
13 20
14 21 /**
15 22 * Class Connect
@@ -15,8 +22,9 @@
15 22 * Class Connect
16 23 */
17 24 class Connect {
18 25 const API_URL = 'https://my.elementor.com/api/connect/v1';
26 + const STATUS_CHECK_TRANSIENT = 'image_optimizer_status_check';
19 27
20 28 /**
21 29 * is_connected
22 30 * @return bool
@@ -37,9 +45,9 @@
37 45 * maybe_handle_admin_connect_page
38 46 * @return bool
39 47 */
40 48 public static function maybe_handle_admin_connect_page(): bool {
41 - 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' ) ) {
42 50 return false;
43 51 }
44 52
45 53 $args = [
@@ -71,10 +79,10 @@
71 79 return;
72 80 }
73 81
74 82 // validate nonce
75 - if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['nonce'] ) ), 'nonce_actionget_token' ) ) {
76 - 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' );
77 85 }
78 86
79 87 $token_response = wp_remote_request( self::API_URL . '/get_token', [
80 88 'method' => 'POST',
@@ -81,14 +89,14 @@
81 89 'body' => [
82 90 'app' => 'library',
83 91 'grant_type' => 'authorization_code',
84 92 'client_id' => Data::get_client_id(),
85 - 'code' => sanitize_text_field( $_GET['code'] ),
93 + 'code' => isset( $_GET['code'] ) ? sanitize_text_field( wp_unslash( $_GET['code'] ) ) : null,
86 94 ],
87 95 ] );
88 96
89 97 if ( is_wp_error( $token_response ) ) {
90 - wp_die( $token_response->get_error_message(), 'image-optimizer' );
98 + wp_die( esc_html( $token_response->get_error_message() ), 'image-optimization' );
91 99 }
92 100
93 101 $data = json_decode( wp_remote_retrieve_body( $token_response ), true );
94 102 Data::set_connect_data( $data );
@@ -105,45 +113,234 @@
105 113 die();
106 114 }
107 115
108 116 /**
109 - * 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
110 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 + */
111 184 public static function disconnect() {
112 - $response = wp_remote_request( self::API_URL . '/disconnect', [
113 - 'method' => 'POST',
114 - 'body' => [
115 - 'app' => 'library',
116 - 'home_url' => trailingslashit( home_url() ),
117 - 'client_id' => Data::get_client_id(),
118 - 'access_token' => Data::get_access_token(),
119 - ],
120 - ] );
185 + do_action( Checkpoint::ON_DISCONNECT );
121 186
122 - if ( is_wp_error( $response ) ) {
123 - 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();
124 203 }
204 + }
125 205
126 - Data::reset();
127 - 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;
128 239 }
129 240
130 - 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() {
131 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() ) {
132 329 Logger::log( Logger::LEVEL_INFO, 'Status check error. Reason: User is not connected' );
133 330
134 331 return null;
135 332 }
136 333
137 - $response = Utils::get_api_client()->make_request(
138 - 'POST',
139 - 'status/check'
140 - );
141 -
142 - 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 ) {
143 340 Logger::log(
144 341 Logger::LEVEL_ERROR,
145 - 'Status check error. Reason: ' . $response->get_error_message()
342 + 'Status check error. Reason: ' . $t->getMessage()
146 343 );
147 344
148 345 return null;
149 346 }
@@ -156,10 +353,23 @@
156 353
157 354 return $response;
158 355 }
159 356
160 - public static function get_connect_route_url( $endpoint ): string {
161 - 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 );
162 372 }
163 373
164 374 public function __construct() {
165 375 // handle connect if elementor is active