PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 3.16.1
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v3.16.1
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / update / class-edd-sl-plugin-updater.php

class-edd-sl-plugin-updater.php in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 3.16.1, at update/class-edd-sl-plugin-updater.php

1,095 lines 41.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined( 'ABSPATH' ) ) exit;
5
6 /**
7 * Allows plugins to use their own update API.
8 *
9 * @author Easy Digital Downloads
10 * @version 1.9.4
11 */
12 if( !class_exists('WPPB_EDD_SL_Plugin_Updater') ) {
13 class WPPB_EDD_SL_Plugin_Updater {
14
15 private $api_url = '';
16 private $api_data = array();
17 private $plugin_file = '';
18 private $name = '';
19 private $slug = '';
20 private $version = '';
21 private $wp_override = false;
22 private $beta = false;
23 private $failed_request_cache_key;
24
25 /**
26 * Class constructor.
27 *
28 * @uses plugin_basename()
29 * @uses hook()
30 *
31 * @param string $_api_url The URL pointing to the custom API endpoint.
32 * @param string $_plugin_file Path to the plugin file.
33 * @param array $_api_data Optional data to send with API calls.
34 */
35 public function __construct($_api_url, $_plugin_file, $_api_data = null)
36 {
37
38 global $edd_plugin_data;
39
40 $this->api_url = trailingslashit($_api_url);
41 $this->api_data = $_api_data;
42 $this->plugin_file = $_plugin_file;
43 $this->name = plugin_basename($_plugin_file);
44 $this->slug = basename(dirname($_plugin_file));
45
46 /**
47 * Necessary in order for the View Details button to work properly when multiple products using
48 * this class are active
49 *
50 * The original takes the base file name as the slug, but our file names are just `index.php` so we
51 * use the folder name instead
52 */
53 if ( $this->slug === 'index') {
54 $this->slug = dirname( plugin_basename( $_plugin_file ) );
55 }
56 // end modification
57
58 $this->version = $_api_data['version'];
59 $this->wp_override = isset($_api_data['wp_override']) ? (bool)$_api_data['wp_override'] : false;
60 $this->beta = !empty($this->api_data['beta']) ? true : false;
61 $this->failed_request_cache_key = 'edd_sl_failed_http_' . md5($this->api_url);
62
63 $edd_plugin_data[$this->slug] = $this->api_data;
64
65 /**
66 * Fires after the $edd_plugin_data is setup.
67 *
68 * @since x.x.x
69 *
70 * @param array $edd_plugin_data Array of EDD SL plugin data.
71 */
72 do_action( 'post_edd_sl_plugin_updater_setup', $edd_plugin_data );
73
74 // Set up hooks.
75 $this->init();
76
77 }
78
79 /**
80 * Set up WordPress filters to hook into WP's update process.
81 *
82 * @uses add_filter()
83 *
84 * @return void
85 */
86 public function init()
87 {
88
89 add_filter('pre_set_site_transient_update_plugins', array($this, 'check_update'));
90 add_filter('plugins_api', array($this, 'plugins_api_filter'), 10, 3);
91 add_action('after_plugin_row', array($this, 'show_update_notification'), 10, 2);
92 add_action('admin_init', array($this, 'show_changelog'));
93
94 }
95
96 /**
97 * Check for Updates at the defined API endpoint and modify the update array.
98 *
99 * This function dives into the update API just when WordPress creates its update array,
100 * then adds a custom API call and injects the custom plugin data retrieved from the API.
101 * It is reassembled from parts of the native WordPress plugin update code.
102 * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
103 *
104 * @uses api_request()
105 *
106 * @param array $_transient_data Update array build by WordPress.
107 * @return array Modified update array with custom plugin data.
108 */
109 public function check_update($_transient_data)
110 {
111
112 global $pagenow;
113
114 if (!is_object($_transient_data)) {
115 $_transient_data = new stdClass;
116 }
117
118 if ('plugins.php' == $pagenow && is_multisite()) {
119 return $_transient_data;
120 }
121
122 if (!empty($_transient_data->response) && !empty($_transient_data->response[$this->name]) && false === $this->wp_override) {
123 return $_transient_data;
124 }
125
126 $current = $this->get_update_transient_data();
127 if (false !== $current && is_object($current) && isset($current->new_version)) {
128 if (version_compare($this->version, $current->new_version, '<')) {
129 $_transient_data->response[$this->name] = $current;
130 } else {
131 // Populating the no_update information is required to support auto-updates in WordPress 5.5.
132 $_transient_data->no_update[$this->name] = $current;
133 }
134 }
135 $_transient_data->last_checked = current_time('timestamp');
136 $_transient_data->checked[$this->name] = $this->version;
137
138 return $_transient_data;
139 }
140
141 /**
142 * Get repo API data from store.
143 * Save to cache.
144 *
145 * @return \stdClass
146 */
147 public function get_repo_api_data() {
148 $version_info = $this->get_cached_version_info();
149
150 if ( false === $version_info ) {
151 $version_info = $this->api_request(
152 'plugin_latest_version',
153 array(
154 'slug' => $this->slug,
155 'beta' => $this->beta,
156 )
157 );
158 if ( ! $version_info ) {
159 return false;
160 }
161
162 // This is required for your plugin to support auto-updates in WordPress 5.5.
163 $version_info->plugin = $this->name;
164 $version_info->id = $this->name;
165 $version_info->tested = $this->get_tested_version( $version_info );
166 if ( ! isset( $version_info->requires ) ) {
167 $version_info->requires = '';
168 }
169 if ( ! isset( $version_info->requires_php ) ) {
170 $version_info->requires_php = '';
171 }
172
173 $this->set_version_info_cache( $version_info );
174 }
175
176 return $version_info;
177 }
178
179 /**
180 * Gets a limited set of data from the API response.
181 * This is used for the update_plugins transient.
182 *
183 * @since 3.8.12
184 * @return \stdClass|false
185 */
186 private function get_update_transient_data() {
187 $version_info = $this->get_repo_api_data();
188
189 if ( ! $version_info ) {
190 return false;
191 }
192
193 $limited_data = new \stdClass();
194 $limited_data->slug = $this->slug;
195 $limited_data->plugin = $this->name;
196 $limited_data->url = $version_info->url;
197 $limited_data->package = $version_info->package;
198 $limited_data->icons = $this->convert_object_to_array( $version_info->icons );
199 $limited_data->banners = $this->convert_object_to_array( $version_info->banners );
200 $limited_data->new_version = $version_info->new_version;
201 $limited_data->tested = $version_info->tested;
202 $limited_data->requires = $version_info->requires;
203 $limited_data->requires_php = $version_info->requires_php;
204
205 return $limited_data;
206 }
207
208 /**
209 * Gets the plugin's tested version.
210 *
211 * @since 1.9.2
212 * @param object $version_info
213 * @return null|string
214 */
215 private function get_tested_version( $version_info ) {
216
217 // There is no tested version.
218 if ( empty( $version_info->tested ) ) {
219 return null;
220 }
221
222 // Strip off extra version data so the result is x.y or x.y.z.
223 list( $current_wp_version ) = explode( '-', get_bloginfo( 'version' ) );
224
225 // The tested version is greater than or equal to the current WP version, no need to do anything.
226 if ( version_compare( $version_info->tested, $current_wp_version, '>=' ) ) {
227 return $version_info->tested;
228 }
229 $current_version_parts = explode( '.', $current_wp_version );
230 $tested_parts = explode( '.', $version_info->tested );
231
232 // The current WordPress version is x.y.z, so update the tested version to match it.
233 if ( isset( $current_version_parts[2] ) && $current_version_parts[0] === $tested_parts[0] && $current_version_parts[1] === $tested_parts[1] ) {
234 $tested_parts[2] = $current_version_parts[2];
235 }
236
237 return implode( '.', $tested_parts );
238 }
239
240 /**
241 * Show the update notification on multisite subsites.
242 *
243 * @param string $file
244 * @param array $plugin
245 */
246 public function show_update_notification( $file, $plugin ) {
247
248 // Return early if in the network admin, or if this is not a multisite install.
249 if ( is_network_admin() || ! is_multisite() ) {
250 return;
251 }
252
253 // Allow single site admins to see that an update is available.
254 if ( ! current_user_can( 'activate_plugins' ) ) {
255 return;
256 }
257
258 if ( $this->name !== $file ) {
259 return;
260 }
261
262 // Do not print any message if update does not exist.
263 $update_cache = get_site_transient( 'update_plugins' );
264
265 if ( ! isset( $update_cache->response[ $this->name ] ) ) {
266 if ( ! is_object( $update_cache ) ) {
267 $update_cache = new stdClass();
268 }
269 $update_cache->response[ $this->name ] = $this->get_repo_api_data();
270 }
271
272 // Return early if this plugin isn't in the transient->response or if the site is running the current or newer version of the plugin.
273 if ( empty( $update_cache->response[ $this->name ] ) || version_compare( $this->version, $update_cache->response[ $this->name ]->new_version, '>=' ) ) {
274 return;
275 }
276
277 printf(
278 '<tr class="plugin-update-tr %3$s" id="%1$s-update" data-slug="%1$s" data-plugin="%2$s">',
279 esc_html( $this->slug ),
280 esc_html( $file ),
281 in_array( $this->name, $this->get_active_plugins(), true ) ? 'active' : 'inactive'
282 );
283
284 echo '<td colspan="3" class="plugin-update colspanchange">';
285 echo '<div class="update-message notice inline notice-warning notice-alt"><p>';
286
287 $changelog_link = '';
288 if ( ! empty( $update_cache->response[ $this->name ]->sections->changelog ) ) {
289 $changelog_link = add_query_arg(
290 array(
291 'edd_sl_action' => 'view_plugin_changelog',
292 'plugin' => urlencode( $this->name ),
293 'slug' => urlencode( $this->slug ),
294 'TB_iframe' => 'true',
295 'width' => 77,
296 'height' => 911,
297 ),
298 self_admin_url( 'index.php' )
299 );
300 }
301 $update_link = add_query_arg(
302 array(
303 'action' => 'upgrade-plugin',
304 'plugin' => urlencode( $this->name ),
305 ),
306 self_admin_url( 'update.php' )
307 );
308
309 printf(
310 /* translators: the plugin name. */
311 esc_html__( 'There is a new version of %1$s available.', 'profile-builder' ),
312 esc_html( $plugin['Name'] )
313 );
314
315 if ( ! current_user_can( 'update_plugins' ) ) {
316 echo ' ';
317 esc_html_e( 'Contact your network administrator to install the update.', 'profile-builder' );
318 } elseif ( empty( $update_cache->response[ $this->name ]->package ) && ! empty( $changelog_link ) ) {
319 echo ' ';
320 printf(
321 /* translators: 1. opening anchor tag, do not translate 2. the new plugin version 3. closing anchor tag, do not translate. */
322 esc_html__( '%1$sView version %2$s details%3$s.', 'profile-builder' ),
323 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
324 esc_html( $update_cache->response[ $this->name ]->new_version ),
325 '</a>'
326 );
327 } elseif ( ! empty( $changelog_link ) ) {
328 echo ' ';
329 printf(
330 esc_html__( '%1$sView version %2$s details%3$s or %4$supdate now%5$s.', 'profile-builder' ),
331 '<a target="_blank" class="thickbox open-plugin-details-modal" href="' . esc_url( $changelog_link ) . '">',
332 esc_html( $update_cache->response[ $this->name ]->new_version ),
333 '</a>',
334 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
335 '</a>'
336 );
337 } else {
338 printf(
339 ' %1$s%2$s%3$s',
340 '<a target="_blank" class="update-link" href="' . esc_url( wp_nonce_url( $update_link, 'upgrade-plugin_' . $file ) ) . '">',
341 esc_html__( 'Update now.', 'profile-builder' ),
342 '</a>'
343 );
344 }
345
346 do_action( "in_plugin_update_message-{$file}", $plugin, $plugin );
347
348 echo '</p></div></td></tr>';
349 }
350
351 /**
352 * Gets the plugins active in a multisite network.
353 *
354 * @return array
355 */
356 private function get_active_plugins() {
357 $active_plugins = (array) get_option( 'active_plugins' );
358 $active_network_plugins = (array) get_site_option( 'active_sitewide_plugins' );
359
360 return array_merge( $active_plugins, array_keys( $active_network_plugins ) );
361 }
362
363 /**
364 * Updates information on the "View version x.x details" page with custom data.
365 *
366 * @uses api_request()
367 *
368 * @param mixed $_data
369 * @param string $_action
370 * @param object $_args
371 * @return object $_data
372 */
373 public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
374
375 if ( 'plugin_information' !== $_action ) {
376
377 return $_data;
378
379 }
380
381 if ( ! isset( $_args->slug ) || ( $_args->slug !== $this->slug ) ) {
382
383 return $_data;
384
385 }
386
387 $to_send = array(
388 'slug' => $this->slug,
389 'is_ssl' => is_ssl(),
390 'fields' => array(
391 'banners' => array(),
392 'reviews' => false,
393 'icons' => array(),
394 ),
395 );
396
397 // Get the transient where we store the api request for this plugin for 24 hours
398 $edd_api_request_transient = $this->get_cached_version_info();
399
400 //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now.
401 if ( empty( $edd_api_request_transient ) ) {
402
403 $api_response = $this->api_request( 'plugin_information', $to_send );
404
405 // Expires in 3 hours
406 $this->set_version_info_cache( $api_response );
407
408 if ( false !== $api_response ) {
409 $_data = $api_response;
410 }
411 } else {
412 $_data = $edd_api_request_transient;
413 }
414
415 // Convert sections into an associative array, since we're getting an object, but Core expects an array.
416 if ( isset( $_data->sections ) && ! is_array( $_data->sections ) ) {
417 $_data->sections = $this->convert_object_to_array( $_data->sections );
418 }
419
420 // Convert banners into an associative array, since we're getting an object, but Core expects an array.
421 if ( isset( $_data->banners ) && ! is_array( $_data->banners ) ) {
422 $_data->banners = $this->convert_object_to_array( $_data->banners );
423 }
424
425 // Convert icons into an associative array, since we're getting an object, but Core expects an array.
426 if ( isset( $_data->icons ) && ! is_array( $_data->icons ) ) {
427 $_data->icons = $this->convert_object_to_array( $_data->icons );
428 }
429
430 // Convert contributors into an associative array, since we're getting an object, but Core expects an array.
431 if ( isset( $_data->contributors ) && ! is_array( $_data->contributors ) ) {
432 $_data->contributors = $this->convert_object_to_array( $_data->contributors );
433 }
434
435 if ( ! isset( $_data->plugin ) ) {
436 $_data->plugin = $this->name;
437 }
438
439 if ( ! isset( $_data->version ) && ! empty( $_data->new_version ) ) {
440 $_data->version = $_data->new_version;
441 }
442
443 return $_data;
444 }
445
446 /**
447 * Convert some objects to arrays when injecting data into the update API
448 *
449 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
450 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
451 *
452 * @since 3.6.5
453 *
454 * @param stdClass $data
455 *
456 * @return array
457 */
458 private function convert_object_to_array( $data ) {
459 if ( ! is_array( $data ) && ! is_object( $data ) ) {
460 return array();
461 }
462 $new_data = array();
463 foreach ( $data as $key => $value ) {
464 $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
465 }
466
467 return $new_data;
468 }
469
470 /**
471 * Disable SSL verification in order to prevent download update failures
472 *
473 * @param array $args
474 * @param string $url
475 * @return object $array
476 */
477 public function http_request_args( $args, $url ) {
478
479 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
480 $args['sslverify'] = $this->verify_ssl();
481 }
482 return $args;
483 }
484
485 /**
486 * Calls the API and, if successfull, returns the object delivered by the API.
487 *
488 * @uses get_bloginfo()
489 * @uses wp_remote_post()
490 * @uses is_wp_error()
491 *
492 * @param string $_action The requested action.
493 * @param array $_data Parameters for the API action.
494 * @return false|object|void
495 */
496 private function api_request( $_action, $_data ) {
497 $data = array_merge( $this->api_data, $_data );
498
499 if ( $data['slug'] !== $this->slug ) {
500 return;
501 }
502
503 // Don't allow a plugin to ping itself
504 if ( trailingslashit( home_url() ) === $this->api_url ) {
505 return false;
506 }
507
508 if ( $this->request_recently_failed() ) {
509 return false;
510 }
511
512 return $this->get_version_from_remote();
513 }
514
515 /**
516 * Determines if a request has recently failed.
517 *
518 * @since 1.9.1
519 *
520 * @return bool
521 */
522 private function request_recently_failed() {
523 $failed_request_details = get_option( $this->failed_request_cache_key );
524
525 // Request has never failed.
526 if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
527 return false;
528 }
529
530 /*
531 * Request previously failed, but the timeout has expired.
532 * This means we're allowed to try again.
533 */
534 if ( current_time( 'timestamp' ) > $failed_request_details ) {
535 delete_option( $this->failed_request_cache_key );
536
537 return false;
538 }
539
540 return true;
541 }
542
543 /**
544 * Logs a failed HTTP request for this API URL.
545 * We set a timestamp for 1 hour from now. This prevents future API requests from being
546 * made to this domain for 1 hour. Once the timestamp is in the past, API requests
547 * will be allowed again. This way if the site is down for some reason we don't bombard
548 * it with failed API requests.
549 *
550 * @see EDD_SL_Plugin_Updater::request_recently_failed
551 *
552 * @since 1.9.1
553 */
554 private function log_failed_request() {
555 update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
556 }
557
558 /**
559 * Gets the current version information from the remote site.
560 *
561 * @return array|false
562 */
563 private function get_version_from_remote() {
564 $api_params = array(
565 'edd_action' => 'get_version',
566 'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
567 'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
568 'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
569 'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
570 'slug' => $this->slug,
571 'author' => $this->api_data['author'],
572 'url' => home_url(),
573 'beta' => $this->beta,
574 'php_version' => phpversion(),
575 'wp_version' => get_bloginfo( 'version' ),
576 );
577
578 /**
579 * Filters the parameters sent in the API request.
580 *
581 * @param array $api_params The array of data sent in the request.
582 * @param array $this->api_data The array of data set up in the class constructor.
583 * @param string $this->plugin_file The full path and filename of the file.
584 */
585 $api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
586
587 $request = wp_remote_post(
588 $this->api_url,
589 array(
590 'timeout' => 15,
591 'sslverify' => $this->verify_ssl(),
592 'body' => $api_params,
593 )
594 );
595
596 if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
597 $this->log_failed_request();
598
599 return false;
600 }
601
602 $request = json_decode( wp_remote_retrieve_body( $request ) );
603
604 if ( $request && isset( $request->sections ) ) {
605 $request->sections = maybe_unserialize( $request->sections );
606 } else {
607 $request = false;
608 }
609
610 if ( $request && isset( $request->banners ) ) {
611 $request->banners = maybe_unserialize( $request->banners );
612 }
613
614 if ( $request && isset( $request->icons ) ) {
615 $request->icons = maybe_unserialize( $request->icons );
616 }
617
618 if ( ! empty( $request->sections ) ) {
619 foreach ( $request->sections as $key => $section ) {
620 $request->$key = (array) $section;
621 }
622 }
623
624 return $request;
625 }
626
627 /**
628 * If available, show the changelog for sites in a multisite install.
629 */
630 public function show_changelog() {
631
632 if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
633 return;
634 }
635
636 if ( empty( $_REQUEST['plugin'] ) ) {
637 return;
638 }
639
640 if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
641 return;
642 }
643
644 if ( ! current_user_can( 'update_plugins' ) ) {
645 wp_die( esc_html__( 'You do not have permission to install plugin updates', 'profile-builder' ), esc_html__( 'Error', 'profile-builder' ), array( 'response' => 403 ) );
646 }
647
648 $version_info = $this->get_repo_api_data();
649 if ( isset( $version_info->sections ) ) {
650 $sections = $this->convert_object_to_array( $version_info->sections );
651 if ( ! empty( $sections['changelog'] ) ) {
652 echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
653 }
654 }
655
656 exit;
657 }
658
659 /**
660 * Get the version info from the cache, if it exists.
661 *
662 * @param string $cache_key
663 * @return object
664 */
665 public function get_cached_version_info( $cache_key = '' ) {
666
667 if ( empty( $cache_key ) ) {
668 $cache_key = $this->get_cache_key();
669 }
670
671 $cache = get_option( $cache_key );
672
673 // Cache is expired
674 if ( empty( $cache['timeout'] ) || current_time('timestamp') > $cache['timeout'] ) {
675 return false;
676 }
677
678 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
679 $cache['value'] = json_decode( $cache['value'] );
680 if ( ! empty( $cache['value']->icons ) ) {
681 $cache['value']->icons = (array) $cache['value']->icons;
682 }
683
684 return $cache['value'];
685 }
686
687 /**
688 * Adds the plugin version information to the database.
689 *
690 * @param string $value
691 * @param string $cache_key
692 */
693 public function set_version_info_cache( $value = '', $cache_key = '' ) {
694
695 if ( empty( $cache_key ) ) {
696 $cache_key = $this->get_cache_key();
697 }
698
699 $data = array(
700 'timeout' => strtotime( '+3 hours', current_time('timestamp') ),
701 'value' => wp_json_encode( $value ),
702 );
703
704 update_option( $cache_key, $data, 'no' );
705
706 // Delete the duplicate option
707 delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
708 }
709
710 /**
711 * Returns if the SSL of the store should be verified.
712 *
713 * @since 1.6.13
714 * @return bool
715 */
716 private function verify_ssl() {
717 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
718 }
719
720 /**
721 * Gets the unique key (option name) for a plugin.
722 *
723 * @since 1.9.0
724 * @return string
725 */
726 private function get_cache_key() {
727 $string = $this->slug . $this->api_data['license'] . $this->beta;
728
729 return 'edd_sl_' . md5( serialize( $string ) );
730 }
731
732 }
733 }
734
735 class WPPB_Plugin_Updater {
736
737 private $store_url = "https://www.cozmoslabs.com";
738
739 public function __construct(){
740
741 if( defined( 'WPPB_PAID_PLUGIN_DIR' ) ){
742 add_action('admin_init', array( $this, 'activate_license' ) );
743 add_action('admin_init', array( $this, 'deactivate_license' ) );
744 add_action('admin_notices', array( $this, 'admin_activation_notices' ) );
745
746 add_filter('pre_set_site_transient_update_plugins', array( $this, 'check_license' ) );
747
748 // Activate current site if license is correct
749 add_action('admin_init', array( $this, 'initial_site_activation' ) );
750 }
751
752 }
753
754 protected function get_option( $license_key_option ){
755
756 if( is_multisite() ){
757
758 $license = get_site_option( $license_key_option );
759
760 // fall back to old settings option in case this is empty
761 if( empty( $license ) )
762 $license = get_option( $license_key_option );
763
764 return $license;
765
766 } else
767 return get_option( $license_key_option );
768
769 }
770
771 protected function delete_option( $license_key_option ){
772 if( is_multisite() )
773 delete_site_option( $license_key_option );
774 else
775 delete_option( $license_key_option );
776 }
777
778 protected function update_option( $license_key_option, $value ){
779 if( is_multisite() )
780 update_site_option( $license_key_option, $value );
781 else
782 update_option( $license_key_option, $value );
783 }
784
785 protected function license_page_url( ){
786
787 if( !is_multisite() )
788 return admin_url( 'admin.php?page=profile-builder-general-settings' );
789 else
790 return network_admin_url( 'admin.php?page=profile-builder-register' );
791
792 }
793
794 public function edd_sanitize_license( $new ) {
795 $new = sanitize_text_field($new);
796 $old = wppb_get_serial_number();
797 if( $old && $old != $new ) {
798 $this->delete_option( 'wppb_license_status' ); // new license has been entered, so must reactivate
799 }
800 return $new;
801 }
802
803 /**
804 * This function is run when wordpress checks for updates ( twice a day I believe )
805 * @param $transient_data
806 * @return mixed
807 */
808 public function check_license( $transient_data ){
809
810 if( empty( $transient_data->response ) )
811 return $transient_data;
812
813 if ( false === ( $wppb_check_license = get_transient( 'wppb_checked_licence' ) ) ) {
814
815 $license = trim( wppb_get_serial_number() );
816 $license_details = array();
817
818 // data to send in our API request
819 $api_params = array(
820 'edd_action' => 'activate_license', //as the license is already activated this does not do anything. We could use check_license action but it gives different results so we can't use it consistently with the result we get from the moment we activate it
821 'license' => $license,
822 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
823 'url' => home_url()
824 );
825
826 // Call the custom API.
827 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
828
829 // make sure the response came back okay
830 if ( !is_wp_error($response) ) {
831
832 $license_data = json_decode(wp_remote_retrieve_body($response));
833
834 if ( false === $license_data->success )
835 $license_details = $license_data;
836 else
837 $license_details = $license_data;
838
839 }
840
841 $this->update_option('wppb_license_details', $license_details);
842
843 if( !$license ){
844 //we need to throw a notice if we have a pro addon active and no license entered
845 $license_details = (object) array( 'error' => 'missing' );
846 $this->update_option('wppb_license_details', $license_details);
847 }
848
849
850 set_transient( 'wppb_checked_licence', 'yes', DAY_IN_SECONDS );
851
852 }
853
854 return $transient_data;
855 }
856
857 public function admin_activation_notices() {
858 if ( isset( $_GET['wppb_sl_activation'] ) && ! empty( $_GET['message'] ) && isset( $_GET['wppb_license_nonce'] ) && wp_verify_nonce( sanitize_text_field( $_GET['wppb_license_nonce'] ), 'wppb_license_display_message' ) ) {
859
860 switch( $_GET['wppb_sl_activation'] ) {
861 case 'false':
862 $class ="error";
863 break;
864 case 'true':
865 default:
866 $class ="updated";
867 break;
868 }
869
870 ?>
871 <div class="<?php echo esc_attr( $class ); ?>">
872 <p><?php echo wp_kses_post( urldecode( $_GET['message'] ) );//phpcs:ignore ?></p>
873 </div>
874 <?php
875 }
876 }
877
878 public function activate_license() {
879
880 // listen for our activate button to be clicked
881 if( isset( $_POST['wppb_edd_license_activate'] ) ) {
882 // run a quick security check
883 if( ! check_admin_referer( 'wppb_license_nonce', 'wppb_license_nonce' ) )
884 return; // get out if we didn't click the Activate button
885
886 if( !current_user_can( 'manage_options' ) )
887 return;
888
889 if ( isset( $_POST['wppb_license_key'] ) && preg_match('/[*]{3,}/', $_POST['wppb_license_key']) && strlen( $_POST['wppb_license_key'] ) > 5 ) { //phpcs:ignore
890 // pressed submit without altering the existing license key (containing only * as outputted by default)
891 // useful for Deactivating/Activating valid license back
892 $license = wppb_get_serial_number();
893 } else {
894 // save the license
895 $license = $this->edd_sanitize_license( trim( $_POST['wppb_license_key'] ) );//phpcs:ignore
896 $this->update_option( 'wppb_license_key', $license );
897 }
898
899 $message = array();
900 $license_details = array();
901
902 // data to send in our API request
903 $api_params = array(
904 'edd_action' => 'activate_license',
905 'license' => $license,
906 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
907 'url' => home_url()
908 );
909
910 // Call the custom API.
911 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
912
913 // make sure the response came back okay
914 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
915
916 $response_error_message = $response->get_error_message();
917 $message[] = ( is_wp_error( $response ) && ! empty( $response_error_message ) ) ? $response->get_error_message() : __( 'An error occurred, please try again.', 'profile-builder' );
918
919 } else {
920
921 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
922
923 if ( false === $license_data->success ) {
924
925 switch( $license_data->error ) {
926 case 'expired' :
927 $message[] = sprintf(
928 __( 'Your license key expired on %s.', 'profile-builder' ),
929 date_i18n( get_option( 'date_format' ), strtotime( $license_data->expires, current_time( 'timestamp' ) ) )
930 );
931 break;
932 case 'revoked' :
933 $message[] = __( 'Your license key has been disabled.', 'profile-builder' );
934 break;
935 case 'missing' :
936 $message[] = __( 'Invalid license.', 'profile-builder' );
937 break;
938 case 'invalid' :
939 case 'site_inactive' :
940 $message[] = __( 'Your license is not active for this URL.', 'profile-builder' );
941 break;
942 case 'item_name_mismatch' :
943 $message[] = sprintf( __( 'This appears to be an invalid license key for %s.', 'profile-builder' ), $this->get_edd_product_name() );
944 break;
945 case 'no_activations_left':
946 $message[] = __( 'Your license key has reached its activation limit.', 'profile-builder' );
947 break;
948 default :
949 $message[] = __( 'An error occurred, please try again.', 'profile-builder' );
950 break;
951 }
952
953 $license_details = $license_data;
954
955 } else {
956 $license_details = $license_data;
957 }
958
959 }
960
961 //store the license reponse for each addon in the database
962 $this->update_option( 'wppb_license_details', $license_details );
963
964 // Check if anything passed on a message constituting a failure
965 if ( ! empty( $message ) ) {
966 $message = implode( "<br/>", array_unique($message) );//if we got the same message for multiple addons show just one, and add a br in case we show multiple messages
967 $redirect = add_query_arg( array( 'wppb_sl_activation' => 'false', 'message' => urlencode( $message ), 'wppb_license_nonce' => wp_create_nonce( 'wppb_license_display_message' ) ), $this->license_page_url() );
968
969 $this->update_option( 'wppb_license_status', isset( $license_data->error ) ? $license_data->error : $license_data->license );
970
971 wp_redirect( $redirect );
972 exit();
973 }
974
975 // $license_data->license will be either "valid" or "invalid"
976 $this->update_option( 'wppb_license_status', isset( $license_data->error ) ? $license_data->error : $license_data->license );
977
978 $redirect = add_query_arg( array( 'wppb_sl_activation' => 'true', 'message' => urlencode( __( 'You have successfully activated your license.', 'profile-builder' ) ), 'wppb_license_nonce' => wp_create_nonce( 'wppb_license_display_message' ) ), $this->license_page_url() );
979
980 wp_redirect( $redirect );
981 exit();
982 }
983 }
984
985
986 public function initial_site_activation(){
987
988 if( is_multisite() )
989 $edd_sl_initial_activation = get_network_option( null, 'wppb_edd_sl_initial_activation', false );
990 else
991 $edd_sl_initial_activation = get_option( 'wppb_edd_sl_initial_activation', false );
992
993 if( $edd_sl_initial_activation != false )
994 return;
995
996 $license = wppb_get_serial_number();
997
998 if( empty( $license ) )
999 return;
1000
1001 // data to send in our API request
1002 $api_params = array(
1003 'edd_action' => 'activate_license',
1004 'license' => $license,
1005 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
1006 'url' => home_url()
1007 );
1008
1009 // Call the custom API.
1010 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
1011
1012 if ( !is_wp_error($response) ) {
1013
1014 $license_data = json_decode(wp_remote_retrieve_body($response));
1015
1016 if ( false === $license_data->success )
1017 $license_details = $license_data;
1018 else
1019 $license_details = $license_data;
1020
1021 }
1022
1023 $this->update_option('wppb_license_details', $license_details);
1024
1025 if( is_multisite() )
1026 update_network_option( null, 'wppb_edd_sl_initial_activation', 'yes' );
1027 else
1028 update_option( 'wppb_edd_sl_initial_activation', 'yes', false );
1029
1030 }
1031
1032 function deactivate_license() {
1033
1034 // listen for our activate button to be clicked
1035 if( isset( $_POST['wppb_edd_license_deactivate'] ) ) {
1036
1037 // run a quick security check
1038 if( ! check_admin_referer( 'wppb_license_nonce', 'wppb_license_nonce' ) )
1039 return; // get out if we didn't click the Activate button
1040
1041 if( !current_user_can( 'manage_options' ) )
1042 return;
1043
1044 // retrieve the license from the database
1045 $license = trim( wppb_get_serial_number() );
1046
1047 // data to send in our API request
1048 $api_params = array(
1049 'edd_action' => 'deactivate_license',
1050 'license' => $license,
1051 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
1052 'url' => home_url()
1053 );
1054
1055 // Call the custom API.
1056 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
1057
1058 // make sure the response came back okay
1059 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
1060
1061 if ( is_wp_error( $response ) )
1062 $message = $response->get_error_message();
1063 else
1064 $message = __( 'An error occurred, please try again.', 'profile-builder' );
1065
1066 wp_redirect( add_query_arg( array( 'wppb_sl_activation' => 'false', 'message' => urlencode( $message ), 'wppb_license_nonce' => wp_create_nonce( 'wppb_license_display_message' ) ), $this->license_page_url() ) );
1067 exit();
1068 }
1069
1070 // decode the license data
1071 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
1072
1073 // $license_data->license will be either "deactivated" or "failed"
1074 // regardless, we delete the record in the client website. Otherwise, if he tries to add a new license, he can't.
1075 if( $license_data->license == 'deactivated' || $license_data->license == 'failed'){
1076 delete_option( 'wppb_license_status' );
1077 delete_option( 'wppb_license_details' );
1078 }
1079
1080 wp_redirect( $this->license_page_url() );
1081 exit();
1082
1083 }
1084
1085 }
1086
1087 function get_edd_product_name(){
1088
1089 return PROFILE_BUILDER;
1090
1091 }
1092
1093 }
1094
1095 new WPPB_Plugin_Updater();