PluginProbe
wpForo Forum / 3.2.2
wpForo Forum v3.2.2
3.2.2 3.2.1 3.2.0 3.1.7 3.1.6 3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 All 142 releases
← All changes | admin/pages/license/src/Services/ActionsService.php +62 -2 3.2.1 → 3.2.2 View file →
@@ -50,8 +50,9 @@
50 50 add_action( $p . 'activate_addon', [ $this, 'ajax_activate_addon' ] );
51 51 add_action( $p . 'deactivate_addon', [ $this, 'ajax_deactivate_addon' ] );
52 52 add_action( $p . 'install_activate_addon', [ $this, 'ajax_install_activate_addon' ] );
53 53 add_action( $p . 'download_addon', [ $this, 'ajax_download_addon' ] );
54 + add_action( $p . 'install_verified_copy', [ $this, 'ajax_install_verified_copy' ] );
54 55
55 56 // Subscription
56 57 add_action( $p . 'cancel_subscription', [ $this, 'ajax_cancel_subscription' ] );
57 58 add_action( $p . 'resume_subscription', [ $this, 'ajax_resume_subscription' ] );
@@ -106,8 +107,12 @@
106 107 }
107 108 $pid = $product['id'];
108 109 $is_bundle = ! empty( $product['is_bundle'] );
109 110
111 + // Marketing links from the Paddle product custom attributes (empty = no button on the card)
112 + $product['product_url'] = $this->product_link( $product, 'product_url' );
113 + $product['demo_url'] = $this->product_link( $product, 'demo_url' );
114 +
110 115 // For bundle products, check if all included addons are licensed
111 116 if( $is_bundle && ! empty( $product['bundle_slugs'] ) ) {
112 117 $bundle_all_licensed = true;
113 118 $bundle_addon_names = [];
@@ -185,8 +190,13 @@
185 190 }
186 191 $can_install = ! $needs_install || $this->addonsService->can_install_addons();
187 192 foreach( $products as &$product ) {
188 193 $product['can_install'] = $can_install;
194 + // Download ZIP is offered only when WordPress can't install a licensed addon (admin.js also sets it after an install fails for that reason)
195 + $product['offer_download'] = ! $can_install && ( $product['is_licensed'] || $product['is_trial'] ) && empty( $product['is_bundle'] ) && $product['addon_status'] === 'not_installed';
196 + // "Reinstall Addon" for every installed licensed addon; emphasized when the installed copy isn't verified
197 + $product['offer_verified_copy'] = ( $product['is_licensed'] || $product['is_trial'] ) && empty( $product['is_bundle'] ) && $product['addon_status'] !== 'not_installed';
198 + $product['needs_verified_copy'] = $product['offer_verified_copy'] && $this->addonsService->needs_verified_copy( $product['plugin_slug'] ?? '' );
189 199 }
190 200 unset( $product );
191 201
192 202 $products = array_values( $products );
@@ -195,8 +205,18 @@
195 205 wp_send_json_error( [ 'message' => __( 'Failed to load products', 'gvectors' ) ] );
196 206 }
197 207 }
198 208
209 + /**
210 + * A link from the product's Paddle custom data (http/https only) — '' when missing, empty or not a web URL.
211 + */
212 + private function product_link( array $product, string $key ): string {
213 + $url = $product['custom_data'][ $key ] ?? '';
214 + if( ! is_string( $url ) || ( $url = trim( $url ) ) === '' ) return '';
215 +
216 + return esc_url_raw( $url, [ 'http', 'https' ] );
217 + }
218 +
199 219 private function verify_admin(): bool {
200 220 if( ! current_user_can( 'administrator' ) ) {
201 221 wp_send_json_error( [ 'message' => __( 'Permission denied', 'gvectors' ) ] );
202 222
@@ -512,11 +532,12 @@
512 532 $response = $this->addonsService->licenseService->activate( $license_key, $product_id );
513 533 if( ! empty( $response['success'] ) ) {
514 534 // Revalidate all licenses to clear expired notices for newly purchased/renewed addons
515 535 $this->addonsService->check_all_license_validity();
536 + $this->addonsService->reverify_licensed_addons();
516 537 wp_send_json_success( [
517 538 'license' => $response['data'],
518 - 'message' => __( 'License activated successfully', 'gvectors' ),
539 + 'message' => self::activated_message( $response['data'] ),
519 540 ] );
520 541 } else {
521 542 $error = $response['error'] ?? __( 'Failed to activate license', 'gvectors' );
522 543 wp_send_json_error( [ 'message' => $error ] );
@@ -537,8 +558,9 @@
537 558
538 559 $response = $this->addonsService->licenseService->activate_by_transaction( $transaction_id, $product_id );
539 560 if( ! empty( $response['success'] ) ) {
540 561 $this->addonsService->check_all_license_validity();
562 + $this->addonsService->reverify_licensed_addons();
541 563 wp_send_json_success( [
542 564 'activated' => $response['data']['activated'] ?? [],
543 565 'errors' => $response['data']['errors'] ?? [],
544 566 'message' => $response['data']['message'] ?? __( 'Licenses activated successfully', 'gvectors' ),
@@ -559,8 +581,9 @@
559 581 $response = $this->addonsService->licenseService->activate_unified( $key, $product_id );
560 582
561 583 if( ! empty( $response['success'] ) ) {
562 584 $this->addonsService->check_all_license_validity();
585 + $this->addonsService->reverify_licensed_addons();
563 586 // Multi-license response (txn_* or by-domain)
564 587 if( ! empty( $response['data']['activated'] ) ) {
565 588 wp_send_json_success( [
566 589 'activated' => $response['data']['activated'],
@@ -570,9 +593,9 @@
570 593 } else {
571 594 // Single license response (gvl_* or legacy key)
572 595 wp_send_json_success( [
573 596 'license' => $response['data'],
574 - 'message' => __( 'License activated successfully', 'gvectors' ),
597 + 'message' => self::activated_message( $response['data'] ),
575 598 ] );
576 599 }
577 600
578 601 return;
@@ -580,8 +603,20 @@
580 603
581 604 wp_send_json_error( [ 'message' => $response['error'] ?? __( 'Could not activate. Please check your key.', 'gvectors' ) ] );
582 605 }
583 606
607 + /**
608 + * Success message for a single activated license. An expired old-store key is still linked to the site
609 + * (the addon keeps working), but it grants no updates until renewed.
610 + */
611 + private static function activated_message( $license ): string {
612 + if( is_array( $license ) && ( $license['status'] ?? '' ) === 'expired' ) {
613 + return __( 'Your license was linked to this site. It has expired: the addon keeps working, renew it to get updates.', 'gvectors' );
614 + }
615 +
616 + return __( 'License activated successfully', 'gvectors' );
617 + }
618 +
584 619 public function ajax_activate_by_domain(): void {
585 620 if( ! $this->verify_admin() ) return;
586 621
587 622 $response = $this->addonsService->licenseService->activate_by_domain();
@@ -586,8 +621,9 @@
586 621
587 622 $response = $this->addonsService->licenseService->activate_by_domain();
588 623 if( ! empty( $response['success'] ) ) {
589 624 $this->addonsService->check_all_license_validity();
625 + $this->addonsService->reverify_licensed_addons();
590 626 wp_send_json_success( [
591 627 'activated' => $response['data']['activated'] ?? [],
592 628 'errors' => $response['data']['errors'] ?? [],
593 629 'message' => $response['data']['message'] ?? __( 'Licenses activated successfully', 'gvectors' ),
@@ -737,8 +773,32 @@
737 773 wp_send_json_success( $result );
738 774 } else {
739 775 wp_send_json_error( [
740 776 'message' => $result['error'] ?? __( 'Install & activate failed', 'gvectors' ),
777 + 'manual_install' => ! empty( $result['manual_install'] ),
778 + ] );
779 + }
780 + }
781 +
782 + /**
783 + * "Reinstall Addon": replace an installed licensed addon with a fresh copy signed for this site
784 + */
785 + public function ajax_install_verified_copy(): void {
786 + if( ! $this->verify_admin() ) return;
787 +
788 + $product_id = isset( $_POST['product_id'] ) ? sanitize_text_field( $_POST['product_id'] ) : '';
789 + if( empty( $product_id ) ) {
790 + wp_send_json_error( [ 'message' => __( 'Product ID required', 'gvectors' ) ] );
791 +
792 + return;
793 + }
794 +
795 + $result = $this->addonsService->install_verified_copy( $product_id );
796 + if( ! empty( $result['success'] ) ) {
797 + wp_send_json_success( $result );
798 + } else {
799 + wp_send_json_error( [
800 + 'message' => $result['error'] ?? __( 'Could not reinstall the addon', 'gvectors' ),
741 801 'manual_install' => ! empty( $result['manual_install'] ),
742 802 ] );
743 803 }
744 804 }