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

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