PluginProbe
Passster – Password Protect Pages and Content / 4.3.16
Passster – Password Protect Pages and Content v4.3.16
4.3.16 4.3.15 4.3.14 4.3.12 4.3.13 4.3.11 4.3.10 4.3.9 4.3.8 4.3.7 4.3.6 4.3.5 trunk 3.5.4 3.5.5.2 3.5.5.8 3.5.5.9 4.0 4.1.4 4.2.10 4.2.11 4.2.12 4.2.13 4.2.14 4.2.15 All 48 releases
← All changes | inc/class-ps-rest-api.php +188 -2 4.3.154.3.16 View file →
@@ -110,8 +110,13 @@
110 110 'required' => false,
111 111 'type' => 'integer',
112 112 'sanitize_callback' => 'absint',
113 113 ),
114 + 'post_type' => array(
115 + 'required' => false,
116 + 'type' => 'string',
117 + 'sanitize_callback' => 'sanitize_key',
118 + ),
114 119 ),
115 120 ) );
116 121 // Hash password endpoint.
117 122 register_rest_route( 'passster/v1', '/hash', array(
@@ -208,8 +213,9 @@
208 213 $redirect = $request->get_param( 'redirect' );
209 214 $protection = $request->get_param( 'protection' );
210 215 $acf = $request->get_param( 'acf' );
211 216 $term_id = absint( $request->get_param( 'term_id' ) );
217 + $post_type_param = sanitize_key( (string) $request->get_param( 'post_type' ) );
212 218 // Default error response.
213 219 $error_message = $options['error'] ?? __( 'Invalid password.', 'content-protector' );
214 220 $remove_spaces = apply_filters( 'passster_remove_spaces_from_list', true );
215 221 if ( empty( $protection ) ) {
@@ -246,8 +252,39 @@
246 252 'success' => false,
247 253 'error' => $error_message,
248 254 ), 200);
249 255 }
256 + // Post type archive protection: post_type is passed directly from the form
257 + // (no single post ID exists to key an unlock request off of on an archive page).
258 + if ( !empty( $post_type_param ) && class_exists( 'passster\\PS_Post_Type_Lock' ) && PS_Post_Type_Lock::is_post_type_protected( $post_type_param ) ) {
259 + $post_type_config = PS_Post_Type_Lock::get_post_type_config( $post_type_param );
260 + $result = $this->validate_post_type_unlock(
261 + $input,
262 + $type,
263 + $post_type_config,
264 + null,
265 + '',
266 + $remove_spaces
267 + );
268 + if ( $result['valid'] ) {
269 + do_action( 'passster_validation_success', $input );
270 + PS_Helper::remember_unlock( hash_hmac( 'sha256', $input, get_option( 'passster_secure_key' ) ) );
271 + if ( !empty( $post_type_config['passster_activate_misc_settings'] ) && !empty( $post_type_config['passster_redirect_url'] ) ) {
272 + return new \WP_REST_Response(array(
273 + 'success' => true,
274 + 'redirect' => esc_url_raw( $post_type_config['passster_redirect_url'] ),
275 + ), 200);
276 + }
277 + return new \WP_REST_Response(array(
278 + 'success' => true,
279 + 'requires_reload' => true,
280 + ), 200);
281 + }
282 + return new \WP_REST_Response(array(
283 + 'success' => false,
284 + 'error' => $error_message,
285 + ), 200);
286 + }
250 287 // Parent page protection inheritance.
251 288 $parent_id = wp_get_post_parent_id( $post_id );
252 289 if ( $parent_id ) {
253 290 $activate_protection = get_post_meta( $parent_id, 'passster_activate_protection', true );
@@ -268,10 +305,13 @@
268 305 }
269 306 // Category/taxonomy protection: if the post itself has no protection,
270 307 // check if it belongs to a protected category and validate against term meta.
271 308 $post_protection = get_post_meta( $post_id, 'passster_activate_protection', true );
272 - if ( !$post_protection && 'full' === $protection && class_exists( 'passster\\PS_Category_Lock' ) ) {
273 - $term_data = PS_Category_Lock::get_instance()->get_protected_term_for_post( $post_id );
309 + $term_data = null;
310 + if ( !$post_protection && 'full' === $protection ) {
311 + if ( class_exists( 'passster\\PS_Category_Lock' ) ) {
312 + $term_data = PS_Category_Lock::get_instance()->get_protected_term_for_post( $post_id );
313 + }
274 314 if ( $term_data ) {
275 315 // Use term redirect if no redirect was sent from the frontend.
276 316 if ( empty( $redirect ) ) {
277 317 $redirect = get_term_meta( $term_data['term_id'], 'passster_redirect_url', true );
@@ -314,8 +354,53 @@
314 354 'success' => false,
315 355 'error' => $error_message,
316 356 ), 200);
317 357 }
358 + // Post type level protection: applies only when the post has neither its
359 + // own protection nor a protected category/term (both take precedence).
360 + if ( class_exists( 'passster\\PS_Post_Type_Lock' ) ) {
361 + $post_type = get_post_type( $post_id );
362 + if ( $post_type && PS_Post_Type_Lock::is_post_type_protected( $post_type ) ) {
363 + $config = PS_Post_Type_Lock::get_post_type_config( $post_type );
364 + if ( empty( $redirect ) && !empty( $config['passster_activate_misc_settings'] ) && !empty( $config['passster_redirect_url'] ) ) {
365 + $redirect = $config['passster_redirect_url'];
366 + }
367 + $result = $this->validate_post_type_unlock(
368 + $input,
369 + $type,
370 + $config,
371 + $post,
372 + $content,
373 + $remove_spaces
374 + );
375 + if ( $result['valid'] ) {
376 + $response_data = array(
377 + 'success' => true,
378 + );
379 + if ( !empty( $redirect ) ) {
380 + $response_data['redirect'] = $redirect;
381 + } elseif ( $this->content_uses_page_builder( $result['content'], $post_id ) ) {
382 + $response_data['requires_reload'] = true;
383 + } else {
384 + $response_data['content'] = apply_filters( 'the_content', str_replace( '{post-id}', $post_id, $result['content'] ) );
385 + }
386 + do_action(
387 + 'passsster_track_record',
388 + $post_id,
389 + $input,
390 + 'full'
391 + );
392 + do_action( 'passster_validation_success', $input );
393 + PS_Helper::remember_unlock( hash_hmac( 'sha256', $input, get_option( 'passster_secure_key' ) ) );
394 + return new \WP_REST_Response($response_data, 200);
395 + }
396 + // Post type protection exists but validation failed.
397 + return new \WP_REST_Response(array(
398 + 'success' => false,
399 + 'error' => $error_message,
400 + ), 200);
401 + }
402 + }
318 403 }
319 404 // Validate based on type.
320 405 $valid = false;
321 406 $result_content = '';
@@ -933,8 +1018,109 @@
933 1018 break;
934 1019 case 'password_list':
935 1020 break;
936 1021 case 'password_lists':
1022 + break;
1023 + }
1024 + return array(
1025 + 'valid' => false,
1026 + 'content' => '',
1027 + );
1028 + }
1029 +
1030 + /**
1031 + * Validate unlock for posts protected wholesale via post type level protection
1032 + * (password stored in the post type's configuration, not on the post itself).
1033 + *
1034 + * @param string $input User input.
1035 + * @param string $type Protection type (password, passwords, password_list, password_lists).
1036 + * @param array $config Post type configuration.
1037 + * @param \WP_Post $post Post object.
1038 + * @param string $content Post content.
1039 + * @param bool $remove_spaces Whether to remove spaces from password lists.
1040 + * @return array
1041 + */
1042 + private function validate_post_type_unlock(
1043 + $input,
1044 + $type,
1045 + array $config,
1046 + $post,
1047 + $content,
1048 + $remove_spaces
1049 + ) {
1050 + // $post is null for archive-level unlocks, where there's no single post to check.
1051 + if ( $post && 'publish' !== $post->post_status ) {
1052 + return array(
1053 + 'valid' => false,
1054 + 'content' => '',
1055 + );
1056 + }
1057 + switch ( $type ) {
1058 + case 'password':
1059 + if ( !empty( $config['passster_password'] ) && $input === $config['passster_password'] ) {
1060 + return array(
1061 + 'valid' => true,
1062 + 'content' => $content,
1063 + );
1064 + }
1065 + break;
1066 + case 'passwords':
1067 + $passwords_str = $config['passster_passwords'];
1068 + if ( $remove_spaces ) {
1069 + $passwords_str = str_replace( ' ', '', $passwords_str );
1070 + }
1071 + $passwords = explode( ',', $passwords_str );
1072 + if ( !empty( $passwords ) && in_array( $input, $passwords, true ) ) {
1073 + return array(
1074 + 'valid' => true,
1075 + 'content' => $content,
1076 + );
1077 + }
1078 + break;
1079 + case 'password_list':
1080 + $list_id = $config['passster_password_list'];
1081 + if ( !empty( $list_id ) ) {
1082 + $passwords_str = get_post_meta( $list_id, 'passster_passwords', true );
1083 + if ( $remove_spaces ) {
1084 + $passwords_str = str_replace( ' ', '', $passwords_str );
1085 + }
1086 + $passwords = explode( ',', $passwords_str );
1087 + if ( in_array( $input, $passwords, true ) ) {
1088 + do_action(
1089 + 'passster_validation_success_list',
1090 + $input,
1091 + $list_id,
1092 + ( $post ? $post->ID : 0 )
1093 + );
1094 + PS_Conditional::maybe_expire_password_from_list__premium_only( $input, $passwords, $list_id );
1095 + return array(
1096 + 'valid' => true,
1097 + 'content' => $content,
1098 + );
1099 + }
1100 + }
1101 + break;
1102 + case 'password_lists':
1103 + foreach ( (array) $config['passster_password_lists'] as $list_id ) {
1104 + $passwords_str = get_post_meta( $list_id, 'passster_passwords', true );
1105 + if ( $remove_spaces ) {
1106 + $passwords_str = str_replace( ' ', '', $passwords_str );
1107 + }
1108 + $passwords = explode( ',', $passwords_str );
1109 + if ( in_array( $input, $passwords, true ) ) {
1110 + do_action(
1111 + 'passster_validation_success_list',
1112 + $input,
1113 + $list_id,
1114 + ( $post ? $post->ID : 0 )
1115 + );
1116 + PS_Conditional::maybe_expire_password_from_list__premium_only( $input, $passwords, $list_id );
1117 + return array(
1118 + 'valid' => true,
1119 + 'content' => $content,
1120 + );
1121 + }
1122 + }
937 1123 break;
938 1124 }
939 1125 return array(
940 1126 'valid' => false,