PluginProbe
PatternsWP – Gutenberg Block Patterns & Page Templates Library / 1.1.0
PatternsWP – Gutenberg Block Patterns & Page Templates Library v1.1.0
1.1.0 trunk 1.0.0 1.0.1 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9
← All changes | includes/lib/class-patterns-license.php +194 -95 1.0.51.1.0 View file →
@@ -1,5 +1,9 @@
1 1 <?php
2 +// Exit if accessed directly
3 +if (!defined('ABSPATH')) {
4 + exit;
5 +}
2 6
3 7 class PatternsWP_LicenseSection {
4 8 private $patternswp_license_key_option;
5 9
@@ -9,8 +13,9 @@
9 13 public function __construct() {
10 14 add_action( 'admin_menu', array( $this, 'patternswp_add_license_page' ) );
11 15 add_action( 'admin_init', array( $this, 'patternswp_save_license_key' ) );
12 16 add_action( 'admin_notices', array( $this, 'patternswp_display_admin_notice' ) );
17 + add_action( 'wp_ajax_patternswp_activate_license', array( $this, 'ajax_activate_license' ) );
13 18 }
14 19
15 20 /**
16 21 * Add License Page
@@ -19,58 +24,20 @@
19 24 add_submenu_page( 'patternswp-plugin-menu', 'License', 'License', 'manage_options', 'patternswp-license_section', array( $this, 'patternswp_plugin_license_section' ) );
20 25 }
21 26
22 27 /**
23 - * License Section
28 + * License Section — React admin mount.
24 29 */
25 30 public function patternswp_plugin_license_section() {
26 - $license_key = $this->patternswp_license_key_option = get_option( 'patternswp_license_key' );
27 - $license_key = isset( $license_key['patternswp_pro_license_key'] ) ? $license_key['patternswp_pro_license_key'] : '';
28 - if (!empty( $license_key ) && strlen( $license_key ) > 8) {
29 - $masked_key = substr( $license_key, 0, 8 ) . str_repeat( 'X', strlen( $license_key ) - 8 );
30 - } else {
31 - $masked_key = $license_key;
31 + if ( ! current_user_can( 'manage_options' ) ) {
32 + return;
32 33 }
33 - $get_license_status = get_option( 'patternswp_plugin_license_data' );
34 - $license_status = isset( $get_license_status['activated'] ) ? $get_license_status['activated'] : false;
35 34 ?>
36 - <div class="wrap">
37 - <h1><?php esc_attr_e( 'License', 'patternswp' ); ?></h1>
38 - <?php settings_errors(); ?>
39 - <h2 class="nav-tab-wrapper">
40 - <a href="?page=patternswp-plugin-menu" class="nav-tab"><?php esc_attr_e( 'Dashboard', 'patternswp' ); ?></a>
41 - <a href="?page=patternswp-plugin-page-1" class="nav-tab"><?php esc_attr_e( 'Support', 'patternswp' ); ?></a>
42 - <a href="?page=patternswp-clear-cache" class="nav-tab"><?php esc_attr_e( 'Clear Cache', 'patternswp' ); ?></a>
43 - <a href="?page=patternswp-license_section" class="nav-tab nav-tab-active"><?php esc_attr_e( 'License', 'patternswp' ); ?></a>
44 - </h2>
45 - <div class="patterns-wp-tabs-content">
46 - <div id="tab-1" class="patterns-wp-tab-content patterns-wp-tab-active">
47 - <div class="wrap">
48 -
49 - <div class="container" style="padding: 40px;background-color: white;border-radius: 5px;overflow: hidden;width: 70%;">
50 - <div class="about__section has-1-columns">
51 - <div class="column" style="">
52 - <h1 class="feature-title"><?php esc_html_e('License', 'patternswp'); ?></h1>
53 - <p><?php esc_html_e('Already purchased? Simply enter your license key below to connect with PatternsWP Pro!', 'patternswp'); ?></p>
54 - </div>
55 - <div style="height: 1px; background-color: #ccc; width: 100%; margin: 20px 0;"></div>
56 - </div>
57 - <div class="">
58 - <form id="patternswp_license_form" method="post" >
59 - <input class="regular-text" type="text" placeholder="Enter your license key here..." name="patternswp_license_key[patternswp_pro_license_key]" id="patternswp_pro_license_key" value="<?php echo esc_attr( $masked_key ) ?>">
60 - <?php wp_nonce_field('patternswp_save_license_action', 'patternswp_license_nonce'); ?>
61 - <input name="patternswp_save_license" type="submit" id="custom_submit_btn" class="button button-primary" value="Save & Activate" style="margin-left: 5px;">
62 - <?php
63 - if ( $license_status == true ) {
64 - echo '<div style="margin-top: 10px;"><span style="color: green;font-weight: bold;">Your license is active and verified. Enjoy all premium features!</span></div>';
65 - }
66 - ?>
67 - <div>
68 - </div>
69 - </form>
70 - </div>
71 - </div>
72 - </div>
35 + <div class="wrap patternswp-admin-wrap">
36 + <h1 class="screen-reader-text"><?php esc_html_e( 'License', 'patternswp' ); ?></h1>
37 + <div id="patternswp-admin-root">
38 + <div class="patternswp-admin__loading">
39 + <span class="spinner is-active" style="float:none;margin:0;"></span>
73 40 </div>
74 41 </div>
75 42 </div>
76 43 <?php
@@ -76,23 +43,161 @@
76 43 <?php
77 44 }
78 45
79 46 /**
80 - * Save License Key
47 + * Mask a license key for display.
48 + *
49 + * @param string $license_key Raw license key.
50 + * @return string
81 51 */
52 + private function mask_license_key( $license_key ) {
53 + if ( ! empty( $license_key ) && strlen( $license_key ) > 8 ) {
54 + return substr( $license_key, 0, 8 ) . str_repeat( 'X', strlen( $license_key ) - 8 );
55 + }
56 + return $license_key;
57 + }
58 +
59 + /**
60 + * AJAX: activate license key.
61 + */
62 + public function ajax_activate_license() {
63 + if ( ! current_user_can( 'manage_options' ) ) {
64 + wp_send_json_error( array( 'message' => __( 'You do not have permission to manage the license.', 'patternswp' ) ), 403 );
65 + }
66 +
67 + check_ajax_referer( 'patternswp_admin_nonce', 'nonce' );
68 +
69 + $license_key = isset( $_POST['license_key'] ) ? sanitize_text_field( wp_unslash( $_POST['license_key'] ) ) : '';
70 +
71 + // Ignore masked keys (contain X padding from previous activation display).
72 + if ( false !== strpos( $license_key, 'X' ) && preg_match( '/X{4,}/', $license_key ) ) {
73 + $stored = get_option( 'patternswp_license_key', array() );
74 + $stored_key = isset( $stored['patternswp_pro_license_key'] ) ? $stored['patternswp_pro_license_key'] : '';
75 + if ( ! empty( $stored_key ) && $this->mask_license_key( $stored_key ) === $license_key ) {
76 + $license_data = get_option( 'patternswp_plugin_license_data', array() );
77 + if ( ! empty( $license_data['activated'] ) ) {
78 + wp_send_json_success(
79 + array(
80 + 'message' => __( 'License Key is already activated.', 'patternswp' ),
81 + 'status' => 'info',
82 + 'activated' => true,
83 + 'maskedKey' => $this->mask_license_key( $stored_key ),
84 + )
85 + );
86 + }
87 + $license_key = $stored_key;
88 + }
89 + }
90 +
91 + if ( empty( $license_key ) ) {
92 + wp_send_json_error(
93 + array(
94 + 'message' => __( 'License activation failed: License Key cannot be empty. Please enter a valid license key.', 'patternswp' ),
95 + )
96 + );
97 + }
98 +
99 + $result = $this->activate_license_ajax( $license_key );
100 +
101 + if ( is_wp_error( $result ) ) {
102 + wp_send_json_error( array( 'message' => $result->get_error_message() ) );
103 + }
104 +
105 + wp_send_json_success( $result );
106 + }
107 +
108 + /**
109 + * Activate license and return structured result (no redirects).
110 + *
111 + * @param string $license_key License key.
112 + * @return array|WP_Error
113 + */
114 + public function activate_license_ajax( $license_key ) {
115 + $stored = get_option( 'patternswp_license_key', array() );
116 + $old_key = isset( $stored['patternswp_pro_license_key'] ) ? $stored['patternswp_pro_license_key'] : '';
117 + $license_data = get_option( 'patternswp_plugin_license_data', array() );
118 +
119 + if ( $old_key === $license_key && ! empty( $license_data['activated'] ) ) {
120 + return array(
121 + 'message' => __( 'License Key is already activated.', 'patternswp' ),
122 + 'status' => 'info',
123 + 'activated' => true,
124 + 'maskedKey' => $this->mask_license_key( $license_key ),
125 + );
126 + }
127 +
128 + $activation_url = PATTERSWP_PLUGIN_API_URL . '/activate';
129 + $data = array(
130 + 'license_key' => $license_key,
131 + 'instance_name' => home_url(),
132 + );
133 + $response = wp_remote_post(
134 + $activation_url,
135 + array(
136 + 'body' => $data,
137 + 'headers' => array( 'Accept' => 'application/json' ),
138 + 'timeout' => 15,
139 + )
140 + );
141 +
142 + if ( is_wp_error( $response ) ) {
143 + return new WP_Error(
144 + 'patternswp_license_request_failed',
145 + $response->get_error_message()
146 + );
147 + }
148 +
149 + $body = json_decode( wp_remote_retrieve_body( $response ) );
150 +
151 + if ( isset( $body->error ) && ! empty( $body->error ) ) {
152 + $error = $body->error;
153 + if ( 'license_key not found.' === $error ) {
154 + $error = __( 'License activation failed: Please enter a valid license key.', 'patternswp' );
155 + }
156 + return new WP_Error( 'patternswp_license_invalid', $error );
157 + }
158 +
159 + if ( isset( $body->activated ) && true === $body->activated ) {
160 + update_option( 'patternswp_plugin_license_data', (array) $body );
161 + $this->clear_transients();
162 + update_option(
163 + 'patternswp_license_key',
164 + array( 'patternswp_pro_license_key' => $license_key )
165 + );
166 + do_action( 'patternswp_load_patterns_by_ra' );
167 +
168 + return array(
169 + 'message' => __( 'License activated successfully.', 'patternswp' ),
170 + 'status' => 'success',
171 + 'activated' => true,
172 + 'maskedKey' => $this->mask_license_key( $license_key ),
173 + );
174 + }
175 +
176 + return new WP_Error(
177 + 'patternswp_license_failed',
178 + __( 'License activation failed. Please try again.', 'patternswp' )
179 + );
180 + }
181 +
182 + /**
183 + * Save License Key (legacy form POST — kept for compatibility).
184 + */
82 185 public function patternswp_save_license_key() {
83 186 $this->patternswp_license_key_option = get_option( 'patternswp_license_key' );
84 187
85 188 if ( isset($_POST['patternswp_save_license'] ) ) {
86 189 if (!isset( $_POST['patternswp_license_nonce'] ) || !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['patternswp_license_nonce'] ) ), 'patternswp_save_license_action') ) {
87 - wp_die(esc_attr('Security check failed. Please try again.', 'patternswp'));
190 + wp_die(esc_html__('Security check failed. Please try again.', 'patternswp'));
88 191 }
89 192 if ( !current_user_can('manage_options' ) ) {
90 - wp_die(esc_attr('You do not have sufficient permissions to perform this action.', 'patternswp'));
193 + wp_die(esc_html__('You do not have sufficient permissions to perform this action.', 'patternswp'));
91 194 }
92 195
93 196 if ( isset( $_POST['patternswp_license_key']['patternswp_pro_license_key'] ) && !empty( $_POST['patternswp_license_key']['patternswp_pro_license_key'] ) ) {
94 - $license_k = isset( $_POST['patternswp_license_key'] ) ? $_POST['patternswp_license_key'] : '';
197 + $license_k = array(
198 + 'patternswp_pro_license_key' => sanitize_text_field( wp_unslash( $_POST['patternswp_license_key']['patternswp_pro_license_key'] ) ),
199 + );
95 200 $empty_option = array();
96 201 if( !empty( $license_k ) ){
97 202 $empty_option['patternswp_pro_license_key'] = $this->patternswp_license_key_option['patternswp_pro_license_key'];
98 203 $this->handle_license_activation( $empty_option, $license_k );
@@ -100,17 +205,19 @@
100 205 }
101 206 $patterns_tkey = 'patterns_data';
102 207 delete_transient( $patterns_tkey );
103 208 }else{
104 - wp_redirect( add_query_arg( array(
209 + $redirect_url = add_query_arg( array(
105 210 'page' => 'patternswp-license_section',
106 211 'pt_msg' => urlencode( 'License activation failed: License Key cannot be empty. Please enter a valid license key.' ),
107 212 'status' => 'error'
108 - ), admin_url( 'admin.php' ) ) );
213 + ), admin_url( 'admin.php' ) );
214 + wp_safe_redirect( $redirect_url );
109 215 exit;
110 216 }
111 217
112 - wp_redirect( add_query_arg( 'updated', 'true', wp_get_referer() ) );
218 + $redirect_url = add_query_arg( 'updated', 'true', wp_get_referer() );
219 + wp_safe_redirect( $redirect_url );
113 220 exit;
114 221 }
115 222 }
116 223
@@ -122,23 +229,25 @@
122 229 */
123 230 public function handle_license_activation( $old_value, $new_value ) {
124 231 if ( isset( $new_value['patternswp_pro_license_key'] ) && ! empty( $new_value['patternswp_pro_license_key'] ) ) {
125 232 if ( $old_value['patternswp_pro_license_key'] === $new_value['patternswp_pro_license_key'] ) {
126 - wp_redirect( add_query_arg( array(
233 + $redirect_url = add_query_arg( array(
127 234 'page' => 'patternswp-license_section',
128 235 'pt_msg' => urlencode( 'License Key is already activated.' ),
129 236 'status' => 'info'
130 - ), admin_url( 'admin.php' ) ) );
237 + ), admin_url( 'admin.php' ) );
238 + wp_safe_redirect( $redirect_url );
131 239 exit;
132 240 } else {
133 241 $this->activate_license( $new_value['patternswp_pro_license_key'] );
134 242 }
135 243 } else {
136 - wp_redirect( add_query_arg( array(
244 + $redirect_url = add_query_arg( array(
137 245 'page' => 'patternswp-license_section',
138 246 'pt_msg' => urlencode( 'License activation failed: License Key cannot be empty. Please enter a valid license key.' ),
139 247 'status' => 'error'
140 - ), admin_url( 'admin.php' ) ) );
248 + ), admin_url( 'admin.php' ) );
249 + wp_safe_redirect( $redirect_url );
141 250 exit;
142 251 }
143 252 }
144 253
@@ -147,45 +256,27 @@
147 256 *
148 257 * @param string $license_key License key.
149 258 */
150 259 public function activate_license( $license_key ) {
151 - global $wpdb;
152 -
153 - $activation_url = PATTERSWP_PLUGIN_API_URL . '/activate';
154 - $data = array( 'license_key' => $license_key, 'instance_name' => home_url() );
155 - $response = wp_remote_post( $activation_url, array( 'body' => $data, 'headers' => array( 'Accept' => 'application/json' ), 'sslverify' => false, 'timeout' => 10 ) );
156 - $response = json_decode( wp_remote_retrieve_body( $response ) );
157 -
158 - if ( isset( $response->error ) && !empty( $response->error ) ) {
159 - if( $response->error == 'license_key not found.' ){
160 - $response->error = 'License activation failed: Please enter a valid license key.';
161 - }
260 + $result = $this->activate_license_ajax( $license_key );
162 261
163 - wp_redirect( add_query_arg( array(
164 - 'page' => 'patternswp-license_section',
165 - 'pt_msg' => urlencode( $response->error ),
166 - 'status' => 'error'
167 - ), admin_url( 'admin.php' ) ) );
262 + if ( is_wp_error( $result ) ) {
263 + $redirect_url = add_query_arg( array(
264 + 'page' => 'patternswp-license_section',
265 + 'pt_msg' => urlencode( $result->get_error_message() ),
266 + 'status' => 'error',
267 + ), admin_url( 'admin.php' ) );
268 + wp_safe_redirect( $redirect_url );
168 269 exit;
169 270 }
170 271
171 - if( isset( $response->activated ) && $response->activated == true ) {
172 - // Store license data in options
173 - $license_data = (array) $response;
174 - update_option( 'patternswp_plugin_license_data', $license_data );
175 - $this->clear_transients();
176 -
177 - $license_key_data = array( 'patternswp_pro_license_key' => $license_key );
178 - $license_key = update_option( 'patternswp_license_key', $license_key_data );
179 -
180 - wp_redirect( add_query_arg( array(
181 - 'page' => 'patternswp-license_section',
182 - 'pt_msg' => urlencode( 'License activated successfully.' ),
183 - 'status' => 'success'
184 - ), admin_url( 'admin.php' ) ) );
185 - exit;
186 -
187 - }
272 + $redirect_url = add_query_arg( array(
273 + 'page' => 'patternswp-license_section',
274 + 'pt_msg' => urlencode( $result['message'] ),
275 + 'status' => isset( $result['status'] ) ? $result['status'] : 'success',
276 + ), admin_url( 'admin.php' ) );
277 + wp_safe_redirect( $redirect_url );
278 + exit;
188 279 }
189 280
190 281 /**
191 282 * Clear transients
@@ -197,12 +288,14 @@
197 288 delete_transient( 'patternswp_category_type' );
198 289
199 290 // Delete all API-related transients
200 291 $pattern = '_transient_patternswp_';
292 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
201 293 $results = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s", $wpdb->esc_like( $pattern ) . '%' ) );
202 294
203 295 // Delete all patterns cache
204 296 $patterns = 'patterns_cache_';
297 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
205 298 $wpdb->query(
206 299 $wpdb->prepare(
207 300 "DELETE FROM $wpdb->options WHERE option_name LIKE %s",
208 301 $wpdb->esc_like( $patterns ) . '%'
@@ -215,16 +308,22 @@
215 308 }
216 309 }
217 310
218 311 /**
219 - * Display admin notice
312 + * Display admin notice (legacy URL flash messages — React also reads these via localize).
220 313 */
221 314 public function patternswp_display_admin_notice() {
222 - if ( isset( $_GET['pt_msg'] ) && ! empty( sanitize_text_field( wp_unslash( $_GET['pt_msg'] ) ) ) ) {
223 - $message = isset($_GET['pt_msg']) ? esc_html( urldecode( sanitize_text_field( wp_unslash( $_GET['pt_msg'] ) ) ) ) : '';
224 - $status = isset( $_GET['status'] ) && sanitize_text_field( wp_unslash( $_GET['status'] ) ) === 'success' ? 'updated' : 'error';
315 + // Notices are shown inside the React admin app when on PatternsWP pages.
316 + $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
317 + if ( $screen && isset( $screen->id ) && false !== strpos( $screen->id, 'patternswp' ) ) {
318 + return;
319 + }
320 +
321 + if ( isset( $_GET['pt_msg'] ) && ! empty( sanitize_text_field( wp_unslash( $_GET['pt_msg'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
322 + $message = isset($_GET['pt_msg']) ? esc_html( urldecode( sanitize_text_field( wp_unslash( $_GET['pt_msg'] ) ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
323 + $status = isset( $_GET['status'] ) && sanitize_text_field( wp_unslash( $_GET['status'] ) ) === 'success' ? 'updated' : 'error'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
225 324 printf( '<div class="%s notice is-dismissible"><p>%s</p></div>', esc_attr( $status ), esc_attr( $message ) );
226 325 }
227 326 }
228 327 }
229 328
230 -new PatternsWP_LicenseSection();
329 +new PatternsWP_LicenseSection();