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

666 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 die( 'You are not allowed to call this page directly.' );
5 }
6
7 class FrmAddon {
8 public $store_url = 'https://formidableforms.com';
9 public $download_id;
10 public $plugin_file;
11 public $plugin_folder;
12 public $plugin_name;
13 public $plugin_slug;
14 public $option_name;
15 public $version;
16 public $author = 'Strategy11';
17 public $is_parent_licence = false;
18 public $needs_license = true;
19 private $is_expired_addon = false;
20 public $license;
21 protected $get_beta = false;
22
23 public function __construct() {
24
25 if ( empty( $this->plugin_slug ) ) {
26 $this->plugin_slug = preg_replace( '/[^a-zA-Z0-9_\s]/', '', str_replace( ' ', '_', strtolower( $this->plugin_name ) ) );
27 }
28 if ( empty( $this->option_name ) ) {
29 $this->option_name = 'edd_' . $this->plugin_slug . '_license_';
30 }
31
32 $this->plugin_folder = plugin_basename( $this->plugin_file );
33 $this->license = $this->get_license();
34
35 add_filter( 'frm_installed_addons', array( &$this, 'insert_installed_addon' ) );
36 $this->edd_plugin_updater();
37 }
38
39 public static function load_hooks() {
40 add_filter( 'frm_include_addon_page', '__return_true' );
41 //new static();
42 }
43
44 public function insert_installed_addon( $plugins ) {
45 $plugins[ $this->plugin_slug ] = $this;
46
47 return $plugins;
48 }
49
50 public static function get_addon( $plugin_slug ) {
51 $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;
58 }
59
60 public function edd_plugin_updater() {
61
62 $this->is_license_revoked();
63 $license = $this->license;
64
65 add_action( 'after_plugin_row_' . plugin_basename( $this->plugin_file ), array( $this, 'maybe_show_license_message' ), 10, 2 );
66
67 if ( ! empty( $license ) ) {
68
69 if ( 'formidable/formidable.php' !== $this->plugin_folder ) {
70 add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
71 }
72
73 add_filter( 'site_transient_update_plugins', array( &$this, 'clear_expired_download' ) );
74 }
75 }
76
77 /**
78 * Updates information on the "View version x.x details" page with custom data.
79 *
80 * @uses api_request()
81 *
82 * @param mixed $_data
83 * @param string $_action
84 * @param object $_args
85 *
86 * @return object $_data
87 */
88 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
89
90 if ( $_action != 'plugin_information' ) {
91 return $_data;
92 }
93
94 $slug = basename( $this->plugin_file, '.php' );
95 if ( ! isset( $_args->slug ) || $_args->slug != $slug ) {
96 return $_data;
97 }
98
99 $item_id = $this->download_id;
100 if ( empty( $item_id ) ) {
101 $_data = array(
102 'name' => $this->plugin_name,
103 'excerpt' => '',
104 'changelog' => 'See the full changelog at <a href="' . esc_url( $this->store_url . '/changelog/' ) . '"></a>',
105 'banners' => array(
106 'high' => '',
107 'low' => 'https://ps.w.org/formidable/assets/banner-1544x500.png',
108 ),
109 );
110 } else {
111 $api = new FrmFormApi( $this->license );
112 $plugins = $api->get_api_info();
113 $_data = $plugins[ $item_id ];
114 }
115
116 $_data['sections'] = array(
117 'description' => $_data['excerpt'],
118 'changelog' => $_data['changelog'],
119 );
120 $_data['author'] = '<a href="' . esc_url( $this->store_url ) . '">' . esc_html( $this->author ) . '</a>';
121 $_data['homepage'] = $this->store_url;
122
123 return (object) $_data;
124 }
125
126 public function get_license() {
127 $license = $this->maybe_get_pro_license();
128 if ( ! empty( $license ) ) {
129 return $license;
130 }
131
132 $license = trim( get_option( $this->option_name . 'key' ) );
133 if ( empty( $license ) ) {
134 $license = $this->activate_defined_license();
135 }
136
137 return $license;
138 }
139
140 /**
141 * @since 3.04.03
142 */
143 protected function maybe_get_pro_license() {
144 // prevent a loop if $this is the pro plugin
145 $get_license = FrmAppHelper::pro_is_installed() && is_callable( 'FrmProAppHelper::get_updater' ) && $this->plugin_name != 'Formidable Pro';
146
147 if ( ! $get_license ) {
148 return false;
149 }
150
151 $api = new FrmFormApi();
152 $api->get_pro_updater();
153 $license = $api->get_license();
154 if ( empty( $license ) ) {
155 return false;
156 }
157
158 $this->get_api_info( $license );
159 if ( ! $this->is_parent_licence ) {
160 $license = false;
161 }
162
163 return $license;
164 }
165
166 /**
167 * Activate the license in wp-config.php
168 *
169 * @since 2.04
170 */
171 public function activate_defined_license() {
172 $license = $this->get_defined_license();
173 if ( ! empty( $license ) && ! $this->is_active() && $this->is_time_to_auto_activate() ) {
174 $response = $this->activate_license( $license );
175 $this->set_auto_activate_time();
176 if ( ! $response['success'] ) {
177 $license = '';
178 }
179 }
180
181 return $license;
182 }
183
184 /**
185 * Check the wp-config.php for the license key
186 *
187 * @since 2.04
188 */
189 public function get_defined_license() {
190 $consant_name = 'FRM_' . strtoupper( $this->plugin_slug ) . '_LICENSE';
191
192 return defined( $consant_name ) ? constant( $consant_name ) : false;
193 }
194
195 public function set_license( $license ) {
196 update_option( $this->option_name . 'key', $license );
197 }
198
199 /**
200 * If the license is in the config, limit the frequency of checks.
201 * The license may be entered incorrectly, so we don't want to check on every page load.
202 *
203 * @since 2.04
204 */
205 private function is_time_to_auto_activate() {
206 $last_try = get_option( $this->option_name . 'last_activate' );
207
208 return ( ! $last_try || $last_try < strtotime( '-1 day' ) );
209 }
210
211 private function set_auto_activate_time() {
212 update_option( $this->option_name . 'last_activate', time() );
213 }
214
215 public function is_active() {
216 return get_option( $this->option_name . 'active' );
217 }
218
219 /**
220 * @since 3.04.03
221 *
222 * @param array error
223 */
224 public function maybe_clear_license( $error ) {
225 if ( $error['code'] === 'disabled' && $error['license'] === $this->license ) {
226 $this->clear_license();
227 }
228 }
229
230 public function clear_license() {
231 delete_option( $this->option_name . 'active' );
232 delete_option( $this->option_name . 'key' );
233 delete_site_option( $this->transient_key() );
234 delete_option( $this->transient_key() );
235 $this->delete_cache();
236 }
237
238 public function set_active( $is_active ) {
239 update_option( $this->option_name . 'active', $is_active );
240 $this->delete_cache();
241 FrmAppHelper::save_combined_js();
242 }
243
244 /**
245 * @since 3.04.03
246 */
247 protected function delete_cache() {
248 delete_transient( 'frm_api_licence' );
249
250 $api = new FrmFormApi( $this->license );
251 $api->reset_cached();
252
253 $api = new FrmFormTemplateApi( $this->license );
254 $api->reset_cached();
255 }
256
257 /**
258 * The Pro version includes the show_license_message function.
259 * We need an extra check before we allow it to show a message.
260 *
261 * @since 3.04.03
262 */
263 public function maybe_show_license_message( $file, $plugin ) {
264 if ( $this->is_expired_addon || isset( $plugin['package'] ) ) {
265 // let's not show a ton of duplicate messages
266 return;
267 }
268
269 $this->show_license_message( $file, $plugin );
270 }
271
272 public function show_license_message( $file, $plugin ) {
273 $message = '';
274 if ( empty( $this->license ) ) {
275 /* translators: %1$s: Plugin name, %2$s: Start link HTML, %3$s: end link HTML */
276 $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>' );
277 } else {
278 $api = new FrmFormApi( $this->license );
279 $errors = $api->error_for_license();
280 if ( ! empty( $errors ) ) {
281 $message = reset( $errors );
282 }
283 }
284
285 if ( empty( $message ) ) {
286 return;
287 }
288
289 $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
290 $id = sanitize_title( $plugin['Name'] ) . '-next';
291
292 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>';
293 echo FrmAppHelper::kses( $message, 'a' ); // WPCS: XSS ok.
294 echo '<script type="text/javascript">var d = document.getElementById("' . esc_attr( $id ) . '").previousSibling;if ( d !== null ){ d.className = d.className + " update"; }</script>';
295 echo '</p></div></td></tr>';
296 }
297
298 public function clear_expired_download( $transient ) {
299 if ( ! is_object( $transient ) ) {
300 return $transient;
301 }
302
303 if ( $this->is_current_version( $transient ) ) {
304 //make sure it doesn't show there is an update if plugin is up-to-date
305 if ( isset( $transient->response[ $this->plugin_folder ] ) ) {
306 unset( $transient->response[ $this->plugin_folder ] );
307 }
308 } elseif ( isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) ) {
309 $this->prepare_update_details( $transient->response[ $this->plugin_folder ] );
310
311 // if the transient has expired, clear the update and trigger it again
312 if ( $transient->response[ $this->plugin_folder ] === false ) {
313 if ( ! $this->has_been_cleared() ) {
314 $this->cleared_plugins();
315 $this->manually_queue_update();
316 }
317 unset( $transient->response[ $this->plugin_folder ] );
318 }
319 }
320
321 return $transient;
322 }
323
324 /**
325 * Check if the plugin information is correct to allow an update
326 *
327 * @since 3.04.03
328 *
329 * @param object $transient - the current plugin info saved for update
330 */
331 private function prepare_update_details( &$transient ) {
332 $version_info = $transient;
333 $has_beta_url = isset( $version_info->beta ) && ! empty( $version_info->beta );
334 if ( $this->get_beta && ! $has_beta_url ) {
335 $version_info = (object) $this->get_api_info( $this->license );
336 }
337
338 if ( isset( $version_info->new_version ) && ! empty( $version_info->new_version ) ) {
339 $this->clear_old_plugin_version( $version_info );
340 if ( $version_info === false ) { // was cleared with timeout
341 $transient = false;
342 } else {
343 $this->maybe_use_beta_url( $version_info );
344
345 if ( version_compare( $version_info->new_version, $this->version, '>' ) ) {
346 $transient = $version_info;
347 }
348 }
349 }
350 }
351
352 /**
353 * Get the API info for this plugin
354 *
355 * @since 3.04.03
356 */
357 protected function get_api_info( $license ) {
358 $api = new FrmFormApi( $license );
359 $addon = $api->get_addon_for_license( $this );
360
361 // if there is no download url, this license does not apply to the addon
362 if ( isset( $addon['package'] ) ) {
363 $this->is_parent_licence = true;
364 } elseif ( isset( $addons['error'] ) ) {
365 // if the license is expired, we must assume all add-ons were packaged
366 $this->is_parent_licence = true;
367 $this->is_expired_addon = true;
368 }
369
370 return $addon;
371 }
372
373 /**
374 * Make sure transients don't stick around on sites that
375 * don't save the transient expiration
376 *
377 * @since 2.05.05
378 */
379 private function clear_old_plugin_version( &$version_info ) {
380 $timeout = ( isset( $version_info->timeout ) && ! empty( $version_info->timeout ) ) ? $version_info->timeout : 0;
381 if ( ! empty( $timeout ) && time() > $timeout ) {
382 $version_info = false; // Cache is expired
383 $api = new FrmFormApi( $this->license );
384 $api->reset_cached();
385 }
386 }
387
388 /**
389 * The beta url is always included if the download has a beta.
390 * Check if the beta should be downloaded.
391 *
392 * @since 3.04.03
393 */
394 private function maybe_use_beta_url( &$version_info ) {
395 if ( $this->get_beta && isset( $version_info->beta ) && ! empty( $version_info->beta ) ) {
396 $version_info->new_version = $version_info->beta['version'];
397 $version_info->package = $version_info->beta['package'];
398 if ( isset( $version_info->plugin ) && ! empty( $version_info->plugin ) ) {
399 $version_info->plugin = $version_info->beta['plugin'];
400 }
401 }
402 }
403
404 private function is_current_version( $transient ) {
405 if ( empty( $transient->checked ) || ! isset( $transient->checked[ $this->plugin_folder ] ) ) {
406 return false;
407 }
408
409 $response = ! isset( $transient->response ) || empty( $transient->response );
410 if ( $response ) {
411 return true;
412 }
413
414 return isset( $transient->response ) && isset( $transient->response[ $this->plugin_folder ] ) && $transient->checked[ $this->plugin_folder ] === $transient->response[ $this->plugin_folder ]->new_version;
415 }
416
417 private function has_been_cleared() {
418 $last_cleared = get_option( 'frm_last_cleared' );
419
420 return ( $last_cleared && $last_cleared > gmdate( 'Y-m-d H:i:s', strtotime( '-5 minutes' ) ) );
421 }
422
423 private function cleared_plugins() {
424 update_option( 'frm_last_cleared', gmdate( 'Y-m-d H:i:s' ) );
425 }
426
427 private function is_license_revoked() {
428 if ( empty( $this->license ) || empty( $this->plugin_slug ) || isset( $_POST['license'] ) ) { // WPCS: CSRF ok.
429 return;
430 }
431
432 if ( is_multisite() ) {
433 $last_checked = get_site_option( $this->transient_key() );
434 } else {
435 $last_checked = get_option( $this->transient_key() );
436 }
437
438 $seven_days_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
439
440 if ( ! $last_checked || $last_checked < $seven_days_ago ) {
441 // check weekly
442 if ( is_multisite() ) {
443 update_site_option( $this->transient_key(), gmdate( 'Y-m-d H:i:s' ) );
444 } else {
445 update_option( $this->transient_key(), gmdate( 'Y-m-d H:i:s' ) );
446 }
447
448 $response = $this->get_license_status();
449 if ( 'revoked' === $response['status'] || 'blocked' === $response['status'] || 'disabled' === $response['status'] ) {
450 $this->clear_license();
451 }
452 }
453 }
454
455 /**
456 * Use a new cache after the license is changed, or Formidable is updated.
457 */
458 private function transient_key() {
459 return 'frm_' . md5( sanitize_key( $this->license . '_' . $this->plugin_slug ) );
460 }
461
462 public static function activate() {
463 FrmAppHelper::permission_check( 'frm_change_settings' );
464 check_ajax_referer( 'frm_ajax', 'nonce' );
465
466 $license = stripslashes( FrmAppHelper::get_param( 'license', '', 'post', 'sanitize_text_field' ) );
467 if ( empty( $license ) ) {
468 wp_die(
469 json_encode(
470 array(
471 'message' => __( 'Oops! You forgot to enter your license number.', 'formidable' ),
472 'success' => false,
473 )
474 )
475 );
476 }
477
478 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
479 $this_plugin = self::get_addon( $plugin_slug );
480 $response = $this_plugin->activate_license( $license );
481
482 echo json_encode( $response );
483 wp_die();
484 }
485
486 private function activate_license( $license ) {
487 $this->set_license( $license );
488 $this->license = $license;
489
490 $response = $this->get_license_status();
491 $response['message'] = '';
492 $response['success'] = false;
493
494 if ( $response['error'] ) {
495 $response['message'] = $response['status'];
496 } else {
497 $messages = $this->get_messages();
498 if ( is_string( $response['status'] ) && isset( $messages[ $response['status'] ] ) ) {
499 $response['message'] = $messages[ $response['status'] ];
500 } else {
501 $response['message'] = FrmAppHelper::kses( $response['status'], array( 'a' ) );
502 }
503
504 $is_valid = false;
505 if ( 'valid' === $response['status'] ) {
506 $is_valid = 'valid';
507 $response['success'] = true;
508 }
509 $this->set_active( $is_valid );
510 }
511
512 return $response;
513 }
514
515 private function get_license_status() {
516 $response = array(
517 'status' => 'missing',
518 'error' => true,
519 );
520 if ( empty( $this->license ) ) {
521 $response['error'] = false;
522
523 return $response;
524 }
525
526 try {
527 $response['error'] = false;
528 $license_data = $this->send_mothership_request( 'activate_license' );
529
530 // $license_data->license will be either "valid" or "invalid"
531 if ( is_array( $license_data ) ) {
532 if ( in_array( $license_data['license'], array( 'valid', 'invalid' ), true ) ) {
533 $response['status'] = $license_data['license'];
534 }
535 } else {
536 $response['status'] = $license_data;
537 }
538 } catch ( Exception $e ) {
539 $response['status'] = $e->getMessage();
540 }
541
542 return $response;
543 }
544
545 private function get_messages() {
546 return array(
547 'valid' => __( 'Your license has been activated. Enjoy!', 'formidable' ),
548 'invalid' => __( 'That license key is invalid', 'formidable' ),
549 'expired' => __( 'That license is expired', 'formidable' ),
550 'revoked' => __( 'That license has been refunded', 'formidable' ),
551 'no_activations_left' => __( 'That license has been used on too many sites', 'formidable' ),
552 'invalid_item_id' => __( 'Oops! That is the wrong license key for this plugin.', 'formidable' ),
553 'missing' => __( 'That license key is invalid', 'formidable' ),
554 );
555 }
556
557 /**
558 * @since 4.03
559 */
560 public static function reset_cache() {
561 FrmAppHelper::permission_check( 'frm_change_settings' );
562 check_ajax_referer( 'frm_ajax', 'nonce' );
563
564 $this_plugin = self::set_license_from_post();
565 $this_plugin->delete_cache();
566
567 $response = array(
568 'success' => true,
569 'message' => __( 'Cache cleared', 'formidable' ),
570 );
571
572 echo json_encode( $response );
573 wp_die();
574 }
575
576 public static function deactivate() {
577 FrmAppHelper::permission_check( 'frm_change_settings' );
578 check_ajax_referer( 'frm_ajax', 'nonce' );
579
580 $this_plugin = self::set_license_from_post();
581
582 $response = array(
583 'success' => false,
584 'message' => '',
585 );
586 try {
587 // $license_data->license will be either "deactivated" or "failed"
588 $license_data = $this_plugin->send_mothership_request( 'deactivate_license' );
589 if ( is_array( $license_data ) && 'deactivated' === $license_data['license'] ) {
590 $response['success'] = true;
591 $response['message'] = __( 'That license was removed successfully', 'formidable' );
592 } else {
593 $response['message'] = __( 'There was an error deactivating your license.', 'formidable' );
594 }
595 } catch ( Exception $e ) {
596 $response['message'] = $e->getMessage();
597 }
598
599 $this_plugin->clear_license();
600
601 echo json_encode( $response );
602 wp_die();
603 }
604
605 /**
606 * @since 4.03
607 */
608 private static function set_license_from_post() {
609 $plugin_slug = FrmAppHelper::get_param( 'plugin', '', 'post', 'sanitize_text_field' );
610 $this_plugin = self::get_addon( $plugin_slug );
611 $license = $this_plugin->get_license();
612 $this_plugin->license = $license;
613 return $this_plugin;
614 }
615
616 public function send_mothership_request( $action ) {
617 $api_params = array(
618 'edd_action' => $action,
619 'license' => $this->license,
620 'url' => home_url(),
621 );
622 if ( is_numeric( $this->download_id ) ) {
623 $api_params['item_id'] = absint( $this->download_id );
624 } else {
625 $api_params['item_name'] = rawurlencode( $this->plugin_name );
626 }
627
628 $arg_array = array(
629 'body' => $api_params,
630 'timeout' => 25,
631 'user-agent' => $this->plugin_slug . '/' . $this->version . '; ' . get_bloginfo( 'url' ),
632 );
633
634 $resp = wp_remote_post( $this->store_url, $arg_array );
635 $body = wp_remote_retrieve_body( $resp );
636
637 $message = __( 'Your License Key was invalid', 'formidable' );
638 if ( is_wp_error( $resp ) ) {
639 $link = FrmAppHelper::admin_upgrade_link( 'api', 'knowledgebase/why-cant-i-activate-formidable-pro/' );
640 /* translators: %1$s: Start link HTML, %2$s: End link HTML */
641 $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>' );
642 $message .= ' ' . $resp->get_error_message();
643 } elseif ( 'error' === $body || is_wp_error( $body ) ) {
644 $message = __( 'You had an HTTP error connecting to the Formidable API', 'formidable' );
645 } else {
646 $json_res = json_decode( $body, true );
647 if ( null !== $json_res ) {
648 if ( is_array( $json_res ) && isset( $json_res['error'] ) ) {
649 $message = $json_res['error'];
650 } else {
651 $message = $json_res;
652 }
653 } elseif ( isset( $resp['response'] ) && isset( $resp['response']['code'] ) ) {
654 /* translators: %1$s: Error code, %2$s: Error message */
655 $message = sprintf( __( 'There was a %1$s error: %2$s', 'formidable' ), $resp['response']['code'], $resp['response']['message'] . ' ' . $resp['body'] );
656 }
657 }
658
659 return $message;
660 }
661
662 public function manually_queue_update() {
663 set_site_transient( 'update_plugins', null );
664 }
665 }
666