558 lines
15 KiB
PHP
558 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* The public-facing functionality of the plugin.
|
|
*
|
|
* @link https://passwordprotectwp.com
|
|
* @since 1.0.0
|
|
*
|
|
* @package Password_Protect_Page
|
|
* @subpackage Password_Protect_Page/public
|
|
*/
|
|
|
|
/**
|
|
* The public-facing functionality of the plugin.
|
|
*
|
|
* Defines the plugin name, version, and two examples hooks for how to
|
|
* enqueue the public-facing stylesheet and JavaScript.
|
|
*
|
|
* @package Password_Protect_Page
|
|
* @subpackage Password_Protect_Page/public
|
|
* @author BWPS <hello@preventdirectaccess.com>
|
|
*/
|
|
class PPW_Public {
|
|
|
|
/**
|
|
* The ID of this plugin.
|
|
*
|
|
* @since 1.0.0
|
|
* @access private
|
|
* @var string $plugin_name The ID of this plugin.
|
|
*/
|
|
private $plugin_name;
|
|
|
|
/**
|
|
* The version of this plugin.
|
|
*
|
|
* @since 1.0.0
|
|
* @access private
|
|
* @var string $version The current version of this plugin.
|
|
*/
|
|
private $version;
|
|
|
|
/**
|
|
* Class PPW_Password_Services
|
|
*
|
|
* @var PPW_Password_Services
|
|
*/
|
|
private $password_services;
|
|
|
|
/**
|
|
* Initialize the class and set its properties.
|
|
*
|
|
* @param string $plugin_name The name of the plugin.
|
|
* @param string $version The version of this plugin.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function __construct( $plugin_name, $version ) {
|
|
$this->plugin_name = $plugin_name;
|
|
$this->version = $version;
|
|
$this->password_services = new PPW_Password_Services();
|
|
}
|
|
|
|
/**
|
|
* Register the stylesheets and javascript for the public-facing side of the site.
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
public function enqueue_assets() {
|
|
|
|
/**
|
|
* This function is provided for demonstration purposes only.
|
|
*
|
|
* An instance of this class should be passed to the run() function
|
|
* defined in Password_Protect_Page_Loader as all of the hooks are defined
|
|
* in that particular class.
|
|
*
|
|
* The Password_Protect_Page_Loader will then create the relationship
|
|
* between the defined hooks and the functions defined in this
|
|
* class.
|
|
*/
|
|
|
|
}
|
|
|
|
/**
|
|
* Filter before render content.
|
|
*
|
|
* @param string $content Content of post/page.
|
|
*
|
|
* @return mixed
|
|
* @deprecated Because we only use post_password_required to show login form.
|
|
* @since 1.2.2 Deprecated for function, we will remove it after 2 release.
|
|
*/
|
|
public function ppw_filter_content( $content ) {
|
|
if ( ! in_the_loop() ) {
|
|
return $content;
|
|
}
|
|
|
|
$post = get_post();
|
|
if ( is_null( $post ) ) {
|
|
return $content;
|
|
}
|
|
|
|
$post_id = $post->ID;
|
|
$is_pro_activate = apply_filters( PPW_Constants::HOOK_IS_PRO_ACTIVATE, false );
|
|
if ( $is_pro_activate ) {
|
|
return apply_filters( PPW_Constants::HOOK_CHECK_PASSWORD_BEFORE_RENDER_CONTENT, $content, $post_id );
|
|
}
|
|
|
|
return $this->ppw_free_content_filter( $content, $post_id );
|
|
}
|
|
|
|
/**
|
|
* Filter content for free version
|
|
*
|
|
* @param array $post_id Data from client.
|
|
* @param string $content Data from client.
|
|
*
|
|
* @return bool|string
|
|
* @deprecated
|
|
*
|
|
*/
|
|
private function ppw_free_content_filter( $content, $post_id ) {
|
|
// 1. Check page/post is protected.
|
|
$result = $this->password_services->is_protected_content( $post_id );
|
|
if ( false === $result ) {
|
|
return $content;
|
|
}
|
|
|
|
// 2. Check password in cookie.
|
|
$passwords = $result['passwords'];
|
|
if ( $this->password_services->is_valid_cookie( $post_id, $passwords, PPW_Constants::COOKIE_NAME ) ) {
|
|
return $content;
|
|
}
|
|
|
|
// 3. Form rendering.
|
|
if ( $result['has_global_passwords'] || ( $result['has_role_passwords'] && $result['has_current_role_password'] ) ) {
|
|
return ppw_core_render_login_form();
|
|
}
|
|
|
|
return '<p><strong>This page is protected. Please try again or contact the website owner.</strong></p>';
|
|
}
|
|
|
|
/**
|
|
* Post class
|
|
*
|
|
* @param array $classes Classes.
|
|
*
|
|
* @return array
|
|
* @since 1.5.0 Mark deprecated function.
|
|
* @deprecated
|
|
*/
|
|
public function ppw_post_class( $classes ) {
|
|
$classes[] = PPW_Constants::CUSTOM_POST_CLASS;
|
|
|
|
return $classes;
|
|
}
|
|
|
|
/**
|
|
* Show custom login form which protected by PPW Plugin, it will replace default form of WordPress.
|
|
*
|
|
* @param string $output The password form HTML output.
|
|
*
|
|
* @return string The password form HTML output.
|
|
*
|
|
* @global WP_Post $post Post object
|
|
* @since 1.2.2 Init the_password_form
|
|
*/
|
|
public function ppw_the_password_form( $output ) {
|
|
$post = $GLOBALS['post'];
|
|
if ( empty( $post->ID ) || ! ppw_is_post_type_selected_in_setting( $post->post_type ) ) {
|
|
return $output;
|
|
}
|
|
|
|
$should_render_form = apply_filters( PPW_Constants::HOOK_SHOULD_RENDER_PASSWORD_FORM, true );
|
|
|
|
if ( ! $should_render_form ) {
|
|
return '';
|
|
}
|
|
|
|
return ppw_core_render_login_form();
|
|
}
|
|
|
|
/**
|
|
* Only render text in all page diff post/page custom post type which it is not have post_id input.
|
|
* Check a site is post/page or custom post type
|
|
* Use regex to check it is our password form then render text.
|
|
*
|
|
* @param string $content Content of the post.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function ppw_the_content( $content ) {
|
|
// Do not handle on admin page.
|
|
if ( is_admin() ) {
|
|
return $content;
|
|
}
|
|
|
|
$is_show_excerpt = ppw_core_get_setting_type_bool_by_option_name( PPW_Constants::PROTECT_EXCERPT, PPW_Constants::MISC_OPTIONS );
|
|
if ( is_singular() && ! $is_show_excerpt ) {
|
|
return $content;
|
|
}
|
|
|
|
$post = get_post();
|
|
// Check post type is selected.
|
|
if ( ! $post || ! ppw_is_post_type_selected_in_setting( $post->post_type ) ) {
|
|
return $content;
|
|
}
|
|
|
|
// Check it is password form.
|
|
if ( post_password_required() ) {
|
|
|
|
return ppw_handle_protected_content( $post, $content, $is_show_excerpt );
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Register shortcodes
|
|
*/
|
|
public function register_shortcodes() {
|
|
$this->create_caps();
|
|
|
|
PPW_Shortcode::get_instance();
|
|
}
|
|
|
|
/**
|
|
* Create PPWP Capabilities.
|
|
*/
|
|
public function create_caps() {
|
|
global $wp_roles;
|
|
|
|
if ( ! class_exists( 'WP_Roles' ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! isset( $wp_roles ) ) {
|
|
$wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
|
|
}
|
|
|
|
$capabilities = array(
|
|
'ppwp_manage_options'
|
|
);
|
|
|
|
foreach ( $capabilities as $cap ) {
|
|
$wp_roles->add_cap( 'administrator', $cap );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check logic and hide pages/posts protected
|
|
*
|
|
* @param string $where The WHERE clause of the query.
|
|
* @param WP_Query $wp_query The WP_Query instance (passed by reference).
|
|
*
|
|
* @return string
|
|
*/
|
|
public function handle_hide_post_protected( $where, $wp_query ) {
|
|
if ( is_admin() ) {
|
|
return $where;
|
|
}
|
|
|
|
return $this->password_services->handle_hide_post_protected( $where, $wp_query );
|
|
}
|
|
|
|
/**
|
|
* Check logic and hide posts protected in recent post
|
|
*
|
|
* @param array $posts_args An array of arguments used to retrieve the recent posts.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function handle_hide_post_protected_recent_post( $posts_args ) {
|
|
if ( is_admin() ) {
|
|
return $posts_args;
|
|
}
|
|
|
|
return $this->password_services->handle_hide_post_protected_recent_post( $posts_args );
|
|
}
|
|
|
|
/**
|
|
* Check logic and hide posts protected in next and previous post
|
|
*
|
|
* @param string $where The WHERE clause of the query.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function handle_hide_post_protected_next_and_previous( $where ) {
|
|
if ( is_admin() ) {
|
|
return $where;
|
|
}
|
|
|
|
return $this->password_services->handle_hide_post_protected_next_and_previous( $where );
|
|
}
|
|
|
|
/**
|
|
* Check condition and exclude protected page in list page get by function get_pages
|
|
*
|
|
* @param array $pages List of pages to retrieve.
|
|
* @param array $param Array of get_pages() arguments.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function handle_hide_page_protected( $pages, $param ) {
|
|
if ( is_admin() ) {
|
|
return $pages;
|
|
}
|
|
|
|
return $this->password_services->handle_hide_page_protected( $pages );
|
|
}
|
|
|
|
/**
|
|
* Check condition and exclude page/post protected in Yoast SEO XML Sitemaps
|
|
*
|
|
* @param array $ids List page_id/post_id exclude in Yoast SEO XML Sitemaps.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function handle_hide_page_protected_yoast_seo_sitemaps( $ids ) {
|
|
if ( is_admin() ) {
|
|
return $ids;
|
|
}
|
|
|
|
return $this->password_services->handle_hide_page_protected_yoast_seo_sitemaps( $ids );
|
|
}
|
|
|
|
/**
|
|
* Validate login.
|
|
*/
|
|
public function ppw_core_validate_login() {
|
|
/**
|
|
* Should check request have parameter generated by PPF Form.
|
|
*/
|
|
if ( ! isset( $_GET['action'] ) || ! isset( $_GET['type'] ) || ! isset( $_GET[ PPW_Constants::CALL_BACK_URL_PARAM ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Processing form data without nonce verification. - Not verify nonce for password validate.
|
|
return;
|
|
}
|
|
if ( 'ppw_postpass' !== $_GET['action'] || 'individual' !== $_GET['type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Processing form data without nonce verification. - Not verify nonce for password validate.
|
|
return;
|
|
}
|
|
|
|
if ( ! ppw_core_get_setting_type_bool_by_option_name( PPW_Constants::USE_CUSTOM_FORM_ACTION, PPW_Constants::MISC_OPTIONS ) ) {
|
|
return;
|
|
}
|
|
|
|
// It is post method and have post_password input from user.
|
|
if ( ! isset( $_POST['post_password'] ) ) {
|
|
wp_safe_redirect( $this->password_services->get_referer_url() );
|
|
exit();
|
|
}
|
|
|
|
// Get post_id from referer url if Post data is not exist post_id.
|
|
$post_id = ppw_get_post_id_from_request();
|
|
|
|
if ( empty( $post_id ) ) {
|
|
wp_safe_redirect( $this->password_services->get_referer_url() );
|
|
exit();
|
|
}
|
|
|
|
$password = wp_unslash( $_POST['post_password'] ); // phpcs:ignore -- not sanitize password because we allow all character.
|
|
|
|
$this->password_services->handle_after_enter_password_in_password_form( $post_id, $password );
|
|
}
|
|
|
|
/**
|
|
* Generate action URL.
|
|
*
|
|
* @param string $action_url Action URL.
|
|
*
|
|
* @return string Action URL after generated.
|
|
*/
|
|
public function ppw_core_get_ppf_action_url( $action_url ) {
|
|
$_get = wp_unslash( $_GET ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verification for render UI request.
|
|
if ( ! ppw_core_get_setting_type_bool_by_option_name( PPW_Constants::USE_CUSTOM_FORM_ACTION, PPW_Constants::MISC_OPTIONS ) ) {
|
|
return $action_url;
|
|
}
|
|
|
|
$callback_value = rawurlencode( apply_filters( PPW_Constants::HOOK_CALLBACK_URL, get_permalink() ) );
|
|
$url = add_query_arg(
|
|
array(
|
|
'action' => 'ppw_postpass',
|
|
'type' => 'individual',
|
|
PPW_Constants::CALL_BACK_URL_PARAM => $callback_value,
|
|
),
|
|
null
|
|
);
|
|
|
|
if ( isset( $_get['ppws'] ) ) {
|
|
$url = add_query_arg( 'ppws', $_get['ppws'], $url );
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* Set cookie time for password.
|
|
*
|
|
* @param integer $time Expired time of a cookie.
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function set_cookie_time( $time ) {
|
|
if ( ! isset( $_GET['ppws'] ) || '1' !== $_GET['ppws'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verification for set cookie function, because already handle in others password checking function.
|
|
return $time;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Set cookie time for password.
|
|
*
|
|
* @param string $form_action Form action URL.
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function set_sitewide_form_action( $form_action ) {
|
|
$_get = wp_unslash( $_GET ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verification for render UI function.
|
|
if ( isset( $_get['ppws'] ) ) {
|
|
$form_action = $form_action . '&ppws=' . $_get['ppws'];
|
|
}
|
|
|
|
return $form_action;
|
|
}
|
|
|
|
/**
|
|
* Handle access link with ppw_ac parameter and without encoding URL.
|
|
*/
|
|
public function handle_access_link() {
|
|
if ( ! isset( $_GET['ppw_ac'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- We no need to handle nonce verification for quick access link.
|
|
return;
|
|
}
|
|
if ( ! is_singular() ) {
|
|
return;
|
|
}
|
|
|
|
$password = wp_unslash( $_GET['ppw_ac'] ); // phpcs:ignore -- not sanitize password because we allow all character.
|
|
$post_id = get_the_ID();
|
|
$permalink = get_permalink( $post_id );
|
|
$current_url = apply_filters( 'ppwp_access_link', $permalink, $post_id );
|
|
|
|
$password_service = new PPW_Password_Services();
|
|
$is_valid = $password_service->is_valid_password_from_request( $post_id, $password );
|
|
|
|
if ( $is_valid ) {
|
|
// Bypass single password.
|
|
add_filter( 'post_password_required', '__return_false', 50 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate password with "No Reload Page" Option.
|
|
*/
|
|
public function ppw_validate_password() {
|
|
if ( isset( $_POST['nonce'] ) && ! wp_verify_nonce( $_POST['nonce'], 'ppw_password_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- We no need to sanitize here
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => 'Cookie nonce is invalid',
|
|
),
|
|
403
|
|
);
|
|
wp_die();
|
|
}
|
|
if ( ! isset( $_POST['post_password'] ) ) {
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => 'Password doest not exist',
|
|
),
|
|
400
|
|
);
|
|
wp_die();
|
|
}
|
|
if ( empty( $_POST['post_id'] ) ) {
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => 'Post ID is empty',
|
|
),
|
|
400
|
|
);
|
|
wp_die();
|
|
}
|
|
$post_id = absint( $_POST['post_id'] );
|
|
$password = wp_unslash( $_POST['post_password'] ); // phpcs:ignore -- we allow all value for password
|
|
|
|
// Not check password if post does not exist.
|
|
$post = get_post( $post_id );
|
|
if ( empty( $post ) ) {
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => 'Post not found',
|
|
),
|
|
400
|
|
);
|
|
wp_die();
|
|
}
|
|
|
|
// Check with recaptcha if user turn on this option.
|
|
$using_recaptcha = PPW_Recaptcha::get_instance()->using_single_recaptcha();
|
|
if ( $using_recaptcha && ! PPW_Recaptcha::get_instance()->is_valid_recaptcha() ) {
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => PPW_Recaptcha::get_instance()->get_error_message(),
|
|
),
|
|
400
|
|
);
|
|
wp_die();
|
|
}
|
|
|
|
$post_content = ppw_support_third_party_content_plugin( $post_id, $post->post_content );
|
|
$post_content = apply_filters( 'the_content', $post_content );
|
|
|
|
do_action( 'ppw_ajax_before_validate_password', $post_id, $password, $post_content );
|
|
|
|
$password_service = new PPW_Password_Services();
|
|
$is_valid = $password_service->is_valid_password_from_request( $post_id, $password );
|
|
|
|
if ( ! $is_valid ) {
|
|
wp_send_json(
|
|
array(
|
|
'success' => false,
|
|
'message' => ppw_core_get_error_msg( $post_id ),
|
|
),
|
|
400
|
|
);
|
|
wp_die();
|
|
}
|
|
|
|
wp_send_json(
|
|
array(
|
|
'success' => true,
|
|
'post_content' => '<div>' . do_shortcode( $post_content ) . '</div>',
|
|
'message' => 'The password you entered is correct',
|
|
),
|
|
200
|
|
);
|
|
wp_die();
|
|
}
|
|
|
|
/**
|
|
* Support our action to compatibility with Divi builder.
|
|
*
|
|
* @param array $actions Ajax Actions.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function add_action_to_divi( $actions ) {
|
|
$actions[] = 'ppw_validate_password';
|
|
|
|
return $actions;
|
|
}
|
|
|
|
}
|