PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.32
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.32
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | classes/models/FrmAddon.php +505 -145 6.36.32 View file →
@@ -4,29 +4,117 @@
4 4 }
5 5
6 6 /** @phpstan-consistent-constructor */
7 7 class FrmAddon {
8 +
9 + /**
10 + * @var string
11 + */
8 12 public $store_url = 'https://formidableforms.com';
13 +
14 + /**
15 + * @var int|string|null
16 + */
9 17 public $download_id;
18 +
19 + /**
20 + * @var string|null
21 + */
10 22 public $plugin_file;
23 +
24 + /**
25 + * @var string
26 + */
11 27 public $plugin_folder;
28 +
29 + /**
30 + * @var string
31 + */
12 32 public $plugin_name;
33 +
34 + /**
35 + * @var string
36 + */
13 37 public $plugin_slug;
38 +
39 + /**
40 + * @var string
41 + */
14 42 public $option_name;
43 +
44 + /**
45 + * @var string|null
46 + */
15 47 public $version;
48 +
49 + /**
50 + * @var string
51 + */
16 52 public $author = 'Strategy11';
53 +
54 + /**
55 + * @var bool
56 + */
17 57 public $is_parent_licence = false;
58 +
59 + /**
60 + * @var bool
61 + */
18 62 public $needs_license = true;
63 +
64 + /**
65 + * @var bool
66 + */
19 67 private $is_expired_addon = false;
68 +
69 + /**
70 + * @var string|null
71 + */
20 72 public $license;
73 +
74 + /**
75 + * @var bool
76 + */
21 77 protected $get_beta = false;
22 78
79 + /**
80 + * @var array|null
81 + */
82 + protected $save_status;
83 +
84 + /**
85 + * This is used to decide whether the license checks should continue.
86 + * The point is to avoid license issues when a site url changes.
87 + *
88 + * @since 6.8.3
89 + *
90 + * @var array
91 + */
92 + private $save_response = array();
93 +
94 + /**
95 + * This is used to flag other add ons not to send a request.
96 + * We only want to send a single API request per page load.
97 + *
98 + * @since 6.8.3
99 + *
100 + * @var string
101 + */
102 + private $transient_lock_key = 'frm_activate_request_lock';
103 +
104 + /**
105 + * @since 6.8.3
106 + *
107 + * @var bool
108 + */
109 + protected $should_clear_cache = true;
110 +
23 111 public function __construct() {
24 -
25 - if ( empty( $this->plugin_slug ) ) {
112 + if ( ! $this->plugin_slug ) {
26 113 $this->plugin_slug = preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->plugin_name ) ) );
27 114 }
28 - if ( empty( $this->option_name ) ) {
115 +
116 + if ( ! $this->option_name ) {
29 117 $this->option_name = 'edd_' . $this->plugin_slug . '_license_';
30 118 }
31 119
32 120 $this->plugin_folder = plugin_basename( $this->plugin_file );
@@ -35,71 +123,96 @@
35 123 add_filter( 'frm_installed_addons', array( &$this, 'insert_installed_addon' ) );
36 124 $this->edd_plugin_updater();
37 125 }
38 126
127 + /**
128 + * @return void
129 + */
39 130 public static function load_hooks() {
40 131 add_filter( 'frm_include_addon_page', '__return_true' );
41 132 new static();
42 133 }
43 134
135 + /**
136 + * @param array $plugins
137 + *
138 + * @return array
139 + */
44 140 public function insert_installed_addon( $plugins ) {
45 141 $plugins[ $this->plugin_slug ] = $this;
46 -
47 142 return $plugins;
48 143 }
49 144
145 + /**
146 + * @param string $plugin_slug
147 + *
148 + * @return false|FrmAddon
149 + */
50 150 public static function get_addon( $plugin_slug ) {
51 151 $plugins = apply_filters( 'frm_installed_addons', array() );
52 - $plugin = false;
53 - if ( isset( $plugins[ $plugin_slug ] ) ) {
54 - $plugin = $plugins[ $plugin_slug ];
55 - }
56 -
57 - return $plugin;
152 + return $plugins[ $plugin_slug ] ?? false;
58 153 }
59 154
155 + /**
156 + * @return void
157 + */
60 158 public function edd_plugin_updater() {
61 -
62 159 $this->is_license_revoked();
63 160 $license = $this->license;
64 161
65 162 add_action( 'after_plugin_row_' . plugin_basename( $this->plugin_file ), array( $this, 'maybe_show_license_message' ), 10, 2 );
66 163
67 - if ( ! empty( $license ) ) {
164 + if ( ! $license ) {
165 + return;
166 + }
68 167
69 - if ( 'formidable/formidable.php' !== $this->plugin_folder ) {
70 - add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
71 - }
168 + if ( 'formidable/formidable.php' !== $this->plugin_folder ) {
169 + add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
170 + }
72 171
73 - add_filter( 'site_transient_update_plugins', array( &$this, 'clear_expired_download' ) );
74 - }
172 + add_filter( 'site_transient_update_plugins', array( &$this, 'clear_expired_download' ) );
75 173 }
76 174
77 175 /**
78 - * Updates information on the "View version x.x details" page with custom data.
176 + * Updates information on the "View version 6.15 details" page with custom data.
79 177 *
80 178 * @uses api_request()
81 179 *
82 - * @param mixed $_data
83 - * @param string $_action
84 - * @param object $_args
180 + * @param mixed $_data
181 + * @param string $_action
182 + * @param object|null $_args
85 183 *
86 - * @return object $_data
184 + * @return object Data.
87 185 */
88 186 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
89 -
90 - if ( $_action != 'plugin_information' ) {
187 + if ( $_action !== 'plugin_information' ) {
91 188 return $_data;
92 189 }
93 190
94 191 $slug = basename( $this->plugin_file, '.php' );
95 192 $slug2 = str_replace( '/' . $slug . '.php', '', $this->plugin_folder );
96 - if ( empty( $_args->slug ) || ( $_args->slug != $slug && $_args->slug !== $slug2 ) ) {
193 +
194 + if ( empty( $_args->slug ) || ( $_args->slug !== $slug && $_args->slug !== $slug2 ) ) {
97 195 return $_data;
98 196 }
99 197
100 198 $item_id = $this->download_id;
101 - if ( empty( $item_id ) ) {
199 +
200 + if ( $item_id ) {
201 + $api = new FrmFormApi( $this->license );
202 +
203 + // Force new API info so we can pull changelog data.
204 + // Change log data is intentionally omitted from the cached API response
205 + // To help reduce the size of the autoloaded option.
206 + $api->force_api_request();
207 + $plugins = $api->get_api_info();
208 +
209 + if ( $plugins ) {
210 + $_data = $plugins[ $item_id ];
211 + }
212 + }
213 +
214 + if ( empty( $plugins ) ) {
102 215 $_data = array(
103 216 'name' => $this->plugin_name,
104 217 'excerpt' => '',
105 218 'changelog' => 'See the full changelog at <a href="' . esc_url( $this->store_url . '/changelog/' ) . '"></a>',
@@ -107,12 +220,8 @@
107 220 'high' => '',
108 221 'low' => 'https://ps.w.org/formidable/assets/banner-1544x500.png',
109 222 ),
110 223 );
111 - } else {
112 - $api = new FrmFormApi( $this->license );
113 - $plugins = $api->get_api_info();
114 - $_data = $plugins[ $item_id ];
115 224 }
116 225
117 226 $_data['sections'] = array(
118 227 'description' => $_data['excerpt'],
@@ -123,28 +232,31 @@
123 232
124 233 return (object) $_data;
125 234 }
126 235
236 + /**
237 + * @return string
238 + */
127 239 public function get_license() {
128 240 $license = $this->maybe_get_pro_license();
129 - if ( ! empty( $license ) ) {
241 +
242 + if ( $license ) {
130 243 return $license;
131 244 }
132 245
133 246 $license = trim( get_option( $this->option_name . 'key' ) );
134 - if ( empty( $license ) ) {
135 - $license = $this->activate_defined_license();
136 - }
137 247
138 - return $license;
248 + return $license ? $license : $this->activate_defined_license();
139 249 }
140 250
141 251 /**
142 252 * @since 3.04.03
253 + *
254 + * @return false|string
143 255 */
144 256 protected function maybe_get_pro_license() {
145 - // prevent a loop if $this is the pro plugin
146 - $get_license = FrmAppHelper::pro_is_installed() && is_callable( 'FrmProAppHelper::get_updater' ) && $this->plugin_name != 'Formidable Pro';
257 + // Prevent a loop if $this is the pro plugin
258 + $get_license = FrmAppHelper::pro_is_installed() && is_callable( 'FrmProAppHelper::get_updater' ) && $this->plugin_name !== 'Formidable Pro';
147 259
148 260 if ( ! $get_license ) {
149 261 return false;
150 262 }
@@ -151,18 +263,16 @@
151 263
152 264 $api = new FrmFormApi();
153 265 $api->get_pro_updater();
154 266 $license = $api->get_license();
155 - if ( empty( $license ) ) {
267 +
268 + if ( ! $license ) {
156 269 return false;
157 270 }
158 271
159 272 $this->get_api_info( $license );
160 - if ( ! $this->is_parent_licence ) {
161 - $license = false;
162 - }
163 273
164 - return $license;
274 + return $this->is_parent_licence ? $license : false;
165 275 }
166 276
167 277 /**
168 278 * Activate the license in wp-config.php
@@ -167,14 +277,17 @@
167 277 /**
168 278 * Activate the license in wp-config.php
169 279 *
170 280 * @since 2.04
281 + *
282 + * @return string
171 283 */
172 284 public function activate_defined_license() {
173 285 $license = $this->get_defined_license();
174 - if ( ! empty( $license ) && ! $this->is_active() && $this->is_time_to_auto_activate() ) {
286 +
287 + if ( $license && ! $this->is_active() && ! $this->checked_recently( '1 day' ) ) {
175 288 $response = $this->activate_license( $license );
176 - $this->set_auto_activate_time();
289 +
177 290 if ( ! $response['success'] ) {
178 291 $license = '';
179 292 }
180 293 }
@@ -185,35 +298,28 @@
185 298 /**
186 299 * Check the wp-config.php for the license key
187 300 *
188 301 * @since 2.04
302 + *
303 + * @return false|string
189 304 */
190 305 public function get_defined_license() {
191 - $consant_name = 'FRM_' . strtoupper( $this->plugin_slug ) . '_LICENSE';
192 -
193 - return defined( $consant_name ) ? constant( $consant_name ) : false;
306 + $constant_name = 'FRM_' . strtoupper( $this->plugin_slug ) . '_LICENSE';
307 + return defined( $constant_name ) ? constant( $constant_name ) : false;
194 308 }
195 309
310 + /**
311 + * @param string $license
312 + *
313 + * @return void
314 + */
196 315 public function set_license( $license ) {
197 316 update_option( $this->option_name . 'key', $license );
198 317 }
199 318
200 319 /**
201 - * If the license is in the config, limit the frequency of checks.
202 - * The license may be entered incorrectly, so we don't want to check on every page load.
203 - *
204 - * @since 2.04
320 + * @return mixed
205 321 */
206 - private function is_time_to_auto_activate() {
207 - $last_try = get_option( $this->option_name . 'last_activate' );
208 -
209 - return ( ! $last_try || $last_try < strtotime( '-1 day' ) );
210 - }
211 -
212 - private function set_auto_activate_time() {
213 - update_option( $this->option_name . 'last_activate', time() );
214 - }
215 -
216 322 public function is_active() {
217 323 return get_option( $this->option_name . 'active' );
218 324 }
219 325
@@ -219,26 +325,65 @@
219 325
220 326 /**
221 327 * @since 3.04.03
222 328 *
223 - * @param array error
329 + * @param array|string $error
330 + *
331 + * @return void
224 332 */
225 333 public function maybe_clear_license( $error ) {
226 - if ( $error['code'] === 'disabled' && $error['license'] === $this->license ) {
334 + if ( is_array( $error ) && $error['code'] === 'disabled' && $error['license'] === $this->license ) {
227 335 $this->clear_license();
228 336 }
229 337 }
230 338
339 + /**
340 + * @return void
341 + */
231 342 public function clear_license() {
232 343 delete_option( $this->option_name . 'active' );
233 344 delete_option( $this->option_name . 'key' );
345 +
346 + if ( ! $this->should_clear_cache ) {
347 + return;
348 + }
349 +
234 350 delete_site_option( $this->transient_key() );
235 351 delete_option( $this->transient_key() );
236 352 $this->delete_cache();
353 + $this->should_clear_cache = true;
237 354 }
238 355
356 + /**
357 + * Don't save an invalid license.
358 + *
359 + * @since 6.8.3
360 + *
361 + * @param bool|string $is_valid If license activation was successful. May be a string 'valid'.
362 + *
363 + * @return void
364 + */
365 + protected function maybe_set_active( $is_valid ) {
366 + update_option( $this->option_name . 'active', $is_valid );
367 +
368 + if ( $is_valid ) {
369 + $this->set_active( $is_valid );
370 + return;
371 + }
372 +
373 + // Don't save the license if it's invalid.
374 + $this->should_clear_cache = false;
375 + $this->clear_license();
376 + delete_option( $this->option_name . 'key' );
377 + $this->license = '';
378 + }
379 +
380 + /**
381 + * @param bool|string $is_active Value may include 'valid'.
382 + *
383 + * @return void
384 + */
239 385 public function set_active( $is_active ) {
240 - update_option( $this->option_name . 'active', $is_active );
241 386 $this->delete_cache();
242 387 FrmAppHelper::save_combined_js();
243 388 $this->update_pro_capabilities();
244 389 }
@@ -246,8 +391,10 @@
246 391 /**
247 392 * Updates roles capabilities after pro license is active.
248 393 *
249 394 * @since 5.0
395 + *
396 + * @return void
250 397 */
251 398 protected function update_pro_capabilities() {
252 399 global $wp_roles;
253 400
@@ -257,18 +404,19 @@
257 404
258 405 $caps = FrmAppHelper::frm_capabilities( 'pro_only' );
259 406 $roles = get_editable_roles();
260 407 $settings = new FrmSettings();
408 +
261 409 foreach ( $caps as $cap => $cap_desc ) {
262 - $cap_roles = (array) ( isset( $settings->$cap ) ? $settings->$cap : 'administrator' );
410 + $cap_roles = (array) ( $settings->$cap ?? 'administrator' );
263 411
264 412 // Make sure administrators always have permissions.
265 - if ( ! in_array( 'administrator', $cap_roles ) ) {
413 + if ( ! in_array( 'administrator', $cap_roles, true ) ) {
266 414 array_push( $cap_roles, 'administrator' );
267 415 }
268 416
269 417 foreach ( $roles as $role => $details ) {
270 - if ( in_array( $role, $cap_roles ) ) {
418 + if ( in_array( $role, $cap_roles, true ) ) {
271 419 $wp_roles->add_cap( $role, $cap );
272 420 } else {
273 421 $wp_roles->remove_cap( $role, $cap );
274 422 }
@@ -277,12 +425,17 @@
277 425 }
278 426
279 427 /**
280 428 * @since 3.04.03
429 + *
430 + * @return void
281 431 */
282 432 protected function delete_cache() {
283 433 delete_transient( 'frm_api_licence' );
284 434
435 + // Cleanup option that has been removed.
436 + delete_option( $this->option_name . 'last_activate' );
437 +
285 438 $api = new FrmFormApi( $this->license );
286 439 $api->reset_cached();
287 440
288 441 $api = new FrmFormTemplateApi( $this->license );
@@ -289,8 +442,14 @@
289 442 $api->reset_cached();
290 443
291 444 $api = new FrmApplicationApi( $this->license );
292 445 $api->reset_cached();
446 +
447 + $api = new FrmSalesApi();
448 + $api->reset_cached();
449 +
450 + $api = new FrmStyleApi();
451 + $api->reset_cached();
293 452 }
294 453
295 454 /**
296 455 * The Pro version includes the show_license_message function.
@@ -296,12 +455,17 @@
296 455 * The Pro version includes the show_license_message function.
297 456 * We need an extra check before we allow it to show a message.
298 457 *
299 458 * @since 3.04.03
459 + *
460 + * @param string $file File path of the plugin.
461 + * @param array $plugin Plugin data.
462 + *
463 + * @return void
300 464 */
301 465 public function maybe_show_license_message( $file, $plugin ) {
302 466 if ( $this->is_expired_addon || isset( $plugin['package'] ) ) {
303 - // let's not show a ton of duplicate messages
467 + // Let's not show a ton of duplicate messages
304 468 return;
305 469 }
306 470
307 471 $this->show_license_message( $file, $plugin );
@@ -306,22 +470,30 @@
306 470
307 471 $this->show_license_message( $file, $plugin );
308 472 }
309 473
474 + /**
475 + * @param string $file File path of the plugin.
476 + * @param array $plugin Plugin data.
477 + *
478 + * @return void
479 + */
310 480 public function show_license_message( $file, $plugin ) {
311 481 $message = '';
312 - if ( empty( $this->license ) ) {
313 - /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
314 - $message = sprintf( esc_html__( 'Your %1$s license key is missing. Please add it on the %2$slicenses page%3$s.', 'formidable' ), esc_html( $this->plugin_name ), '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-settings' ) ) . '">', '</a>' );
315 - } else {
482 +
483 + if ( $this->license ) {
316 484 $api = new FrmFormApi( $this->license );
317 485 $errors = $api->error_for_license();
318 - if ( ! empty( $errors ) ) {
486 +
487 + if ( $errors ) {
319 488 $message = reset( $errors );
320 489 }
490 + } else {
491 + /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
492 + $message = sprintf( esc_html__( 'Your %1$s license key is missing. Please add it on the %2$slicenses page%3$s.', 'formidable' ), esc_html( $this->plugin_name ), '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-settings' ) ) . '">', '</a>' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
321 493 }
322 494
323 - if ( empty( $message ) ) {
495 + if ( ! $message ) {
324 496 return;
325 497 }
326 498
327 499 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
@@ -326,14 +498,19 @@
326 498
327 499 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
328 500 $id = sanitize_title( $plugin['Name'] ) . '-next';
329 501
330 - echo '<tr class="plugin-update-tr active" id="' . esc_attr( $id ) . '"><td colspan="' . esc_attr( $wp_list_table->get_column_count() ) . '" class="plugin-update colspanchange"><div class="update-message notice error inline notice-error notice-alt"><p>';
331 - echo FrmAppHelper::kses( $message, 'a' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
332 - echo '<script type="text/javascript">var d = document.getElementById("' . esc_attr( $id ) . '").previousSibling;if ( d !== null ){ d.className = d.className + " update"; }</script>';
502 + echo '<tr class="plugin-update-tr active" id="' . esc_attr( $id ) . '"><td colspan="' . esc_attr( $wp_list_table->get_column_count() ) . '" class="plugin-update colspanchange"><div class="update-message notice error inline notice-error notice-alt"><p>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
503 + FrmAppHelper::kses_echo( $message, 'a' );
504 + echo '<script type="text/javascript">var d = document.getElementById("' . esc_attr( $id ) . '").previousSibling;if ( d !== null ){ d.className = d.className + " update"; }</script>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
333 505 echo '</p></div></td></tr>';
334 506 }
335 507
508 + /**
509 + * @param mixed $transient
510 + *
511 + * @return mixed
512 + */
336 513 public function clear_expired_download( $transient ) {
337 514 if ( ! is_object( $transient ) ) {
338 515 return $transient;
339 516 }
@@ -338,9 +515,9 @@
338 515 return $transient;
339 516 }
340 517
341 518 if ( $this->is_current_version( $transient ) ) {
342 - //make sure it doesn't show there is an update if plugin is up-to-date
519 + // Make sure it doesn't show there is an update if plugin is up-to-date.
343 520 if ( isset( $transient->response[ $this->plugin_folder ] ) ) {
344 521 unset( $transient->response[ $this->plugin_folder ] );
345 522 }
346 523 } elseif ( isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) ) {
@@ -345,9 +522,9 @@
345 522 }
346 523 } elseif ( isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) ) {
347 524 $this->prepare_update_details( $transient->response[ $this->plugin_folder ] );
348 525
349 - // if the transient has expired, clear the update and trigger it again
526 + // If the transient has expired, clear the update and trigger it again
350 527 if ( $transient->response[ $this->plugin_folder ] === false ) {
351 528 if ( ! $this->has_been_cleared() ) {
352 529 $this->cleared_plugins();
353 530 $this->manually_queue_update();
@@ -363,29 +540,38 @@
363 540 * Check if the plugin information is correct to allow an update
364 541 *
365 542 * @since 3.04.03
366 543 *
367 - * @param object $transient - the current plugin info saved for update
544 + * @param object $transient The current plugin info saved for update.
545 + *
546 + * @return void
368 547 */
369 548 private function prepare_update_details( &$transient ) {
370 549 $version_info = $transient;
371 - $has_beta_url = isset( $version_info->beta ) && ! empty( $version_info->beta );
550 + $has_beta_url = ! empty( $version_info->beta );
551 +
372 552 if ( $this->get_beta && ! $has_beta_url ) {
373 553 $version_info = (object) $this->get_api_info( $this->license );
374 554 }
375 555
376 - if ( isset( $version_info->new_version ) && ! empty( $version_info->new_version ) ) {
377 - $this->clear_old_plugin_version( $version_info );
378 - if ( $version_info === false ) { // was cleared with timeout
379 - $transient = false;
380 - } else {
381 - $this->maybe_use_beta_url( $version_info );
556 + if ( empty( $version_info->new_version ) ) {
557 + return;
558 + }
382 559
383 - if ( version_compare( $version_info->new_version, $this->version, '>' ) ) {
384 - $transient = $version_info;
385 - }
386 - }
560 + $this->clear_old_plugin_version( $version_info );
561 +
562 + if ( $version_info === false ) {
563 + // Was cleared with timeout.
564 + $transient = false;
565 +
566 + return;
387 567 }
568 +
569 + $this->maybe_use_beta_url( $version_info );
570 +
571 + if ( version_compare( $version_info->new_version, $this->version, '>' ) ) {
572 + $transient = $version_info;
573 + }
388 574 }
389 575
390 576 /**
391 577 * Get the API info for this plugin
@@ -390,18 +576,22 @@
390 576 /**
391 577 * Get the API info for this plugin
392 578 *
393 579 * @since 3.04.03
580 + *
581 + * @param string $license The license key.
582 + *
583 + * @return array The API info for the plugin.
394 584 */
395 585 protected function get_api_info( $license ) {
396 586 $api = new FrmFormApi( $license );
397 587 $addon = $api->get_addon_for_license( $this );
398 588
399 - // if there is no download url, this license does not apply to the addon
589 + // If there is no download url, this license does not apply to the addon
400 590 if ( isset( $addon['package'] ) ) {
401 591 $this->is_parent_licence = true;
402 592 } elseif ( isset( $addon['error'] ) ) {
403 - // if the license is expired, we must assume all add-ons were packaged
593 + // If the license is expired, we must assume all add-ons were packaged
404 594 $this->is_parent_licence = true;
405 595 $this->is_expired_addon = true;
406 596 }
407 597
@@ -412,16 +602,24 @@
412 602 * Make sure transients don't stick around on sites that
413 603 * don't save the transient expiration
414 604 *
415 605 * @since 2.05.05
606 + *
607 + * @param object $version_info The version info for the plugin.
608 + *
609 + * @return void
416 610 */
417 611 private function clear_old_plugin_version( &$version_info ) {
418 - $timeout = ( isset( $version_info->timeout ) && ! empty( $version_info->timeout ) ) ? $version_info->timeout : 0;
419 - if ( ! empty( $timeout ) && time() > $timeout ) {
420 - $version_info = false; // Cache is expired
421 - $api = new FrmFormApi( $this->license );
422 - $api->reset_cached();
612 + $timeout = ! empty( $version_info->timeout ) ? $version_info->timeout : 0;
613 +
614 + if ( ! $timeout || time() <= $timeout ) {
615 + return;
423 616 }
617 +
618 + // Cache is expired.
619 + $version_info = false;
620 + $api = new FrmFormApi( $this->license );
621 + $api->reset_cached();
424 622 }
425 623
426 624 /**
427 625 * The beta url is always included if the download has a beta.
@@ -427,55 +625,85 @@
427 625 * The beta url is always included if the download has a beta.
428 626 * Check if the beta should be downloaded.
429 627 *
430 628 * @since 3.04.03
629 + *
630 + * @param object $version_info The version info for the plugin.
631 + *
632 + * @return void
431 633 */
432 634 private function maybe_use_beta_url( &$version_info ) {
433 - if ( $this->get_beta && isset( $version_info->beta ) && ! empty( $version_info->beta ) ) {
434 - $version_info->new_version = $version_info->beta['version'];
435 - $version_info->package = $version_info->beta['package'];
436 - if ( isset( $version_info->plugin ) && ! empty( $version_info->plugin ) ) {
437 - $version_info->plugin = $version_info->beta['plugin'];
438 - }
635 + if ( ! $this->get_beta || empty( $version_info->beta ) ) {
636 + return;
439 637 }
638 +
639 + $version_info->new_version = $version_info->beta['version'];
640 + $version_info->package = $version_info->beta['package'];
641 +
642 + if ( ! empty( $version_info->plugin ) ) {
643 + $version_info->plugin = $version_info->beta['plugin'];
644 + }
440 645 }
441 646
647 + /**
648 + * @param object $transient
649 + *
650 + * @return bool
651 + */
442 652 private function is_current_version( $transient ) {
443 653 if ( empty( $transient->checked ) || ! isset( $transient->checked[ $this->plugin_folder ] ) ) {
444 654 return false;
445 655 }
446 656
447 - $response = ! isset( $transient->response ) || empty( $transient->response );
657 + $response = empty( $transient->response );
658 +
448 659 if ( $response ) {
449 660 return true;
450 661 }
451 662
452 - return isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) && $transient->checked[ $this->plugin_folder ] === $transient->response[ $this->plugin_folder ]->new_version;
663 + return isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) && $transient->checked[ $this->plugin_folder ] === $transient->response[ $this->plugin_folder ]->new_version; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
453 664 }
454 665
666 + /**
667 + * @return bool
668 + */
455 669 private function has_been_cleared() {
456 670 $last_cleared = get_option( 'frm_last_cleared' );
457 -
458 - return ( $last_cleared && $last_cleared > gmdate( 'Y-m-d H:i:s', strtotime( '-5 minutes' ) ) );
671 + return $last_cleared && $last_cleared > gmdate( 'Y-m-d H:i:s', strtotime( '-5 minutes' ) );
459 672 }
460 673
674 + /**
675 + * @return void
676 + */
461 677 private function cleared_plugins() {
462 678 update_option( 'frm_last_cleared', gmdate( 'Y-m-d H:i:s' ) );
463 679 }
464 680
681 + /**
682 + * @return void
683 + */
465 684 private function is_license_revoked() {
466 - if ( empty( $this->license ) || empty( $this->plugin_slug ) || isset( $_POST['license'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
685 + if ( ! $this->license || ! $this->plugin_slug || isset( $_POST['license'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
467 686 return;
468 687 }
469 688
689 + if ( $this->get_defined_license() ) {
690 + // Don't check if the license is defined in wp-config.php since we can't remove it.
691 + return;
692 + }
693 +
470 694 // Only check weekly.
471 - if ( $this->checked_recently( '7 days' ) ) {
695 + if ( $this->checked_recently( '7 days' ) || $this->is_running() ) {
472 696 return;
473 697 }
474 698
475 - $this->update_last_checked();
699 + $response = $this->get_license_status();
476 700
477 - $response = $this->get_license_status();
701 + if ( ! empty( $this->save_status['response_code'] ) && 429 === $this->save_status['response_code'] ) {
702 + // If we got a rate limit response, don't clear the license.
703 + return;
704 + }
705 +
478 706 if ( 'revoked' === $response['status'] || 'blocked' === $response['status'] || 'disabled' === $response['status'] || 'missing' === $response['status'] ) {
479 707 $this->clear_license();
480 708 }
481 709 }
@@ -482,53 +710,100 @@
482 710
483 711 /**
484 712 * Has this been checked too recently?
485 713 *
486 - * @param string $time ie. '1 day'
487 - * @return bool
714 + * @param string $time ie. '1 day'.
715 + *
716 + * @return bool True if the last check time was recent.
488 717 */
489 718 private function checked_recently( $time ) {
490 719 $last_checked = $this->last_checked();
491 - $time_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-' . $time ) );
492 - return $last_checked && $last_checked > $time_ago;
720 +
721 + if ( ! $last_checked ) {
722 + return false;
723 + }
724 +
725 + $is_429 = isset( $last_checked['response_code'] ) && 429 === $last_checked['response_code'];
726 +
727 + // If the last check was a a rate limit error, adjust the check time.
728 + if ( $is_429 ) {
729 + // If the time is 2 minutes, do not adjust it so people
730 + // can still try to activate again in 2 minutes (and not 5).
731 + switch ( $time ) {
732 + case '7 days':
733 + // 7 days is used to check for license revocation.
734 + $time = '30 minutes';
735 + break;
736 + case '1 day':
737 + // 1 day is used for defined license constants.
738 + // The response won't change often, so we can check less frequently.
739 + $time = '2 hours';
740 + break;
741 + }
742 + }
743 +
744 + if ( empty( $last_checked['time'] ) ) {
745 + // If we do not have time, data is really old.
746 + return false;
747 + }
748 +
749 + $time_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-' . $time ) );
750 + return $last_checked['time'] > $time_ago;
493 751 }
494 752
495 753 /**
496 - * @return bool|string
754 + * @since 6.8.3 Switched to an array to store extra response info.
755 + *
756 + * @return array
497 757 */
498 758 private function last_checked() {
499 - if ( is_multisite() ) {
500 - $last_checked = get_site_option( $this->transient_key() );
501 - } else {
502 - $last_checked = get_option( $this->transient_key() );
759 + $last_checked = is_multisite() ? get_site_option( $this->transient_key() ) : get_option( $this->transient_key() );
760 +
761 + if ( $last_checked && ! is_array( $last_checked ) ) {
762 + // Get string into array for existing values.
763 + $last_checked = array( 'time' => $last_checked );
503 764 }
504 - return $last_checked;
765 +
766 + return $last_checked ? $last_checked : array();
505 767 }
506 768
507 769 /**
770 + * @since 6.30 Added the $is_valid param.
771 + *
772 + * @param bool $is_valid
773 + *
508 774 * @return void
509 775 */
510 - private function update_last_checked() {
776 + private function update_last_checked( $is_valid ) {
777 + $this->save_response['time'] = gmdate( 'Y-m-d H:i:s' );
778 + $this->save_response['is_valid'] = $is_valid;
779 +
511 780 if ( is_multisite() ) {
512 - update_site_option( $this->transient_key(), gmdate( 'Y-m-d H:i:s' ) );
781 + update_site_option( $this->transient_key(), $this->save_response );
513 782 } else {
514 - update_option( $this->transient_key(), gmdate( 'Y-m-d H:i:s' ) );
783 + update_option( $this->transient_key(), $this->save_response );
515 784 }
516 785 }
517 786
518 787 /**
519 788 * Use a new cache after the license is changed, or Formidable is updated.
789 + *
790 + * @return string
520 791 */
521 792 private function transient_key() {
522 793 return 'frm_' . md5( sanitize_key( $this->license . '_' . $this->plugin_slug ) );
523 794 }
524 795
796 + /**
797 + * @return void
798 + */
525 799 public static function activate() {
526 800 FrmAppHelper::permission_check( 'frm_change_settings' );
527 801 check_ajax_referer( 'frm_ajax', 'nonce' );
528 802
529 803 $license = stripslashes( FrmAppHelper::get_param( 'license', '', 'post', 'sanitize_text_field' ) );
530 - if ( empty( $license ) ) {
804 +
805 + if ( ! $license ) {
531 806 wp_send_json(
532 807 array(
533 808 'message' => __( 'Oops! You forgot to enter your license number.', 'formidable' ),
534 809 'success' => false,
@@ -538,13 +813,20 @@
538 813
539 814 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
540 815 $response = self::activate_license_for_plugin( $license, $plugin_slug );
541 816
817 + FrmInbox::clear_cache();
818 +
542 819 wp_send_json( $response );
543 820 }
544 821
545 822 /**
546 823 * @since 4.08
824 + *
825 + * @param string $license The license key.
826 + * @param string $plugin_slug The plugin slug.
827 + *
828 + * @return array The response from the license activation.
547 829 */
548 830 public static function activate_license_for_plugin( $license, $plugin_slug ) {
549 831 $this_plugin = self::get_addon( $plugin_slug );
550 832 return $this_plugin->activate_license( $license );
@@ -549,8 +831,13 @@
549 831 $this_plugin = self::get_addon( $plugin_slug );
550 832 return $this_plugin->activate_license( $license );
551 833 }
552 834
835 + /**
836 + * @param string $license The license key.
837 + *
838 + * @return array The response from the license activation.
839 + */
553 840 private function activate_license( $license ) {
554 841 $this->set_license( $license );
555 842 $this->license = $license;
556 843
@@ -559,12 +846,15 @@
559 846 $response = $this->get_license_status();
560 847 $response['message'] = '';
561 848 $response['success'] = false;
562 849
850 + $is_valid = false;
851 +
563 852 if ( $response['error'] ) {
564 853 $response['message'] = $response['status'];
565 854 } else {
566 855 $messages = $this->get_messages();
856 +
567 857 if ( is_string( $response['status'] ) && isset( $messages[ $response['status'] ] ) ) {
568 858 $response['message'] = $messages[ $response['status'] ];
569 859 } else {
570 860 $response['message'] = FrmAppHelper::kses( $response['status'], array( 'a' ) );
@@ -569,17 +859,17 @@
569 859 } else {
570 860 $response['message'] = FrmAppHelper::kses( $response['status'], array( 'a' ) );
571 861 }
572 862
573 - $is_valid = false;
574 863 if ( 'valid' === $response['status'] ) {
575 864 $is_valid = 'valid';
576 865 $response['success'] = true;
577 866 }
578 - $this->set_active( $is_valid );
867 +
868 + $this->maybe_set_active( $is_valid );
579 869 }
580 870
581 - $this->update_last_checked();
871 + $this->update_last_checked( (bool) $is_valid );
582 872
583 873 return $response;
584 874 }
585 875
@@ -601,19 +891,26 @@
601 891 )
602 892 );
603 893 }
604 894
895 + /**
896 + * @return array
897 + */
605 898 private function get_license_status() {
899 + $this->set_running();
900 +
606 901 $response = array(
607 902 'status' => 'missing',
608 903 'error' => true,
609 904 );
610 - if ( empty( $this->license ) ) {
905 +
906 + if ( ! $this->license ) {
611 907 $response['error'] = false;
612 -
613 908 return $response;
614 909 }
615 910
911 + $is_valid = false;
912 +
616 913 try {
617 914 $response['error'] = false;
618 915 $license_data = $this->send_mothership_request( 'activate_license' );
619 916
@@ -618,10 +915,12 @@
618 915 $license_data = $this->send_mothership_request( 'activate_license' );
619 916
620 917 // $license_data->license will be either "valid" or "invalid"
621 918 if ( is_array( $license_data ) ) {
622 - if ( isset( $license_data['license'] ) && in_array( $license_data['license'], array( 'valid', 'invalid' ), true ) ) {
623 - $response['status'] = $license_data['license'];
919 + if ( ! empty( $license_data['license'] ) && in_array( $license_data['license'], array( 'valid', 'invalid' ), true ) ) {
920 + $response['status'] = $license_data['license'];
921 + $this->save_status['status'] = $license_data['license'];
922 + $is_valid = 'valid' === $license_data['license'];
624 923 }
625 924 } else {
626 925 $response['status'] = $license_data;
627 926 }
@@ -628,11 +927,16 @@
628 927 } catch ( Exception $e ) {
629 928 $response['status'] = $e->getMessage();
630 929 }
631 930
931 + $this->update_last_checked( $is_valid );
932 + $this->done_running();
632 933 return $response;
633 934 }
634 935
936 + /**
937 + * @return array
938 + */
635 939 private function get_messages() {
636 940 return array(
637 941 'valid' => __( 'Your license has been activated. Enjoy!', 'formidable' ),
638 942 'invalid' => __( 'That license key is invalid', 'formidable' ),
@@ -645,8 +949,10 @@
645 949 }
646 950
647 951 /**
648 952 * @since 4.03
953 + *
954 + * @return void
649 955 */
650 956 public static function reset_cache() {
651 957 FrmAppHelper::permission_check( 'frm_change_settings' );
652 958 check_ajax_referer( 'frm_ajax', 'nonce' );
@@ -661,8 +967,11 @@
661 967
662 968 wp_send_json( $response );
663 969 }
664 970
971 + /**
972 + * @return void
973 + */
665 974 public static function deactivate() {
666 975 FrmAppHelper::permission_check( 'frm_change_settings' );
667 976 check_ajax_referer( 'frm_ajax', 'nonce' );
668 977
@@ -671,11 +980,13 @@
671 980 $response = array(
672 981 'success' => false,
673 982 'message' => '',
674 983 );
984 +
675 985 try {
676 986 // $license_data->license will be either "deactivated" or "failed"
677 987 $license_data = $this_plugin->send_mothership_request( 'deactivate_license' );
988 +
678 989 if ( is_array( $license_data ) && 'deactivated' === $license_data['license'] ) {
679 990 $response['success'] = true;
680 991 $response['message'] = __( 'That license was removed successfully', 'formidable' );
681 992 } else {
@@ -685,8 +996,9 @@
685 996 $response['message'] = $e->getMessage();
686 997 }
687 998
688 999 $this_plugin->clear_license();
1000 + FrmInbox::clear_cache();
689 1001
690 1002 wp_send_json( $response );
691 1003 }
692 1004
@@ -691,18 +1003,21 @@
691 1003 }
692 1004
693 1005 /**
694 1006 * @since 4.03
1007 + *
1008 + * @return FrmAddon
695 1009 */
696 1010 private static function set_license_from_post() {
697 1011 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
698 1012 $this_plugin = self::get_addon( $plugin_slug );
699 - $license = $this_plugin->get_license();
700 - $this_plugin->license = $license;
1013 + $this_plugin->license = $this_plugin->get_license();
701 1014 return $this_plugin;
702 1015 }
703 1016
704 1017 /**
1018 + * @param string $action
1019 + *
705 1020 * @return string
706 1021 */
707 1022 public function send_mothership_request( $action ) {
708 1023 $api_params = array(
@@ -709,8 +1024,9 @@
709 1024 'edd_action' => $action,
710 1025 'license' => $this->license,
711 1026 'url' => home_url(),
712 1027 );
1028 +
713 1029 if ( is_numeric( $this->download_id ) ) {
714 1030 $api_params['item_id'] = absint( $this->download_id );
715 1031 } else {
716 1032 $api_params['item_name'] = rawurlencode( $this->plugin_name );
@@ -721,31 +1037,32 @@
721 1037 'timeout' => 25,
722 1038 'user-agent' => $this->plugin_slug . '/' . $this->version . '; ' . get_bloginfo( 'url' ),
723 1039 );
724 1040
725 - $resp = wp_remote_post(
1041 + $resp = wp_remote_post(
726 1042 $this->store_url . '?l=' . urlencode( base64_encode( $this->license ) ),
727 1043 $arg_array
728 1044 );
729 - $body = wp_remote_retrieve_body( $resp );
1045 + $response_code = wp_remote_retrieve_response_code( $resp );
1046 + $body = wp_remote_retrieve_body( $resp );
1047 + $this->save_status = array( 'response_code' => $response_code );
1048 + $this->save_response['response_code'] = $response_code;
730 1049
731 1050 $message = __( 'Your License Key was invalid', 'formidable' );
1051 +
732 1052 if ( is_wp_error( $resp ) ) {
733 1053 $link = FrmAppHelper::admin_upgrade_link( 'api', 'knowledgebase/why-cant-i-activate-formidable-pro/' );
734 1054 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
735 - $message = sprintf( __( 'You had an error communicating with the Formidable API. %1$sClick here%2$s for more information.', 'formidable' ), '<a href="' . esc_url( $link ) . '" target="_blank">', '</a>' );
1055 + $message = sprintf( __( 'You had an error communicating with the Formidable API. %1$sClick here%2$s for more information.', 'formidable' ), '<a href="' . esc_url( $link ) . '" target="_blank">', '</a>' ); // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
736 1056 $message .= ' ' . $resp->get_error_message();
737 1057 } elseif ( 'error' === $body || is_wp_error( $body ) ) {
738 1058 $message = __( 'You had an HTTP error connecting to the Formidable API', 'formidable' );
739 1059 } else {
740 1060 $json_res = json_decode( $body, true );
1061 +
741 1062 if ( null !== $json_res ) {
742 - if ( is_array( $json_res ) && isset( $json_res['error'] ) ) {
743 - $message = $json_res['error'];
744 - } else {
745 - $message = $json_res;
746 - }
747 - } elseif ( isset( $resp['response'] ) && isset( $resp['response']['code'] ) ) {
1063 + $message = is_array( $json_res ) && isset( $json_res['error'] ) ? $json_res['error'] : $json_res;
1064 + } elseif ( ! empty( $resp['response'] ) && ! empty( $resp['response']['code'] ) ) {
748 1065 $resp['body'] = wp_strip_all_tags( $resp['body'] );
749 1066
750 1067 $message = sprintf(
751 1068 /* translators: %1$s: Error code, %2$s: Error message */
@@ -753,13 +1070,16 @@
753 1070 esc_html( $resp['response']['code'] ),
754 1071 $resp['response']['message'] . ' ' . $resp['body']
755 1072 );
756 1073 }
757 - }
1074 + }//end if
758 1075
759 1076 return $message;
760 1077 }
761 1078
1079 + /**
1080 + * @return void
1081 + */
762 1082 public function manually_queue_update() {
763 1083 $updates = new stdClass();
764 1084 $updates->last_checked = 0;
765 1085 $updates->response = array();
@@ -766,6 +1086,46 @@
766 1086 $updates->translations = array();
767 1087 $updates->no_update = array();
768 1088 $updates->checked = array();
769 1089 set_site_transient( 'update_plugins', $updates );
1090 + }
1091 +
1092 + /**
1093 + * Set the transient key for the lock. It should be unique to the license.
1094 + *
1095 + * @since 6.8.3
1096 + *
1097 + * @return bool
1098 + */
1099 + protected function lock_key() {
1100 + return $this->transient_lock_key . '_' . $this->license;
1101 + }
1102 +
1103 + /**
1104 + * Prevent multiple requests from running at the same time.
1105 + *
1106 + * @since 6.8.3
1107 + *
1108 + * @return bool
1109 + */
1110 + protected function is_running() {
1111 + return get_transient( $this->lock_key() );
1112 + }
1113 +
1114 + /**
1115 + * @since 6.8.3
1116 + *
1117 + * @return void
1118 + */
1119 + protected function set_running() {
1120 + set_transient( $this->lock_key(), true, 2 * MINUTE_IN_SECONDS );
1121 + }
1122 +
1123 + /**
1124 + * @since 6.8.3
1125 + *
1126 + * @return void
1127 + */
1128 + protected function done_running() {
1129 + delete_transient( $this->lock_key() );
770 1130 }
771 1131 }