PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.28
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.28
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
formidable / classes / models / FrmAddon.php

FrmAddon.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.28, at classes/models/FrmAddon.php

1,101 lines 27.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 /** @phpstan-consistent-constructor */
7 class FrmAddon {
8
9 /**
10 * @var string
11 */
12 public $store_url = 'https://formidableforms.com';
13
14 /**
15 * @var int|string|null
16 */
17 public $download_id;
18
19 /**
20 * @var string|null
21 */
22 public $plugin_file;
23
24 /**
25 * @var string
26 */
27 public $plugin_folder;
28
29 /**
30 * @var string
31 */
32 public $plugin_name;
33
34 /**
35 * @var string
36 */
37 public $plugin_slug;
38
39 /**
40 * @var string
41 */
42 public $option_name;
43
44 /**
45 * @var string|null
46 */
47 public $version;
48
49 /**
50 * @var string
51 */
52 public $author = 'Strategy11';
53
54 /**
55 * @var bool
56 */
57 public $is_parent_licence = false;
58
59 /**
60 * @var bool
61 */
62 public $needs_license = true;
63
64 /**
65 * @var bool
66 */
67 private $is_expired_addon = false;
68
69 /**
70 * @var string|null
71 */
72 public $license;
73
74 /**
75 * @var bool
76 */
77 protected $get_beta = false;
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
111 public function __construct() {
112 if ( empty( $this->plugin_slug ) ) {
113 $this->plugin_slug = preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->plugin_name ) ) );
114 }
115
116 if ( empty( $this->option_name ) ) {
117 $this->option_name = 'edd_' . $this->plugin_slug . '_license_';
118 }
119
120 $this->plugin_folder = plugin_basename( $this->plugin_file );
121 $this->license = $this->get_license();
122
123 add_filter( 'frm_installed_addons', array( &$this, 'insert_installed_addon' ) );
124 $this->edd_plugin_updater();
125 }
126
127 /**
128 * @return void
129 */
130 public static function load_hooks() {
131 add_filter( 'frm_include_addon_page', '__return_true' );
132 new static();
133 }
134
135 /**
136 * @param array $plugins
137 *
138 * @return array
139 */
140 public function insert_installed_addon( $plugins ) {
141 $plugins[ $this->plugin_slug ] = $this;
142 return $plugins;
143 }
144
145 /**
146 * @param string $plugin_slug
147 *
148 * @return false|FrmAddon
149 */
150 public static function get_addon( $plugin_slug ) {
151 $plugins = apply_filters( 'frm_installed_addons', array() );
152 return $plugins[ $plugin_slug ] ?? false;
153 }
154
155 /**
156 * @return void
157 */
158 public function edd_plugin_updater() {
159 $this->is_license_revoked();
160 $license = $this->license;
161
162 add_action( 'after_plugin_row_' . plugin_basename( $this->plugin_file ), array( $this, 'maybe_show_license_message' ), 10, 2 );
163
164 if ( ! $license ) {
165 return;
166 }
167
168 if ( 'formidable/formidable.php' !== $this->plugin_folder ) {
169 add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
170 }
171
172 add_filter( 'site_transient_update_plugins', array( &$this, 'clear_expired_download' ) );
173 }
174
175 /**
176 * Updates information on the "View version 6.15 details" page with custom data.
177 *
178 * @uses api_request()
179 *
180 * @param mixed $_data
181 * @param string $_action
182 * @param object|null $_args
183 *
184 * @return object Data.
185 */
186 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
187 if ( $_action !== 'plugin_information' ) {
188 return $_data;
189 }
190
191 $slug = basename( $this->plugin_file, '.php' );
192 $slug2 = str_replace( '/' . $slug . '.php', '', $this->plugin_folder );
193
194 if ( empty( $_args->slug ) || ( $_args->slug !== $slug && $_args->slug !== $slug2 ) ) {
195 return $_data;
196 }
197
198 $item_id = $this->download_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 ) ) {
215 $_data = array(
216 'name' => $this->plugin_name,
217 'excerpt' => '',
218 'changelog' => 'See the full changelog at <a href="' . esc_url( $this->store_url . '/changelog/' ) . '"></a>',
219 'banners' => array(
220 'high' => '',
221 'low' => 'https://ps.w.org/formidable/assets/banner-1544x500.png',
222 ),
223 );
224 }
225
226 $_data['sections'] = array(
227 'description' => $_data['excerpt'],
228 'changelog' => $_data['changelog'],
229 );
230 $_data['author'] = '<a href="' . esc_url( $this->store_url ) . '">' . esc_html( $this->author ) . '</a>';
231 $_data['homepage'] = $this->store_url;
232
233 return (object) $_data;
234 }
235
236 /**
237 * @return string
238 */
239 public function get_license() {
240 $license = $this->maybe_get_pro_license();
241
242 if ( $license ) {
243 return $license;
244 }
245
246 $license = trim( get_option( $this->option_name . 'key' ) );
247
248 return $license ? $license : $this->activate_defined_license();
249 }
250
251 /**
252 * @since 3.04.03
253 *
254 * @return false|string
255 */
256 protected function maybe_get_pro_license() {
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';
259
260 if ( ! $get_license ) {
261 return false;
262 }
263
264 $api = new FrmFormApi();
265 $api->get_pro_updater();
266 $license = $api->get_license();
267
268 if ( ! $license ) {
269 return false;
270 }
271
272 $this->get_api_info( $license );
273
274 return $this->is_parent_licence ? $license : false;
275 }
276
277 /**
278 * Activate the license in wp-config.php
279 *
280 * @since 2.04
281 *
282 * @return string
283 */
284 public function activate_defined_license() {
285 $license = $this->get_defined_license();
286
287 if ( $license && ! $this->is_active() && ! $this->checked_recently( '1 day' ) ) {
288 $response = $this->activate_license( $license );
289
290 if ( ! $response['success'] ) {
291 $license = '';
292 }
293 }
294
295 return $license;
296 }
297
298 /**
299 * Check the wp-config.php for the license key
300 *
301 * @since 2.04
302 *
303 * @return false|string
304 */
305 public function get_defined_license() {
306 $constant_name = 'FRM_' . strtoupper( $this->plugin_slug ) . '_LICENSE';
307 return defined( $constant_name ) ? constant( $constant_name ) : false;
308 }
309
310 /**
311 * @param string $license
312 *
313 * @return void
314 */
315 public function set_license( $license ) {
316 update_option( $this->option_name . 'key', $license );
317 }
318
319 /**
320 * @return mixed
321 */
322 public function is_active() {
323 return get_option( $this->option_name . 'active' );
324 }
325
326 /**
327 * @since 3.04.03
328 *
329 * @param array|string $error
330 *
331 * @return void
332 */
333 public function maybe_clear_license( $error ) {
334 if ( is_array( $error ) && $error['code'] === 'disabled' && $error['license'] === $this->license ) {
335 $this->clear_license();
336 }
337 }
338
339 /**
340 * @return void
341 */
342 public function clear_license() {
343 delete_option( $this->option_name . 'active' );
344 delete_option( $this->option_name . 'key' );
345
346 if ( ! $this->should_clear_cache ) {
347 return;
348 }
349
350 delete_site_option( $this->transient_key() );
351 delete_option( $this->transient_key() );
352 $this->delete_cache();
353 $this->should_clear_cache = true;
354 }
355
356 /**
357 * Don't save an invalid license.
358 *
359 * @since 6.8.3
360 *
361 * @param bool $is_valid If license activation was successful.
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 $is_active
382 *
383 * @return void
384 */
385 public function set_active( $is_active ) {
386 $this->delete_cache();
387 FrmAppHelper::save_combined_js();
388 $this->update_pro_capabilities();
389 }
390
391 /**
392 * Updates roles capabilities after pro license is active.
393 *
394 * @since 5.0
395 *
396 * @return void
397 */
398 protected function update_pro_capabilities() {
399 global $wp_roles;
400
401 if ( ! function_exists( 'get_editable_roles' ) ) {
402 require_once ABSPATH . 'wp-admin/includes/user.php';
403 }
404
405 $caps = FrmAppHelper::frm_capabilities( 'pro_only' );
406 $roles = get_editable_roles();
407 $settings = new FrmSettings();
408
409 foreach ( $caps as $cap => $cap_desc ) {
410 $cap_roles = (array) ( $settings->$cap ?? 'administrator' );
411
412 // Make sure administrators always have permissions.
413 if ( ! in_array( 'administrator', $cap_roles, true ) ) {
414 array_push( $cap_roles, 'administrator' );
415 }
416
417 foreach ( $roles as $role => $details ) {
418 if ( in_array( $role, $cap_roles, true ) ) {
419 $wp_roles->add_cap( $role, $cap );
420 } else {
421 $wp_roles->remove_cap( $role, $cap );
422 }
423 }
424 }
425 }
426
427 /**
428 * @since 3.04.03
429 *
430 * @return void
431 */
432 protected function delete_cache() {
433 delete_transient( 'frm_api_licence' );
434
435 // Cleanup option that has been removed.
436 delete_option( $this->option_name . 'last_activate' );
437
438 $api = new FrmFormApi( $this->license );
439 $api->reset_cached();
440
441 $api = new FrmFormTemplateApi( $this->license );
442 $api->reset_cached();
443
444 $api = new FrmApplicationApi( $this->license );
445 $api->reset_cached();
446
447 $api = new FrmSalesApi();
448 $api->reset_cached();
449
450 $api = new FrmStyleApi();
451 $api->reset_cached();
452 }
453
454 /**
455 * The Pro version includes the show_license_message function.
456 * We need an extra check before we allow it to show a message.
457 *
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
464 */
465 public function maybe_show_license_message( $file, $plugin ) {
466 if ( $this->is_expired_addon || isset( $plugin['package'] ) ) {
467 // Let's not show a ton of duplicate messages
468 return;
469 }
470
471 $this->show_license_message( $file, $plugin );
472 }
473
474 /**
475 * @param string $file File path of the plugin.
476 * @param array $plugin Plugin data.
477 *
478 * @return void
479 */
480 public function show_license_message( $file, $plugin ) {
481 $message = '';
482
483 if ( empty( $this->license ) ) {
484 /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
485 $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
486 } else {
487 $api = new FrmFormApi( $this->license );
488 $errors = $api->error_for_license();
489
490 if ( $errors ) {
491 $message = reset( $errors );
492 }
493 }
494
495 if ( ! $message ) {
496 return;
497 }
498
499 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
500 $id = sanitize_title( $plugin['Name'] ) . '-next';
501
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
505 echo '</p></div></td></tr>';
506 }
507
508 /**
509 * @param mixed $transient
510 *
511 * @return mixed
512 */
513 public function clear_expired_download( $transient ) {
514 if ( ! is_object( $transient ) ) {
515 return $transient;
516 }
517
518 if ( $this->is_current_version( $transient ) ) {
519 // Make sure it doesn't show there is an update if plugin is up-to-date.
520 if ( isset( $transient->response[ $this->plugin_folder ] ) ) {
521 unset( $transient->response[ $this->plugin_folder ] );
522 }
523 } elseif ( isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) ) {
524 $this->prepare_update_details( $transient->response[ $this->plugin_folder ] );
525
526 // If the transient has expired, clear the update and trigger it again
527 if ( $transient->response[ $this->plugin_folder ] === false ) {
528 if ( ! $this->has_been_cleared() ) {
529 $this->cleared_plugins();
530 $this->manually_queue_update();
531 }
532 unset( $transient->response[ $this->plugin_folder ] );
533 }
534 }
535
536 return $transient;
537 }
538
539 /**
540 * Check if the plugin information is correct to allow an update
541 *
542 * @since 3.04.03
543 *
544 * @param object $transient The current plugin info saved for update.
545 *
546 * @return void
547 */
548 private function prepare_update_details( &$transient ) {
549 $version_info = $transient;
550 $has_beta_url = ! empty( $version_info->beta );
551
552 if ( $this->get_beta && ! $has_beta_url ) {
553 $version_info = (object) $this->get_api_info( $this->license );
554 }
555
556 if ( empty( $version_info->new_version ) ) {
557 return;
558 }
559
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;
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 }
574 }
575
576 /**
577 * Get the API info for this plugin
578 *
579 * @since 3.04.03
580 *
581 * @param string $license The license key.
582 *
583 * @return array The API info for the plugin.
584 */
585 protected function get_api_info( $license ) {
586 $api = new FrmFormApi( $license );
587 $addon = $api->get_addon_for_license( $this );
588
589 // If there is no download url, this license does not apply to the addon
590 if ( isset( $addon['package'] ) ) {
591 $this->is_parent_licence = true;
592 } elseif ( isset( $addon['error'] ) ) {
593 // If the license is expired, we must assume all add-ons were packaged
594 $this->is_parent_licence = true;
595 $this->is_expired_addon = true;
596 }
597
598 return $addon;
599 }
600
601 /**
602 * Make sure transients don't stick around on sites that
603 * don't save the transient expiration
604 *
605 * @since 2.05.05
606 *
607 * @param object $version_info The version info for the plugin.
608 *
609 * @return void
610 */
611 private function clear_old_plugin_version( &$version_info ) {
612 $timeout = ! empty( $version_info->timeout ) ? $version_info->timeout : 0;
613
614 if ( ! $timeout || time() <= $timeout ) {
615 return;
616 }
617
618 // Cache is expired.
619 $version_info = false;
620 $api = new FrmFormApi( $this->license );
621 $api->reset_cached();
622 }
623
624 /**
625 * The beta url is always included if the download has a beta.
626 * Check if the beta should be downloaded.
627 *
628 * @since 3.04.03
629 *
630 * @param object $version_info The version info for the plugin.
631 *
632 * @return void
633 */
634 private function maybe_use_beta_url( &$version_info ) {
635 if ( $this->get_beta && ! empty( $version_info->beta ) ) {
636 $version_info->new_version = $version_info->beta['version'];
637 $version_info->package = $version_info->beta['package'];
638
639 if ( ! empty( $version_info->plugin ) ) {
640 $version_info->plugin = $version_info->beta['plugin'];
641 }
642 }
643 }
644
645 /**
646 * @param object $transient
647 *
648 * @return bool
649 */
650 private function is_current_version( $transient ) {
651 if ( empty( $transient->checked ) || ! isset( $transient->checked[ $this->plugin_folder ] ) ) {
652 return false;
653 }
654
655 $response = empty( $transient->response );
656
657 if ( $response ) {
658 return true;
659 }
660
661 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
662 }
663
664 /**
665 * @return bool
666 */
667 private function has_been_cleared() {
668 $last_cleared = get_option( 'frm_last_cleared' );
669 return $last_cleared && $last_cleared > gmdate( 'Y-m-d H:i:s', strtotime( '-5 minutes' ) );
670 }
671
672 /**
673 * @return void
674 */
675 private function cleared_plugins() {
676 update_option( 'frm_last_cleared', gmdate( 'Y-m-d H:i:s' ) );
677 }
678
679 /**
680 * @return void
681 */
682 private function is_license_revoked() {
683 if ( empty( $this->license ) || empty( $this->plugin_slug ) || isset( $_POST['license'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
684 return;
685 }
686
687 if ( $this->get_defined_license() ) {
688 // Don't check if the license is defined in wp-config.php since we can't remove it.
689 return;
690 }
691
692 // Only check weekly.
693 if ( $this->checked_recently( '7 days', 'valid' ) || $this->is_running() ) {
694 return;
695 }
696
697 $response = $this->get_license_status();
698
699 if ( 'revoked' === $response['status'] || 'blocked' === $response['status'] || 'disabled' === $response['status'] || 'missing' === $response['status'] ) {
700 $this->clear_license();
701 }
702 }
703
704 /**
705 * Has this been checked too recently?
706 *
707 * @param string $time ie. '1 day'.
708 * @param string $required_status Return false if the last check does not match. ie 'valid'.
709 *
710 * @return bool
711 */
712 private function checked_recently( $time, $required_status = '' ) {
713 $last_checked = $this->last_checked();
714 $is_429 = isset( $last_checked['response_code'] ) && 429 === $last_checked['response_code'];
715
716 if ( $is_429 ) {
717 // If the last check was a a rate limit, we'll need to check again sooner.
718 $time = '5 minutes';
719 $required_status = '';
720 }
721
722 if ( $required_status && ( ! isset( $last_checked['status'] ) || $last_checked['status'] !== $required_status ) ) {
723 // If the last check was invalid, we don't need to check again.
724 return true;
725 }
726
727 $checked_time = $last_checked['time'] ?? false;
728 $time_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-' . $time ) );
729 return $checked_time && $checked_time > $time_ago;
730 }
731
732 /**
733 * @since 6.8.3 Switched to an array to store extra response info.
734 *
735 * @return array
736 */
737 private function last_checked() {
738 $last_checked = is_multisite() ? get_site_option( $this->transient_key() ) : get_option( $this->transient_key() );
739
740 if ( $last_checked && ! is_array( $last_checked ) ) {
741 // Get string into array for existing values.
742 $last_checked = array( 'time' => $last_checked );
743 }
744
745 return $last_checked ? $last_checked : array();
746 }
747
748 /**
749 * @return void
750 */
751 private function update_last_checked() {
752 $this->save_response['time'] = gmdate( 'Y-m-d H:i:s' );
753
754 if ( is_multisite() ) {
755 update_site_option( $this->transient_key(), $this->save_response );
756 } else {
757 update_option( $this->transient_key(), $this->save_response );
758 }
759 }
760
761 /**
762 * Use a new cache after the license is changed, or Formidable is updated.
763 *
764 * @return string
765 */
766 private function transient_key() {
767 return 'frm_' . md5( sanitize_key( $this->license . '_' . $this->plugin_slug ) );
768 }
769
770 /**
771 * @return void
772 */
773 public static function activate() {
774 FrmAppHelper::permission_check( 'frm_change_settings' );
775 check_ajax_referer( 'frm_ajax', 'nonce' );
776
777 $license = stripslashes( FrmAppHelper::get_param( 'license', '', 'post', 'sanitize_text_field' ) );
778
779 if ( ! $license ) {
780 wp_send_json(
781 array(
782 'message' => __( 'Oops! You forgot to enter your license number.', 'formidable' ),
783 'success' => false,
784 )
785 );
786 }
787
788 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
789 $response = self::activate_license_for_plugin( $license, $plugin_slug );
790
791 FrmInbox::clear_cache();
792
793 wp_send_json( $response );
794 }
795
796 /**
797 * @since 4.08
798 *
799 * @param string $license The license key.
800 * @param string $plugin_slug The plugin slug.
801 *
802 * @return array The response from the license activation.
803 */
804 public static function activate_license_for_plugin( $license, $plugin_slug ) {
805 $this_plugin = self::get_addon( $plugin_slug );
806 return $this_plugin->activate_license( $license );
807 }
808
809 /**
810 * @param string $license The license key.
811 *
812 * @return array The response from the license activation.
813 */
814 private function activate_license( $license ) {
815 $this->set_license( $license );
816 $this->license = $license;
817
818 $this->die_if_not_allowed();
819
820 $response = $this->get_license_status();
821 $response['message'] = '';
822 $response['success'] = false;
823
824 if ( $response['error'] ) {
825 $response['message'] = $response['status'];
826 } else {
827 $messages = $this->get_messages();
828
829 if ( is_string( $response['status'] ) && isset( $messages[ $response['status'] ] ) ) {
830 $response['message'] = $messages[ $response['status'] ];
831 } else {
832 $response['message'] = FrmAppHelper::kses( $response['status'], array( 'a' ) );
833 }
834
835 $is_valid = false;
836
837 if ( 'valid' === $response['status'] ) {
838 $is_valid = 'valid';
839 $response['success'] = true;
840 }
841 $this->maybe_set_active( $is_valid );
842 }
843
844 $this->update_last_checked();
845
846 return $response;
847 }
848
849 /**
850 * Prevent this check from happening more than once per minute with the same license.
851 *
852 * @return void
853 */
854 private function die_if_not_allowed() {
855 if ( ! $this->checked_recently( '2 minutes' ) ) {
856 return;
857 }
858
859 // Don't check more than once per minute.
860 wp_send_json(
861 array(
862 'message' => __( 'Please wait two minutes before trying again.', 'formidable' ),
863 'success' => false,
864 )
865 );
866 }
867
868 /**
869 * @return array
870 */
871 private function get_license_status() {
872 $this->set_running();
873
874 $response = array(
875 'status' => 'missing',
876 'error' => true,
877 );
878
879 if ( empty( $this->license ) ) {
880 $response['error'] = false;
881 return $response;
882 }
883
884 try {
885 $response['error'] = false;
886 $license_data = $this->send_mothership_request( 'activate_license' );
887
888 // $license_data->license will be either "valid" or "invalid"
889 if ( is_array( $license_data ) ) {
890 if ( ! empty( $license_data['license'] ) && in_array( $license_data['license'], array( 'valid', 'invalid' ), true ) ) {
891 $response['status'] = $license_data['license'];
892 $this->save_status['status'] = $license_data['license'];
893 }
894 } else {
895 $response['status'] = $license_data;
896 }
897 } catch ( Exception $e ) {
898 $response['status'] = $e->getMessage();
899 }
900
901 $this->update_last_checked();
902 $this->done_running();
903 return $response;
904 }
905
906 /**
907 * @return array
908 */
909 private function get_messages() {
910 return array(
911 'valid' => __( 'Your license has been activated. Enjoy!', 'formidable' ),
912 'invalid' => __( 'That license key is invalid', 'formidable' ),
913 'expired' => __( 'That license is expired', 'formidable' ),
914 'revoked' => __( 'That license has been refunded', 'formidable' ),
915 'no_activations_left' => __( 'That license has been used on too many sites', 'formidable' ),
916 'invalid_item_id' => __( 'Oops! That is the wrong license key for this plugin.', 'formidable' ),
917 'missing' => __( 'That license key is invalid', 'formidable' ),
918 );
919 }
920
921 /**
922 * @since 4.03
923 *
924 * @return void
925 */
926 public static function reset_cache() {
927 FrmAppHelper::permission_check( 'frm_change_settings' );
928 check_ajax_referer( 'frm_ajax', 'nonce' );
929
930 $this_plugin = self::set_license_from_post();
931 $this_plugin->delete_cache();
932
933 $response = array(
934 'success' => true,
935 'message' => __( 'Cache cleared', 'formidable' ),
936 );
937
938 wp_send_json( $response );
939 }
940
941 /**
942 * @return void
943 */
944 public static function deactivate() {
945 FrmAppHelper::permission_check( 'frm_change_settings' );
946 check_ajax_referer( 'frm_ajax', 'nonce' );
947
948 $this_plugin = self::set_license_from_post();
949
950 $response = array(
951 'success' => false,
952 'message' => '',
953 );
954
955 try {
956 // $license_data->license will be either "deactivated" or "failed"
957 $license_data = $this_plugin->send_mothership_request( 'deactivate_license' );
958
959 if ( is_array( $license_data ) && 'deactivated' === $license_data['license'] ) {
960 $response['success'] = true;
961 $response['message'] = __( 'That license was removed successfully', 'formidable' );
962 } else {
963 $response['message'] = __( 'There was an error deactivating your license.', 'formidable' );
964 }
965 } catch ( Exception $e ) {
966 $response['message'] = $e->getMessage();
967 }
968
969 $this_plugin->clear_license();
970 FrmInbox::clear_cache();
971
972 wp_send_json( $response );
973 }
974
975 /**
976 * @since 4.03
977 *
978 * @return FrmAddon
979 */
980 private static function set_license_from_post() {
981 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
982 $this_plugin = self::get_addon( $plugin_slug );
983 $license = $this_plugin->get_license();
984 $this_plugin->license = $license;
985 return $this_plugin;
986 }
987
988 /**
989 * @param string $action
990 *
991 * @return string
992 */
993 public function send_mothership_request( $action ) {
994 $api_params = array(
995 'edd_action' => $action,
996 'license' => $this->license,
997 'url' => home_url(),
998 );
999
1000 if ( is_numeric( $this->download_id ) ) {
1001 $api_params['item_id'] = absint( $this->download_id );
1002 } else {
1003 $api_params['item_name'] = rawurlencode( $this->plugin_name );
1004 }
1005
1006 $arg_array = array(
1007 'body' => $api_params,
1008 'timeout' => 25,
1009 'user-agent' => $this->plugin_slug . '/' . $this->version . '; ' . get_bloginfo( 'url' ),
1010 );
1011
1012 $resp = wp_remote_post(
1013 $this->store_url . '?l=' . urlencode( base64_encode( $this->license ) ),
1014 $arg_array
1015 );
1016 $body = wp_remote_retrieve_body( $resp );
1017 $this->save_status = array( 'response_code' => wp_remote_retrieve_response_code( $resp ) );
1018
1019 $message = __( 'Your License Key was invalid', 'formidable' );
1020
1021 if ( is_wp_error( $resp ) ) {
1022 $link = FrmAppHelper::admin_upgrade_link( 'api', 'knowledgebase/why-cant-i-activate-formidable-pro/' );
1023 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
1024 $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
1025 $message .= ' ' . $resp->get_error_message();
1026 } elseif ( 'error' === $body || is_wp_error( $body ) ) {
1027 $message = __( 'You had an HTTP error connecting to the Formidable API', 'formidable' );
1028 } else {
1029 $json_res = json_decode( $body, true );
1030
1031 if ( null !== $json_res ) {
1032 $message = is_array( $json_res ) && isset( $json_res['error'] ) ? $json_res['error'] : $json_res;
1033 } elseif ( ! empty( $resp['response'] ) && ! empty( $resp['response']['code'] ) ) {
1034 $resp['body'] = wp_strip_all_tags( $resp['body'] );
1035
1036 $message = sprintf(
1037 /* translators: %1$s: Error code, %2$s: Error message */
1038 esc_html__( 'There was a %1$s error: %2$s', 'formidable' ),
1039 esc_html( $resp['response']['code'] ),
1040 $resp['response']['message'] . ' ' . $resp['body']
1041 );
1042 }
1043 }//end if
1044
1045 return $message;
1046 }
1047
1048 /**
1049 * @return void
1050 */
1051 public function manually_queue_update() {
1052 $updates = new stdClass();
1053 $updates->last_checked = 0;
1054 $updates->response = array();
1055 $updates->translations = array();
1056 $updates->no_update = array();
1057 $updates->checked = array();
1058 set_site_transient( 'update_plugins', $updates );
1059 }
1060
1061 /**
1062 * Set the transient key for the lock. It should be unique to the license.
1063 *
1064 * @since 6.8.3
1065 *
1066 * @return bool
1067 */
1068 protected function lock_key() {
1069 return $this->transient_lock_key . '_' . $this->license;
1070 }
1071
1072 /**
1073 * Prevent multiple requests from running at the same time.
1074 *
1075 * @since 6.8.3
1076 *
1077 * @return bool
1078 */
1079 protected function is_running() {
1080 return get_transient( $this->lock_key() );
1081 }
1082
1083 /**
1084 * @since 6.8.3
1085 *
1086 * @return void
1087 */
1088 protected function set_running() {
1089 set_transient( $this->lock_key(), true, 2 * MINUTE_IN_SECONDS );
1090 }
1091
1092 /**
1093 * @since 6.8.3
1094 *
1095 * @return void
1096 */
1097 protected function done_running() {
1098 delete_transient( $this->lock_key() );
1099 }
1100 }
1101