PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 3.16.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v3.16.3
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.3, at update/class-edd-sl-plugin-updater.php

1,181 lines 46.1 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 * Safely normalizes a remote payload field without ever unserializing it.
448 *
449 * The store responds with JSON, so `sections`, `banners` and `icons` are
450 * already decoded into objects/arrays. Older endpoints may still deliver them
451 * as a JSON-encoded string, which we decode here. Remote data is never passed
452 * through `unserialize()`/`maybe_unserialize()`, avoiding PHP object injection.
453 *
454 * @param mixed $data
455 * @return mixed
456 */
457 private function maybe_json_decode( $data ) {
458 if ( is_string( $data ) ) {
459 $trimmed = trim( $data );
460 $decoded = json_decode( $trimmed );
461 if ( null !== $decoded || 'null' === $trimmed ) {
462 return $decoded;
463 }
464 }
465
466 return $data;
467 }
468
469 /**
470 * Convert some objects to arrays when injecting data into the update API
471 *
472 * Some data like sections, banners, and icons are expected to be an associative array, however due to the JSON
473 * decoding, they are objects. This method allows us to pass in the object and return an associative array.
474 *
475 * @since 3.6.5
476 *
477 * @param stdClass $data
478 *
479 * @return array
480 */
481 private function convert_object_to_array( $data ) {
482 if ( ! is_array( $data ) && ! is_object( $data ) ) {
483 return array();
484 }
485 $new_data = array();
486 foreach ( $data as $key => $value ) {
487 $new_data[ $key ] = is_object( $value ) ? $this->convert_object_to_array( $value ) : $value;
488 }
489
490 return $new_data;
491 }
492
493 /**
494 * Disable SSL verification in order to prevent download update failures
495 *
496 * @param array $args
497 * @param string $url
498 * @return object $array
499 */
500 public function http_request_args( $args, $url ) {
501
502 if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
503 $args['sslverify'] = $this->verify_ssl();
504 }
505 return $args;
506 }
507
508 /**
509 * Calls the API and, if successfull, returns the object delivered by the API.
510 *
511 * @uses get_bloginfo()
512 * @uses wp_remote_post()
513 * @uses is_wp_error()
514 *
515 * @param string $_action The requested action.
516 * @param array $_data Parameters for the API action.
517 * @return false|object|void
518 */
519 private function api_request( $_action, $_data ) {
520 $data = array_merge( $this->api_data, $_data );
521
522 if ( $data['slug'] !== $this->slug ) {
523 return;
524 }
525
526 // Don't allow a plugin to ping itself
527 if ( trailingslashit( home_url() ) === $this->api_url ) {
528 return false;
529 }
530
531 if ( $this->request_recently_failed() ) {
532 return false;
533 }
534
535 return $this->get_version_from_remote();
536 }
537
538 /**
539 * Determines if a request has recently failed.
540 *
541 * @since 1.9.1
542 *
543 * @return bool
544 */
545 private function request_recently_failed() {
546 $failed_request_details = get_option( $this->failed_request_cache_key );
547
548 // Request has never failed.
549 if ( empty( $failed_request_details ) || ! is_numeric( $failed_request_details ) ) {
550 return false;
551 }
552
553 /*
554 * Request previously failed, but the timeout has expired.
555 * This means we're allowed to try again.
556 */
557 if ( current_time( 'timestamp' ) > $failed_request_details ) {
558 delete_option( $this->failed_request_cache_key );
559
560 return false;
561 }
562
563 return true;
564 }
565
566 /**
567 * Logs a failed HTTP request for this API URL.
568 * We set a timestamp for 1 hour from now. This prevents future API requests from being
569 * made to this domain for 1 hour. Once the timestamp is in the past, API requests
570 * will be allowed again. This way if the site is down for some reason we don't bombard
571 * it with failed API requests.
572 *
573 * @see EDD_SL_Plugin_Updater::request_recently_failed
574 *
575 * @since 1.9.1
576 */
577 private function log_failed_request() {
578 update_option( $this->failed_request_cache_key, strtotime( '+1 hour' ) );
579 }
580
581 /**
582 * Gets the current version information from the remote site.
583 *
584 * @return array|false
585 */
586 private function get_version_from_remote() {
587 $api_params = array(
588 'edd_action' => 'get_version',
589 'license' => ! empty( $this->api_data['license'] ) ? $this->api_data['license'] : '',
590 'item_name' => isset( $this->api_data['item_name'] ) ? $this->api_data['item_name'] : false,
591 'item_id' => isset( $this->api_data['item_id'] ) ? $this->api_data['item_id'] : false,
592 'version' => isset( $this->api_data['version'] ) ? $this->api_data['version'] : false,
593 'slug' => $this->slug,
594 'author' => $this->api_data['author'],
595 'url' => home_url(),
596 'beta' => $this->beta,
597 'php_version' => phpversion(),
598 'wp_version' => get_bloginfo( 'version' ),
599 );
600
601 /**
602 * Filters the parameters sent in the API request.
603 *
604 * @param array $api_params The array of data sent in the request.
605 * @param array $this->api_data The array of data set up in the class constructor.
606 * @param string $this->plugin_file The full path and filename of the file.
607 */
608 $api_params = apply_filters( 'edd_sl_plugin_updater_api_params', $api_params, $this->api_data, $this->plugin_file );
609
610 $request = wp_remote_post(
611 $this->api_url,
612 array(
613 'timeout' => 15,
614 'sslverify' => $this->verify_ssl(),
615 'body' => $api_params,
616 )
617 );
618
619 if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
620 $this->log_failed_request();
621
622 return false;
623 }
624
625 $request = json_decode( wp_remote_retrieve_body( $request ) );
626
627 if ( $request && isset( $request->sections ) ) {
628 $request->sections = $this->maybe_json_decode( $request->sections );
629 } else {
630 $request = false;
631 }
632
633 if ( $request && isset( $request->banners ) ) {
634 $request->banners = $this->maybe_json_decode( $request->banners );
635 }
636
637 if ( $request && isset( $request->icons ) ) {
638 $request->icons = $this->maybe_json_decode( $request->icons );
639 }
640
641 if ( ! empty( $request->sections ) ) {
642 foreach ( $request->sections as $key => $section ) {
643 $request->$key = (array) $section;
644 }
645 }
646
647 return $request;
648 }
649
650 /**
651 * If available, show the changelog for sites in a multisite install.
652 */
653 public function show_changelog() {
654
655 if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' !== $_REQUEST['edd_sl_action'] ) {
656 return;
657 }
658
659 if ( empty( $_REQUEST['plugin'] ) ) {
660 return;
661 }
662
663 if ( empty( $_REQUEST['slug'] ) || $this->slug !== $_REQUEST['slug'] ) {
664 return;
665 }
666
667 if ( ! current_user_can( 'update_plugins' ) ) {
668 wp_die( esc_html__( 'You do not have permission to install plugin updates', 'profile-builder' ), esc_html__( 'Error', 'profile-builder' ), array( 'response' => 403 ) );
669 }
670
671 $version_info = $this->get_repo_api_data();
672 if ( isset( $version_info->sections ) ) {
673 $sections = $this->convert_object_to_array( $version_info->sections );
674 if ( ! empty( $sections['changelog'] ) ) {
675 echo '<div style="background:#fff;padding:10px;">' . wp_kses_post( $sections['changelog'] ) . '</div>';
676 }
677 }
678
679 exit;
680 }
681
682 /**
683 * Get the version info from the cache, if it exists.
684 *
685 * @param string $cache_key
686 * @return object
687 */
688 public function get_cached_version_info( $cache_key = '' ) {
689
690 if ( empty( $cache_key ) ) {
691 $cache_key = $this->get_cache_key();
692 }
693
694 $cache = get_option( $cache_key );
695
696 // Cache is expired
697 if ( empty( $cache['timeout'] ) || current_time('timestamp') > $cache['timeout'] ) {
698 return false;
699 }
700
701 // We need to turn the icons into an array, thanks to WP Core forcing these into an object at some point.
702 $cache['value'] = json_decode( $cache['value'] );
703 if ( ! empty( $cache['value']->icons ) ) {
704 $cache['value']->icons = (array) $cache['value']->icons;
705 }
706
707 return $cache['value'];
708 }
709
710 /**
711 * Adds the plugin version information to the database.
712 *
713 * @param string $value
714 * @param string $cache_key
715 */
716 public function set_version_info_cache( $value = '', $cache_key = '' ) {
717
718 if ( empty( $cache_key ) ) {
719 $cache_key = $this->get_cache_key();
720 }
721
722 $data = array(
723 'timeout' => strtotime( '+3 hours', current_time('timestamp') ),
724 'value' => wp_json_encode( $value ),
725 );
726
727 update_option( $cache_key, $data, 'no' );
728
729 // Delete the duplicate option
730 delete_option( 'edd_api_request_' . md5( serialize( $this->slug . $this->api_data['license'] . $this->beta ) ) );
731 }
732
733 /**
734 * Returns if the SSL of the store should be verified.
735 *
736 * @since 1.6.13
737 * @return bool
738 */
739 private function verify_ssl() {
740 return (bool) apply_filters( 'edd_sl_api_request_verify_ssl', true, $this );
741 }
742
743 /**
744 * Gets the unique key (option name) for a plugin.
745 *
746 * @since 1.9.0
747 * @return string
748 */
749 private function get_cache_key() {
750 $string = $this->slug . $this->api_data['license'] . $this->beta;
751
752 return 'edd_sl_' . md5( serialize( $string ) );
753 }
754
755 }
756 }
757
758 if ( ! class_exists( 'WPPB_Plugin_Updater' ) ) {
759
760 class WPPB_Plugin_Updater {
761
762 private $store_url = "https://www.cozmoslabs.com";
763
764 public function __construct(){
765
766 if( defined( 'WPPB_PAID_PLUGIN_DIR' ) ){
767 add_action('admin_init', array( $this, 'activate_license' ) );
768 add_action('admin_init', array( $this, 'deactivate_license' ) );
769 add_action('admin_notices', array( $this, 'admin_activation_notices' ) );
770
771 add_filter('pre_set_site_transient_update_plugins', array( $this, 'check_license' ) );
772
773 // Activate current site if license is correct
774 add_action('admin_init', array( $this, 'initial_site_activation' ) );
775 }
776
777 }
778
779 protected function get_option( $license_key_option ){
780
781 if( is_multisite() ){
782
783 $license = get_site_option( $license_key_option );
784
785 // fall back to old settings option in case this is empty
786 if( empty( $license ) )
787 $license = get_option( $license_key_option );
788
789 return $license;
790
791 } else
792 return get_option( $license_key_option );
793
794 }
795
796 protected function delete_option( $license_key_option ){
797 if( is_multisite() )
798 delete_site_option( $license_key_option );
799 else
800 delete_option( $license_key_option );
801 }
802
803 protected function update_option( $license_key_option, $value ){
804 if( is_multisite() )
805 update_site_option( $license_key_option, $value );
806 else
807 update_option( $license_key_option, $value );
808 }
809
810 protected function license_page_url( ){
811
812 if( !is_multisite() )
813 return admin_url( 'admin.php?page=profile-builder-general-settings' );
814 else
815 return network_admin_url( 'admin.php?page=profile-builder-register' );
816
817 }
818
819 public function edd_sanitize_license( $new ) {
820 $new = sanitize_text_field($new);
821 $old = wppb_get_serial_number();
822 if( $old && $old != $new ) {
823 $this->delete_option( 'wppb_license_status' ); // new license has been entered, so must reactivate
824 }
825 return $new;
826 }
827
828 /**
829 * This function is run when wordpress checks for updates ( twice a day I believe )
830 * @param $transient_data
831 * @return mixed
832 */
833 public function check_license( $transient_data ){
834
835 if( empty( $transient_data->response ) )
836 return $transient_data;
837
838 if ( false === ( $wppb_check_license = get_transient( 'wppb_checked_licence' ) ) ) {
839
840 $license = trim( wppb_get_serial_number() );
841 $license_details = array();
842
843 // data to send in our API request
844 $api_params = array(
845 '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
846 'license' => $license,
847 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
848 'url' => home_url()
849 );
850
851 // Call the custom API.
852 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => true, 'body' => $api_params ) );
853
854 // make sure the response came back okay
855 if ( !is_wp_error($response) ) {
856
857 $license_data = json_decode(wp_remote_retrieve_body($response));
858
859 if ( false === $license_data->success )
860 $license_details = $license_data;
861 else
862 $license_details = $license_data;
863
864 }
865
866 $this->update_option('wppb_license_details', $license_details);
867
868 if( !$license ){
869 //we need to throw a notice if we have a pro addon active and no license entered
870 $license_details = (object) array( 'error' => 'missing' );
871 $this->update_option('wppb_license_details', $license_details);
872 }
873
874
875 set_transient( 'wppb_checked_licence', 'yes', DAY_IN_SECONDS );
876
877 }
878
879 return $transient_data;
880 }
881
882 public function admin_activation_notices() {
883 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' ) ) {
884
885 switch( $_GET['wppb_sl_activation'] ) {
886 case 'false':
887 $class ="error";
888 break;
889 case 'true':
890 default:
891 $class ="updated";
892 break;
893 }
894
895 ?>
896 <div class="<?php echo esc_attr( $class ); ?>">
897 <p><?php echo wp_kses_post( urldecode( $_GET['message'] ) );//phpcs:ignore ?></p>
898 </div>
899 <?php
900 }
901 }
902
903 public function activate_license() {
904
905 // listen for our activate button to be clicked
906 if( isset( $_POST['wppb_edd_license_activate'] ) ) {
907 // run a quick security check
908 if( ! check_admin_referer( 'wppb_license_nonce', 'wppb_license_nonce' ) )
909 return; // get out if we didn't click the Activate button
910
911 if( !current_user_can( 'manage_options' ) )
912 return;
913
914 if ( isset( $_POST['wppb_license_key'] ) && preg_match('/[*]{3,}/', $_POST['wppb_license_key']) && strlen( $_POST['wppb_license_key'] ) > 5 ) { //phpcs:ignore
915 // pressed submit without altering the existing license key (containing only * as outputted by default)
916 // useful for Deactivating/Activating valid license back
917 $license = wppb_get_serial_number();
918 } else {
919 // save the license
920 $license = $this->edd_sanitize_license( trim( $_POST['wppb_license_key'] ) );//phpcs:ignore
921 $this->update_option( 'wppb_license_key', $license );
922 }
923
924 $message = array();
925 $license_details = array();
926
927 // data to send in our API request
928 $api_params = array(
929 'edd_action' => 'activate_license',
930 'license' => $license,
931 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
932 'url' => home_url()
933 );
934
935 // Call the custom API.
936 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => true, 'body' => $api_params ) );
937
938 // make sure the response came back okay
939 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
940
941 $response_error_message = $response->get_error_message();
942 $message[] = ( is_wp_error( $response ) && ! empty( $response_error_message ) ) ? $response->get_error_message() : __( 'An error occurred, please try again.', 'profile-builder' );
943
944 } else {
945
946 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
947
948 if ( false === $license_data->success ) {
949
950 switch( $license_data->error ) {
951 case 'expired' :
952 $message[] = sprintf(
953 __( 'Your license key expired on %s.', 'profile-builder' ),
954 date_i18n( get_option( 'date_format' ), strtotime( $license_data->expires, current_time( 'timestamp' ) ) )
955 );
956 break;
957 case 'revoked' :
958 $message[] = __( 'Your license key has been disabled.', 'profile-builder' );
959 break;
960 case 'missing' :
961 $message[] = __( 'Invalid license.', 'profile-builder' );
962 break;
963 case 'invalid' :
964 case 'site_inactive' :
965 $message[] = __( 'Your license is not active for this URL.', 'profile-builder' );
966 break;
967 case 'item_name_mismatch' :
968 $message[] = sprintf( __( 'This appears to be an invalid license key for %s.', 'profile-builder' ), $this->get_edd_product_name() );
969 break;
970 case 'no_activations_left':
971 $message[] = __( 'Your license key has reached its activation limit.', 'profile-builder' );
972 break;
973 default :
974 $message[] = __( 'An error occurred, please try again.', 'profile-builder' );
975 break;
976 }
977
978 $license_details = $license_data;
979
980 } else {
981 $license_details = $license_data;
982 }
983
984 }
985
986 //store the license reponse for each addon in the database
987 $this->update_option( 'wppb_license_details', $license_details );
988
989 // Check if anything passed on a message constituting a failure
990 if ( ! empty( $message ) ) {
991 $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
992 $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() );
993
994 $this->update_option( 'wppb_license_status', isset( $license_data->error ) ? $license_data->error : $license_data->license );
995
996 wp_redirect( $redirect );
997 exit();
998 }
999
1000 // $license_data->license will be either "valid" or "invalid"
1001 $this->update_option( 'wppb_license_status', isset( $license_data->error ) ? $license_data->error : $license_data->license );
1002
1003 $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() );
1004
1005 wp_redirect( $redirect );
1006 exit();
1007 }
1008 }
1009
1010
1011 public function initial_site_activation(){
1012
1013 if( is_multisite() )
1014 $edd_sl_initial_activation = get_network_option( null, 'wppb_edd_sl_initial_activation', false );
1015 else
1016 $edd_sl_initial_activation = get_option( 'wppb_edd_sl_initial_activation', false );
1017
1018 if( $edd_sl_initial_activation != false )
1019 return;
1020
1021 $license = wppb_get_serial_number();
1022
1023 if( empty( $license ) )
1024 return;
1025
1026 // data to send in our API request
1027 $api_params = array(
1028 'edd_action' => 'activate_license',
1029 'license' => $license,
1030 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
1031 'url' => home_url()
1032 );
1033
1034 // Call the custom API.
1035 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => true, 'body' => $api_params ) );
1036
1037 if ( !is_wp_error($response) ) {
1038
1039 $license_data = json_decode(wp_remote_retrieve_body($response));
1040
1041 if ( false === $license_data->success )
1042 $license_details = $license_data;
1043 else
1044 $license_details = $license_data;
1045
1046 }
1047
1048 $this->update_option('wppb_license_details', $license_details);
1049
1050 if( is_multisite() )
1051 update_network_option( null, 'wppb_edd_sl_initial_activation', 'yes' );
1052 else
1053 update_option( 'wppb_edd_sl_initial_activation', 'yes', false );
1054
1055 }
1056
1057 function deactivate_license() {
1058
1059 // listen for our activate button to be clicked
1060 if( isset( $_POST['wppb_edd_license_deactivate'] ) ) {
1061
1062 // run a quick security check
1063 if( ! check_admin_referer( 'wppb_license_nonce', 'wppb_license_nonce' ) )
1064 return; // get out if we didn't click the Activate button
1065
1066 if( !current_user_can( 'manage_options' ) )
1067 return;
1068
1069 // retrieve the license from the database
1070 $license = trim( wppb_get_serial_number() );
1071
1072 // data to send in our API request
1073 $api_params = array(
1074 'edd_action' => 'deactivate_license',
1075 'license' => $license,
1076 'item_name' => urlencode( $this->get_edd_product_name() ), // the name of our product in EDD
1077 'url' => home_url()
1078 );
1079
1080 // Call the custom API.
1081 $response = wp_remote_post( $this->store_url, array( 'timeout' => 15, 'sslverify' => true, 'body' => $api_params ) );
1082
1083 // make sure the response came back okay
1084 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
1085
1086 if ( is_wp_error( $response ) )
1087 $message = $response->get_error_message();
1088 else
1089 $message = __( 'An error occurred, please try again.', 'profile-builder' );
1090
1091 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() ) );
1092 exit();
1093 }
1094
1095 // decode the license data
1096 $license_data = json_decode( wp_remote_retrieve_body( $response ) );
1097
1098 // $license_data->license will be either "deactivated" or "failed"
1099 // regardless, we delete the record in the client website. Otherwise, if he tries to add a new license, he can't.
1100 if( $license_data->license == 'deactivated' || $license_data->license == 'failed'){
1101 delete_option( 'wppb_license_status' );
1102 delete_option( 'wppb_license_details' );
1103 }
1104
1105 wp_redirect( $this->license_page_url() );
1106 exit();
1107
1108 }
1109
1110 }
1111
1112 function get_edd_product_name(){
1113
1114 return PROFILE_BUILDER;
1115
1116 }
1117
1118 }
1119
1120 new WPPB_Plugin_Updater();
1121
1122 }
1123
1124 if ( defined( 'WPPB_PAID_EDD_ITEM_ID' ) && defined( 'WPPB_PAID_PLUGIN_FILE' ) && function_exists( 'wppb_paid_plugin_owns_updates' ) && wppb_paid_plugin_owns_updates() ) {
1125 add_action( 'plugins_loaded', 'wppb_paid_plugin_init_updater', 20 );
1126 }
1127
1128 if ( ! function_exists( 'wppb_paid_plugin_init_updater' ) ) {
1129 function wppb_paid_plugin_init_updater() {
1130
1131 if ( ! function_exists( 'wppb_paid_plugin_owns_updates' ) || ! wppb_paid_plugin_owns_updates() )
1132 return;
1133
1134 if( ! function_exists( 'get_plugin_data' ) )
1135 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1136
1137 $plugin_data = get_plugin_data( WPPB_PAID_PLUGIN_FILE, false, false );
1138 $plugin_version = ( $plugin_data && $plugin_data['Version'] ) ? $plugin_data['Version'] : '3.7.6';
1139
1140 new WPPB_EDD_SL_Plugin_Updater( 'https://cozmoslabs.com', WPPB_PAID_PLUGIN_FILE, array(
1141 'version' => $plugin_version,
1142 'license' => wppb_get_serial_number(),
1143 'item_name' => PROFILE_BUILDER,
1144 'item_id' => WPPB_PAID_EDD_ITEM_ID,
1145 'author' => 'Cozmoslabs',
1146 'beta' => false,
1147 ) );
1148
1149 $update_message_hook = 'in_plugin_update_message-' . plugin_basename( WPPB_PAID_PLUGIN_FILE );
1150
1151 remove_action( $update_message_hook, 'wppb_plugin_update_message', 10 );
1152
1153 if( ! has_action( $update_message_hook, 'wppb_paid_plugin_update_message' ) )
1154 add_action( $update_message_hook, 'wppb_paid_plugin_update_message', 10, 2 );
1155 }
1156 }
1157
1158 if ( ! function_exists( 'wppb_paid_plugin_update_message' ) ) {
1159 function wppb_paid_plugin_update_message( $plugin_data, $new_data ) {
1160
1161 if( ! function_exists( 'wppb_get_serial_number' ) )
1162 return;
1163
1164 $wppb_profile_builder_serial = wppb_get_serial_number();
1165 $wppb_profile_builder_serial_status = wppb_get_serial_number_status();
1166
1167 if( empty( $wppb_profile_builder_serial ) ){
1168
1169 echo '<br />' . wp_kses_post( sprintf( __('To enable updates, please enter your license key on the %sSettings%s page. If you don\'t have a license key, you can %sbuy one now%s.', 'profile-builder' ), '<a href="'.esc_url( admin_url('admin.php?page=profile-builder-general-settings') ).'">', '</a>', '<a href="https://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wpbackend&utm_medium=clientsite&utm_content=license-updates-disabled-notification&utm_campaign=PBPro#pricing" target="_blank">', '</a>' ) );
1170
1171 } else if( $wppb_profile_builder_serial_status == 'expired' ) {
1172
1173 echo '<br />' . wp_kses_post( sprintf( __('To enable updates, your licence needs to be renewed. Please go to the <a href="%s">Cozmoslabs Account</a> page and login to renew.', 'profile-builder' ), 'https://www.cozmoslabs.com/account/?utm_source=wpbackend&utm_medium=clientsite&utm_content=license-updates-disabled-notification&utm_campaign=PBPro' ) );
1174
1175 } else if ( $wppb_profile_builder_serial_status != 'valid' ) {
1176
1177 echo '<br />' . wp_kses_post( sprintf( __('To enable updates, you need an active license. %1$sRenew%2$s or %3$spurchase a new license%4$s.', 'profile-builder' ), '<a href="https://www.cozmoslabs.com/account/?utm_source=wpbackend&utm_medium=clientsite&utm_content=license-updates-disabled-notification&utm_campaign=PBPro" target="_blank">', '</a>', '<a href="https://www.cozmoslabs.com/wordpress-profile-builder/?utm_source=wpbackend&utm_medium=clientsite&utm_content=license-updates-disabled-notification&utm_campaign=PBPro#pricing" target="_blank">', '</a>' ) );
1178
1179 }
1180 }
1181 }