File: /var/www/html/wp-content/plugins/memberpress/app/controllers/MeprRulesCtrl.php
<?php
if (!defined('ABSPATH')) {
die('You are not allowed to call this page directly.');
}
class MeprRulesCtrl extends MeprCptCtrl
{
/**
* Loads the necessary hooks for the rules controller.
*
* @return void
*/
public function load_hooks()
{
add_action('after_setup_theme', function () {
add_action(MeprOptions::fetch()->redirect_method, 'MeprRulesCtrl::rule_redirection', 3);
}, 20);
// I think the_content is called before the_content_feed, so this is redundant
// add_filter('the_content_feed', 'MeprRulesCtrl::rule_content', 999999, 1);.
add_filter('the_content', 'MeprRulesCtrl::rule_content', 999999, 1);
add_action('admin_init', 'MeprRulesCtrl::admin_rule_redirection', 3);
add_filter('comments_template', 'MeprRulesCtrl::rule_comments');
add_action('mod_rewrite_rules', 'MeprRulesCtrl::mod_rewrite_rules');
// All other stuff.
add_filter('bulk_actions-edit-memberpressrule', 'MeprRulesCtrl::disable_bulk');
add_filter('post_row_actions', 'MeprRulesCtrl::disable_row', 10, 2);
add_action('admin_enqueue_scripts', 'MeprRulesCtrl::enqueue_scripts');
add_action('admin_init', 'MeprRule::cleanup_db'); // Clear out all unused auto-save's.
add_action('manage_posts_custom_column', 'MeprRulesCtrl::custom_columns', 10, 2);
add_filter('manage_edit-memberpressrule_columns', 'MeprRulesCtrl::columns');
add_action('save_post', 'MeprRulesCtrl::save_postdata');
add_action('delete_post', 'MeprRulesCtrl::delete_access_rules', 10);
add_action('wp_ajax_mepr_show_content_dropdown', 'MeprRulesCtrl::display_content_dropdown');
add_action('wp_ajax_mepr_remove_access_condition', 'MeprRulesCtrl::remove_access_condition');
add_action('wp_ajax_mepr_rule_content_search', 'MeprRulesCtrl::ajax_content_search');
add_filter('default_title', 'MeprRulesCtrl::get_page_title_code');
add_filter('posts_results', 'MeprRulesCtrl::filter_protected_posts_for_rest', 10, 2);
// Add virtual capabilities.
add_filter('user_has_cap', 'MeprRulesCtrl::authorized_cap', 10, 3);
add_filter('user_has_cap', 'MeprRulesCtrl::product_authorized_cap', 10, 3); // Deprecated.
add_filter('user_has_cap', 'MeprRulesCtrl::rule_authorized_cap', 10, 3); // Deprecated.
add_filter('user_has_cap', 'MeprRulesCtrl::active_cap', 10, 3);
MeprHooks::add_shortcode('mepr_rule', 'MeprRulesCtrl::rule_shortcode'); // Deprecated.
MeprHooks::add_shortcode('mepr_active', 'MeprRulesCtrl::active_shortcode');
MeprHooks::add_shortcode('mepr_unauthorized_message', 'MeprRulesCtrl::unauthorized_message_shortcode');
MeprHooks::add_shortcode('mepr_show', 'MeprRulesCtrl::show_shortcode');
MeprHooks::add_shortcode('mepr_hide', 'MeprRulesCtrl::hide_shortcode');
// Cleanup list view.
add_filter('views_edit-' . MeprRule::$cpt, 'MeprAppCtrl::cleanup_list_view');
// Protect WooCommerce Products (this used to be included in our old WC add-on which has since been deprecated).
include_once(ABSPATH . 'wp-admin/includes/plugin.php');
if (!is_plugin_active('memberpress-woocommerce/main.php')) {
add_filter('woocommerce_is_purchasable', 'MeprRulesCtrl::override_wc_is_purchasable', 11, 2);
add_filter('woocommerce_product_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 2);
add_filter('woocommerce_variation_is_visible', 'MeprRulesCtrl::override_wc_is_visible', 11, 4);
add_filter('mepr_pre_run_rule_content', 'MeprRulesCtrl::dont_hide_wc_product_content', 11, 3);
}
}
/**
* Registers the custom post type for rules.
*
* @return void
*/
public function register_post_type()
{
$args = MeprHooks::apply_filters(
'mepr_' . MeprRule::$cpt . '_post_type_args',
[
'labels' => [
'name' => __('Rules', 'memberpress'),
'singular_name' => __('Rule', 'memberpress'),
'add_new' => __('Add New', 'memberpress'),
'add_new_item' => __('Add New Rule', 'memberpress'),
'edit_item' => __('Edit Rule', 'memberpress'),
'new_item' => __('New Rule', 'memberpress'),
'view_item' => __('View Rule', 'memberpress'),
'search_items' => __('Search Rules', 'memberpress'),
'not_found' => __('No Rules found', 'memberpress'),
'not_found_in_trash' => __('No Rules found in Trash', 'memberpress'),
'parent_item_colon' => __('Parent Rule:', 'memberpress'),
],
'public' => false,
'show_ui' => true,
'show_in_menu' => 'memberpress',
'capability_type' => 'page',
'hierarchical' => false,
'register_meta_box_cb' => 'MeprRulesCtrl::add_meta_boxes',
'rewrite' => false,
'supports' => ['title'],
]
);
register_post_type(MeprRule::$cpt, $args);
}
/**
* Retrieves the page title code.
*
* @param string $title The current page title.
*
* @return string The modified page title
*/
public static function get_page_title_code($title)
{
global $current_screen;
if (empty($title) && isset($current_screen->post_type) && $current_screen->post_type === MeprRule::$cpt) {
return __('All Content: ', 'memberpress');
} else {
return $title;
}
}
/**
* Filters protected posts for REST API access.
*
* @param array $posts The posts to filter.
* @param WP_Query $query The query object.
*
* @return array Filtered posts
*/
public static function filter_protected_posts_for_rest($posts, $query)
{
$mepr_options = MeprOptions::fetch();
if (!$mepr_options->enable_wp_rest_api_protection) {
return $posts;
}
// Check if the current request is a REST API request.
if (defined('REST_REQUEST') && REST_REQUEST && is_array($posts)) {
// Loop through the posts.
foreach ($posts as $key => $post) {
$uri = get_permalink($post);
// Check if the post is protected by MemberPress.
if (MeprRule::is_locked($post)) {
// Remove the protected post from the results.
unset($posts[$key]);
continue;
}
if ($uri !== false && MeprRule::is_uri_locked($uri)) {
// Remove the protected post from the results.
unset($posts[$key]);
}
}
// Re-index the array to prevent issues with keys.
$posts = array_values($posts);
}
return $posts;
}
/**
* Defines the columns for the rules list table.
*
* @param array $columns The existing columns.
*
* @return array The modified columns
*/
public static function columns($columns)
{
$columns = [
'cb' => '<input type="checkbox" />',
'ID' => __('ID', 'memberpress'),
'title' => __('Title', 'memberpress'),
'rule-type' => __('Type', 'memberpress'),
'rule-content' => __('Content', 'memberpress'),
'rule-products' => __('Access', 'memberpress'),
'rule-drip' => __('Drip time', 'memberpress'),
'rule-expiration' => __('Expiration time', 'memberpress'),
];
return $columns;
}
/**
* Renders custom columns for the rules list table.
*
* @param string $column The name of the column.
* @param integer $rule_id The ID of the rule.
*
* @return void
*/
public static function custom_columns($column, $rule_id)
{
$rule = new MeprRule($rule_id);
if ($rule->ID !== null) {
$rule_contents = MeprRule::get_contents_array($rule->mepr_type);
$types = MeprRule::get_types();
if ('ID' === $column) {
echo esc_html($rule->ID);
} elseif ('rule-type' === $column and isset($types[$rule->mepr_type])) {
echo esc_html($types[$rule->mepr_type]);
} elseif (
'rule-content' === $column and $rule->mepr_type !== 'custom' and
isset($rule_contents[$rule->mepr_content])
) {
echo esc_html($rule_contents[$rule->mepr_content]);
} elseif (
'rule-content' === $column and $rule->mepr_type === 'custom' and
isset($rule->mepr_content)
) {
echo esc_html($rule->mepr_content);
} elseif (
'rule-content' === $column and
strstr($rule->mepr_type, 'all_') !== false and
isset($rule->mepr_content)
) {
echo esc_html(
sprintf(
'%s: %s',
__('Except', 'memberpress'),
$rule->mepr_content
)
);
} elseif ('rule-products' === $column) {
echo esc_html(implode(', ', $rule->get_formatted_accesses()));
} elseif ('rule-drip' === $column) {
if ($rule->drip_enabled) {
$time = array_keys(MeprRule::get_time_units(), $rule->drip_unit, true);
echo esc_html(
sprintf(
// Translators: %1$s: time period, %2$s: time unit, %3$s: trigger.
__('%1$s %2$s after %3$s', 'memberpress'),
$rule->drip_amount,
$time[0],
MeprRule::get_expires_after($rule->drip_after, $rule->drip_after_fixed)
)
);
} else {
echo '--';
}
} elseif ('rule-expiration' === $column) {
if ($rule->expires_enabled) {
$time = array_keys(MeprRule::get_time_units(), $rule->expires_unit, true);
echo esc_html(
sprintf(
// Translators: %1$s: time period, %2$s: time unit, %3$s: trigger.
__('%1$s %2$s after %3$s', 'memberpress'),
$rule->expires_amount,
$time[0],
MeprRule::get_expires_after($rule->expires_after, $rule->expires_after_fixed)
)
);
} else {
echo '--';
}
}
}
}
/**
* Handles rule comments.
*
* @param string $template The template to use.
*
* @return string
*/
public static function rule_comments($template = '')
{
$current_post = MeprUtils::get_current_post();
$mepr_options = MeprOptions::fetch();
if (isset($current_post)) {
if (MeprRule::is_locked($current_post)) {
if (MeprHooks::apply_filters('mepr_rule_comments', true)) {
return MeprView::file('/shared/unauthorized_comments');
}
}
}
return $template;
}
/**
* Used to redirect unauthorized visitors if redirect_on_unauthorized is selected in MeprOptions or
* if we're protecting a WP controlled-URI.
*/
public static function rule_redirection()
{
global $post;
// Prevents us double matching a URI and causing a redirect loop.
if (isset($_GET['action']) && $_GET['action'] === 'mepr_unauthorized') {
return;
}
$uri = esc_url(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? '')));
$mepr_options = MeprOptions::fetch();
$delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url);
$is_ssl = MeprUtils::is_ssl();
// Add this filter to allow external resources
// to control whether to redirect away from this content
// if the resource sets the filter to FALSE then no redirect will occur.
if (!MeprHooks::apply_filters('mepr_pre_run_rule_redirection', true, $uri, $delim)) {
return;
}
// Let's check the URI's first ok?
// This is here to perform an unauthorized redirection based on the uri.
if (MeprRule::is_uri_locked($uri)) {
if ($mepr_options->redirect_on_unauthorized) { // Send to unauth page.
$redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=" . urlencode($uri);
} else { // Send to login page.
$redirect_url = $mepr_options->login_page_url('action=mepr_unauthorized&redirect_to=' . urlencode($uri));
}
// Handle SSL.
$redirect_url = ($is_ssl) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url;
MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr_rule_redirect_unauthorized_url', $redirect_url, $delim, $uri));
exit;
}
// If the URI isn't protected, let's check the other Rules.
if ($mepr_options->redirect_on_unauthorized) {
$do_redirect = MeprHooks::apply_filters('mepr_rule_do_redirection', self::should_do_redirect());
if (
(!is_singular() && $do_redirect) ||
($do_redirect && isset($post) && MeprRule::is_locked($post)) ||
(!is_user_logged_in() && isset($post) && $post->ID === $mepr_options->account_page_id)
) {
$redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to=" . urlencode($uri);
// Handle SSL.
$redirect_url = ($is_ssl) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url;
MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr_rule_redirect_unauthorized_url', $redirect_url, $delim, $uri));
exit;
}
}
}
/**
* Allow control of the admin dashboard URL's too
*
* @return void
*/
public static function admin_rule_redirection()
{
$uri = esc_url(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? '')));
$mepr_options = MeprOptions::fetch();
$delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url);
// This performs an unauthorized redirection based on the uri.
if (MeprRule::is_uri_locked($uri)) {
if ($mepr_options->redirect_on_unauthorized) { // Send to unauth page.
$redirect_url = "{$mepr_options->unauthorized_redirect_url}{$delim}action=mepr_unauthorized&redirect_to=" . urlencode($uri);
} else { // Send to login page.
$redirect_url = $mepr_options->login_page_url('action=mepr_unauthorized&redirect_to=' . urlencode($uri));
}
// Handle SSL.
$redirect_url = (MeprUtils::is_ssl()) ? str_replace('http:', 'https:', $redirect_url) : $redirect_url;
MeprUtils::wp_redirect($redirect_url);
exit;
}
}
/**
* Redirect to login page or unauth page
* Used by addons BBPress and MP Downloads
*
* @param WP_Post $post The post object.
* @return void
*/
public static function redirect_unauthorized($post)
{
$mepr_options = MeprOptions::fetch();
$uri = urlencode(esc_url(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? ''))));
if ($mepr_options->redirect_on_unauthorized) {
$delim = MeprAppCtrl::get_param_delimiter_char($mepr_options->unauthorized_redirect_url);
$redirect_to = "{$mepr_options->unauthorized_redirect_url}{$delim}mepr-unauth-page={$post->ID}&redirect_to={$uri}";
} else {
$redirect_to = $mepr_options->login_page_url("action=mepr_unauthorized&mepr-unauth-page={$post->ID}&redirect_to=" . $uri);
$redirect_to = (MeprUtils::is_ssl()) ? str_replace('http:', 'https:', $redirect_to) : $redirect_to;
}
MeprUtils::wp_redirect(MeprHooks::apply_filters('mepr_rule_redirect_unauthorized', $redirect_to, $uri));
exit;
}
/**
* Determines if a redirect should occur.
*
* @return boolean True if redirect should occur, false otherwise
*/
public static function should_do_redirect()
{
global $wp_query;
$mepr_options = MeprOptions::fetch();
if (!empty($wp_query->posts) && $mepr_options->redirect_non_singular) {
// If even one post on this non-singular page is protected, let's redirect brotha.
foreach ($wp_query->posts as $post) {
if (MeprRule::is_locked($post)) {
return true;
}
}
}
return is_singular();
}
/**
* Used to replace content for unauthorized visitors if redirect_on_unauthorized is not selected in MeprOptions.
*
* @param string $content The content to replace.
*
* @return string
*/
public static function rule_content($content)
{
$current_post = MeprUtils::get_current_post();
// This isn't a post? Just return the content then.
if ($current_post === false) {
return $content;
}
// WARNING the_content CAN be run more than once per page load
// so this static var prevents stuff from happening twice
// like cancelling a subscr or resuming etc...
static $already_run = [];
static $new_content = [];
static $content_length = [];
// Init this posts static values.
if (!isset($new_content[$current_post->ID]) || empty($new_content[$current_post->ID])) {
$already_run[$current_post->ID] = false;
$new_content[$current_post->ID] = '';
$content_length[$current_post->ID] = -1;
}
if ($already_run[$current_post->ID] && strlen($content) === $content_length[$current_post->ID]) {
return $new_content[$current_post->ID];
}
$content_length[$current_post->ID] = strlen($content);
$already_run[$current_post->ID] = true;
// Get the URI.
$uri = sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? ''));
// Add this filter to allow external resources
// to control whether to show or hide this content
// if the resource sets the filter to FALSE then it will not be protected.
if (!MeprHooks::apply_filters('mepr_pre_run_rule_content', true, $current_post, $uri)) {
// See notes above.
$new_content[$current_post->ID] = $content;
return $new_content[$current_post->ID];
}
if (MeprRule::is_locked($current_post) || (MeprRule::is_uri_locked($uri))) {
$content = do_shortcode(self::unauthorized_message($current_post));
} else {
// The user is allowed to see this content, but let's give developers one last chance to
// block it if necessary - will be very helpful for magazine style membership sites
// return TRUE here to block the content from this user.
if (MeprHooks::apply_filters('mepr_last_chance_to_block_content', false, $current_post, $uri)) {
$content = do_shortcode(self::unauthorized_message($current_post));
}
}
// See notes above.
$new_content[$current_post->ID] = $content;
return $new_content[$current_post->ID];
}
/**
* Shortcode for displaying unauthorized message.
*
* @param array $atts The attributes of the shortcode.
*
* @return string The unauthorized message
*/
public static function unauthorized_message_shortcode($atts = '')
{
$mepr_options = MeprOptions::fetch();
$message = '';
$post = isset($_REQUEST['mepr-unauth-page']) ? get_post(esc_html(sanitize_text_field(wp_unslash($_REQUEST['mepr-unauth-page'])))) : false;
if (
isset($_REQUEST['mepr-unauth-page']) &&
is_numeric($_REQUEST['mepr-unauth-page']) &&
$post
) {
$message = self::unauthorized_message($post);
} elseif (isset($GLOBALS['post'])) {
$message = self::unauthorized_message($GLOBALS['post']);
} else {
$message = wpautop($mepr_options->unauthorized_message);
}
return do_shortcode($message);
}
/**
* Displays the unauthorized message.
*
* @param WP_Post $post The post object.
*
* @return string The unauthorized message
*/
public static function unauthorized_message($post)
{
$mepr_options = MeprOptions::fetch();
$unauth = MeprRule::get_unauth_settings_for($post);
static $login_form_shown = false;
$show_login = ($unauth->show_login && !$login_form_shown);
// If this is a singular page, then allow it to be shown more than once
// It won't literally be shown on the page more than once, but in case something
// Calls the_content filter during an earlier hook, we'll want to make sure the form shows
// Up on the page itself still.
if ($show_login && !is_singular()) {
$login_form_shown = true;
}
try {
$login_ctrl = MeprCtrlFactory::fetch('login');
$form = MeprHooks::apply_filters('mepr_unauthorized_login_form', $login_ctrl->render_login_form(null, null, true), $post);
} catch (Exception $e) {
$form = '<a href="' . $mepr_options->login_page_url() . '">' . __('Login', 'memberpress') . '</a>';
}
ob_start();
if (MeprReadyLaunchCtrl::template_enabled('account') || MeprAppHelper::has_block('memberpress/pro-account-tabs')) {
MeprView::render('/readylaunch/shared/unauthorized_message', get_defined_vars());
} else {
if (isset($unauth->modern_paywall) && true === $unauth->modern_paywall && ! MeprAppHelper::is_memberpress_page($post)) {
wp_enqueue_script('mepr-modern-paywall', MEPR_JS_URL . '/modern_paywall.js', ['jquery'], MEPR_VERSION, true);
MeprView::render('/shared/modern_paywall_inline_css', get_defined_vars());
MeprView::render('/shared/unauthorized_message_modern_paywall', get_defined_vars());
} else {
MeprView::render('/shared/unauthorized_message', get_defined_vars());
}
}
$content = ob_get_clean();
// TODO: oEmbed still not working for some strange reason.
return MeprHooks::apply_filters('mepr_unauthorized_content', $content, $post);
}
/**
* Adds meta boxes for rules.
*
* @return void
*/
public static function add_meta_boxes()
{
add_meta_box('memberpress-rule-meta', __('Content & Access', 'memberpress'), 'MeprRulesCtrl::rule_meta_box', MeprRule::$cpt, 'normal', 'high');
add_meta_box('memberpress-rule-drip', __('Drip / Expiration', 'memberpress'), 'MeprRulesCtrl::rule_drip_meta_box', MeprRule::$cpt, 'normal', 'high');
add_meta_box('memberpress-rule-unauth', __('Unauthorized Access', 'memberpress'), 'MeprRulesCtrl::rule_unauth_meta_box', MeprRule::$cpt, 'normal', 'high');
}
/**
* Saves post data for rules.
*
* @param integer $post_id The ID of the post.
*
* @return integer|void
*/
public static function save_postdata($post_id)
{
$post = get_post($post_id);
if (!wp_verify_nonce((isset($_POST[MeprRule::$mepr_nonce_str])) ? sanitize_text_field(wp_unslash($_POST[MeprRule::$mepr_nonce_str])) : '', MeprRule::$mepr_nonce_str . wp_salt())) {
return $post_id; // Nonce prevents meta data from being wiped on move to trash.
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return $post_id;
}
if (defined('DOING_AJAX')) {
return;
}
if (!empty($post) && $post->post_type === MeprRule::$cpt) {
$rule = new MeprRule($post_id);
$rule->mepr_type = sanitize_text_field(wp_unslash($_POST[MeprRule::$mepr_type_str] ?? ''));
$rule->mepr_content = (('partial' !== $rule->mepr_type && isset($_POST[MeprRule::$mepr_content_str])) ? sanitize_text_field(wp_unslash($_POST[MeprRule::$mepr_content_str] ?? '')) : '');
$rule->drip_enabled = isset($_POST[MeprRule::$drip_enabled_str]);
$rule->drip_amount = sanitize_text_field(wp_unslash($_POST[MeprRule::$drip_amount_str] ?? ''));
$rule->drip_unit = sanitize_text_field(wp_unslash($_POST[MeprRule::$drip_unit_str] ?? ''));
$rule->drip_after = sanitize_text_field(wp_unslash($_POST[MeprRule::$drip_after_str] ?? ''));
$rule->drip_after_fixed = sanitize_text_field(wp_unslash($_POST[MeprRule::$drip_after_fixed_str] ?? ''));
$rule->expires_enabled = isset($_POST[MeprRule::$expires_enabled_str]);
$rule->expires_amount = sanitize_text_field(wp_unslash($_POST[MeprRule::$expires_amount_str] ?? ''));
$rule->expires_unit = sanitize_text_field(wp_unslash($_POST[MeprRule::$expires_unit_str] ?? ''));
$rule->expires_after = sanitize_text_field(wp_unslash($_POST[MeprRule::$expires_after_str] ?? ''));
$rule->expires_after_fixed = sanitize_text_field(wp_unslash($_POST[MeprRule::$expires_after_fixed_str] ?? ''));
$rule->unauth_excerpt_type = sanitize_text_field(wp_unslash($_POST[MeprRule::$unauth_excerpt_type_str] ?? ''));
$rule->unauth_excerpt_size = sanitize_text_field(wp_unslash($_POST[MeprRule::$unauth_excerpt_size_str] ?? ''));
$rule->unauth_message_type = sanitize_text_field(wp_unslash($_POST[MeprRule::$unauth_message_type_str] ?? ''));
$rule->unauth_message = wp_kses_post(wp_unslash($_POST[MeprRule::$unauth_message_str] ?? ''));
$rule->unauth_login = sanitize_text_field(wp_unslash($_POST[MeprRule::$unauth_login_str] ?? ''));
$rule->auto_gen_title = isset($_POST[MeprRule::$auto_gen_title_str]) && $_POST[MeprRule::$auto_gen_title_str] === 'true';
$rule->unauth_modern_paywall = isset($_POST[MeprRule::$unauth_modern_paywall_str]);
$rule->is_mepr_content_regexp = isset($_POST[MeprRule::$is_mepr_content_regexp_str]);
$rule->store_meta();
self::validate_rule_content($rule, $post_id);
// Delete rules first then add them back below.
MeprRuleAccessCondition::delete_all_by_rule($post_id);
// Let's store the access rules.
if (isset($_POST['mepr_access_row']) && !empty($_POST['mepr_access_row'])) {
$access_types = array_map('sanitize_text_field', wp_unslash($_POST['mepr_access_row']['type'] ?? []));
foreach ($access_types as $index => $access_type) {
$rule_access_condition = new MeprRuleAccessCondition(intval(wp_unslash($_POST['mepr_access_row']['rule_access_condition_id'][$index] ?? 0)));
$rule_access_condition->rule_id = $post_id;
$rule_access_condition->access_type = $access_type;
$rule_access_condition->access_operator = isset($_POST['mepr_access_row']['operator'][$index]) ? sanitize_text_field(wp_unslash($_POST['mepr_access_row']['operator'][$index])) : '';
$rule_access_condition->access_condition = isset($_POST['mepr_access_row']['condition'][$index]) ? sanitize_text_field(wp_unslash($_POST['mepr_access_row']['condition'][$index])) : '';
$rule_access_condition->store();
}
}
}
}
/**
* Deletes access rules.
*
* @param integer $post_id The ID of the post.
*
* @return void
*/
public static function delete_access_rules($post_id)
{
$rule = new MeprRule($post_id);
$rule->delete_access_conditions();
}
/**
* Displays the rule meta box.
*
* @return void
*/
public static function rule_meta_box()
{
global $post_id;
$rule = new MeprRule($post_id);
MeprView::render('/admin/rules/form', get_defined_vars());
}
/**
* Displays the rule drip meta box.
*
* @return void
*/
public static function rule_drip_meta_box()
{
global $post_id;
$rule = new MeprRule($post_id);
MeprView::render('/admin/rules/drip_form', get_defined_vars());
}
/**
* Displays the rule unauthorized meta box.
*
* @return void
*/
public static function rule_unauth_meta_box()
{
global $post_id;
$rule = new MeprRule($post_id);
MeprView::render('/admin/rules/unauth_meta_box', get_defined_vars());
}
/**
* Displays the content dropdown for rules.
*
* @return void
*/
public static function display_content_dropdown()
{
check_ajax_referer('content_dropdown', 'content_dropdown_nonce');
if (!isset($_POST['field_name']) || !isset($_POST['type'])) {
wp_die(esc_html__('Error', 'memberpress'));
}
if (MeprUtils::is_logged_in_and_an_admin()) {
MeprRulesHelper::content_dropdown(sanitize_text_field(wp_unslash($_POST['field_name'])), '', sanitize_text_field(wp_unslash($_POST['type'])));
}
die();
}
/**
* Removes access conditions.
*
* @return void
*/
public static function remove_access_condition()
{
check_ajax_referer('remove_access_condition', 'remove_access_condition_nonce');
if (!isset($_POST['rule_access_condition_id'])) {
wp_die(esc_html__('Error', 'memberpress'));
}
if (MeprUtils::is_logged_in_and_an_admin()) {
$rule_access_condition = new MeprRuleAccessCondition(sanitize_text_field(wp_unslash($_POST['rule_access_condition_id'])));
$rule_access_condition->destroy();
}
wp_die();
}
/**
* Disables a row in the rules list table.
*
* @param array $actions The actions to disable.
* @param WP_Post $post The post object.
*
* @return array
*/
public static function disable_row($actions, $post)
{
global $current_screen;
if (!isset($current_screen->post_type) || $current_screen->post_type !== MeprRule::$cpt) {
return $actions;
}
unset($actions['inline hide-if-no-js']); // Hides quick-edit.
return $actions;
}
/**
* Disables bulk actions in the rules list table.
*
* @param array $actions The actions to disable.
*
* @return array
*/
public static function disable_bulk($actions)
{
unset($actions['edit']); // Disables bulk edit.
return $actions;
}
/**
* Enqueue scripts and styles for the rules page.
*
* @return void
*/
public static function enqueue_scripts()
{
global $current_screen;
if ($current_screen->post_type === MeprRule::$cpt) {
$rules_json = [
'mepr_no_products_message' => __('Please select at least one Membership before saving.', 'memberpress'),
'types' => MeprRule::get_types(),
'content_dropdown_nonce' => wp_create_nonce('content_dropdown'),
'content_search_nonce' => wp_create_nonce('content_search'),
'remove_access_condition_nonce' => wp_create_nonce('remove_access_condition'),
'access_row' => MeprHooks::apply_filters('mepr_rules_js_access_row', [
'role' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'role']), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string('role'),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('role'),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string('role'),
],
'login_state' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'role']), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string('login_state'),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('login_state'),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string('login_state'),
],
'capability' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'capability']), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string('capability'),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('capability'),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string('capability'),
],
'membership' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'membership']), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string('membership'),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('membership'),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string('membership'),
],
'member' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(['access_type' => 'member']), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string('member'),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string('member'),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string('member'),
],
'blank' => [
'row_tpl' => MeprRulesHelper::access_row_string(new MeprRuleAccessCondition(), 1),
'types_tpl' => MeprRulesHelper::access_types_dropdown_string(),
'operator_tpl' => MeprRulesHelper::access_operators_dropdown_string(),
'condition_tpl' => MeprRulesHelper::access_conditions_field_string(),
],
]),
];
wp_register_style('mepr-jquery-ui-smoothness', MEPR_CSS_URL . '/vendor/jquery-ui/smoothness.min.css', [], '1.13.3');
wp_enqueue_style('jquery-ui-timepicker-addon', MEPR_CSS_URL . '/vendor/jquery-ui-timepicker-addon.css', ['mepr-jquery-ui-smoothness'], MEPR_VERSION);
wp_register_script('mepr-timepicker-js', MEPR_JS_URL . '/vendor/jquery-ui-timepicker-addon.js', ['jquery-ui-datepicker'], MEPR_VERSION);
wp_register_script('mepr-date-picker-js', MEPR_JS_URL . '/date_picker.js', ['mepr-timepicker-js'], MEPR_VERSION);
wp_register_script('rule-form-validator', MEPR_JS_URL . '/vendor/jquery.form-validator.min.js', ['jquery'], '2.3.26');
wp_dequeue_script('autosave'); // Disable auto-saving
// Need mepr-rules-js to load in the footer since this script doesn't fully use document.ready().
wp_enqueue_script('mepr-rules-js', MEPR_JS_URL . '/admin_rules.js', ['jquery','jquery-ui-autocomplete','mepr-date-picker-js','rule-form-validator'], MEPR_VERSION, true);
wp_register_style('mepr-simplegrid', MEPR_CSS_URL . '/vendor/simplegrid.css', [], MEPR_VERSION);
wp_enqueue_style('mepr-rules-css', MEPR_CSS_URL . '/admin-rules.css', ['mepr-simplegrid'], MEPR_VERSION);
wp_localize_script('mepr-rules-js', 'MeprRule', $rules_json);
wp_enqueue_script('mepr-helpers', MEPR_JS_URL . '/mphelpers.js', ['suggest'], MEPR_VERSION);
}
}
/**
* Modifies the rewrite rules.
*
* @param string $rules The existing rewrite rules.
*
* @return string The modified rewrite rules
*/
public static function mod_rewrite_rules($rules)
{
$mepr_options = MeprOptions::fetch();
// If disabled mod_rewrite is checked let's not go on.
if ($mepr_options->disable_mod_rewrite) {
return $rules;
}
$rule_uri = MEPR_URL . '/lock.php';
$rule_path = preg_replace('#^(https?:)?//[^/]+#', '', $rule_uri); // Grab the root.
$subdir = preg_replace('#^https?://[^/]+#', '', site_url());
$mepr_rules = "\n";
$mepr_rules .= "# BEGIN MemberPress Rules\n";
$mepr_rules .= "<IfModule mod_rewrite.c>\n\n";
// Make sure there's been a cookie set for us to access the file.
$mepr_rules .= "RewriteCond %{HTTP_COOKIE} mplk=([a-zA-Z0-9]+)\n";
// See if there's also a rule file for the rule hash.
$mepr_rules .= 'RewriteCond ' . MeprRule::rewrite_rule_file_dir(true) . "/%1 -f\n";
// If rule hash exists in query string, there's a rule file and they match then short circuit to the actual url.
$mepr_rules .= "RewriteRule ^(.*)$ - [L]\n\n";
// If the url is the lock url then don't lock it or we'll end up in an infinite redirect
// Don't need this now that we're bypassing php files alltogether
// $mepr_rules .= "RewriteRule memberpress\/lock\.php$ - [L]\n";
// Directories that we shouldn't allow to be protected.
$no_protect_dirs = MeprHooks::apply_filters('mepr_rewrite_rules_no_protect_dirs', ['wp-admin','wp-includes','wp-content/plugins','wp-content/themes'], $rules);
$npstr = implode('|', $no_protect_dirs);
$mepr_rules .= 'RewriteCond %{REQUEST_URI} !^/(' . $npstr . ")\n";
// File types that we will allow to be protected
// Eventually we can maybe make this configurable by the user ...
$protect_types = MeprHooks::apply_filters('mepr_rewrite_rules_protect_types', ['zip','gz','tar','rar','doc','docx','xls','xlsx','xlsm','pdf','mp4','m4v','mp3','ts','key','m3u8'], $rules);
$ptstr = implode('|', $protect_types);
$mepr_rules .= 'RewriteCond %{REQUEST_URI} \.(' . strtolower($ptstr) . '|' . strtoupper($ptstr) . ")$\n";
// All else fails ... run it through lock.php to see if it's protected.
$mepr_rules .= "RewriteRule . {$rule_path} [L]\n\n";
$mepr_rules .= "</IfModule>\n";
$mepr_rules .= "# END MemberPress Rules\n";
$mepr_rules = MeprHooks::apply_filters('mepr_rewrite_rules', $mepr_rules, $rules);
// Mepr rules must appear *AFTER* wp's rules because we
// don't know how wp will handle the uri unless its a file.
return $rules . $mepr_rules;
}
/**
* Deprecated
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
*
* @return string The modified content
*/
public static function rule_shortcode($atts, $content = '')
{
return self::protect_shortcode_content($atts, $content, 'mepr_rule');
}
/**
* Active shortcode
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
*
* @return string The modified content
*/
public static function active_shortcode($atts, $content = '')
{
return self::protect_shortcode_content($atts, $content);
}
/**
* Show shortcode
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
*
* @return string The modified content
*/
public static function show_shortcode($atts, $content = '')
{
return self::protect_shortcode_content($atts, $content, 'mepr_show');
}
/**
* Hide shortcode
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
*
* @return string The modified content
*/
public static function hide_shortcode($atts, $content = '')
{
return self::protect_shortcode_content($atts, $content, 'mepr_hide');
}
/**
* Protect shortcode content
*
* @param array $atts The attributes of the shortcode.
* @param string $content The content of the shortcode.
* @param string $shortcode_type The type of shortcode.
*
* @return string The modified content
*/
public static function protect_shortcode_content($atts, $content = '', $shortcode_type = 'mepr_active')
{
$mepr_options = MeprOptions::fetch();
// Allow single level shortcode nesting
// This only works if the inner shortcode does NOT have an ending tag.
$content = do_shortcode($content);
if ($shortcode_type === 'mepr_show') {
$hide_if_allowed = false;
} elseif ($shortcode_type === 'mepr_hide') {
$hide_if_allowed = true;
} else {
$hide_if_allowed = (
((isset($atts['hide']) && trim($atts['hide']) === 'true') ||
(isset($atts['ifallowed']) && trim($atts['ifallowed']) === 'hide'))
);
}
$rule_ids = [];
if (isset($atts['rule'])) {
$rule_ids = array_map('trim', explode(',', $atts['rule']));
}
if (isset($atts['rules'])) {
$rule_ids = array_map('trim', explode(',', $atts['rules']));
}
$unauth = '';
if (isset($atts['unauth'])) {
if (trim($atts['unauth']) === 'message' || trim($atts['unauth']) === 'both') {
if (isset($atts['unauth_message'])) {
$class = doing_action('render_block') ? 'mepr_block_error' : 'mepr_error';
$unauth = '<div class="' . esc_attr($class) . '">' . trim($atts['unauth_message']) . '</div>';
} else {
$unauth = MeprRule::get_custom_unauth_message_from_rule_ids($rule_ids);
}
}
if (trim($atts['unauth']) === 'login' || trim($atts['unauth']) === 'both') {
try {
$login_ctrl = MeprCtrlFactory::fetch('login');
$unauth .= '<div>' . $login_ctrl->render_login_form() . '</div>';
} catch (Exception $e) {
$unauth = '<div><a href="' . $mepr_options->login_page_url() . '">' . __('Login', 'memberpress') . '</a></div>';
}
}
}
$allowed = false;
if (isset($atts['if']) && preg_match('/^logged[ _-]?in$/', $atts['if'])) {
$allowed = MeprUtils::is_user_logged_in();
} elseif (isset($atts['if']) && preg_match('/^logged[ _-]?out$/', $atts['if'])) {
$allowed = !MeprUtils::is_user_logged_in();
} else {
// Check if we've been given sanitary input, if not this shortcode
// is no good so let's return the full content here.
if (MeprUtils::is_mepr_admin()) {
return ($hide_if_allowed ? $unauth : $content);
}
// Check if any of the rules has a login_state access condition.
$login_state_allowed = null;
foreach ($rule_ids as $rule_id) {
$rule = new MeprRule($rule_id);
foreach ($rule->access_conditions() as $condition) {
if ('login_state' === $condition->access_type) {
// Access condition is either 'logged_in' or 'guest'.
if ('logged_in' === $condition->access_condition) {
$login_state_allowed = MeprUtils::is_user_logged_in(); // Allowed if logged-in.
} else {
$login_state_allowed = !MeprUtils::is_user_logged_in(); // Allowed if guest.
}
break;
}
}
if (null !== $login_state_allowed) {
break;
}
}
if (null !== $login_state_allowed) {
$allowed = $login_state_allowed;
} else if (MeprUtils::is_user_logged_in()) {
// Deprecated.
if ($shortcode_type === 'mepr_rule') {
$allowed = (
(isset($atts['id']) && current_user_can('mepr-active', "rule: {$atts['id']}")) ||
(isset($atts['ids']) && current_user_can('mepr-active', "rules: {$atts['ids']}"))
);
} else {
$allowed = (
(isset($atts['if']) && current_user_can('mepr-active', $atts['if'])) ||
(isset($atts['id']) && current_user_can('mepr-active', $atts['id'])) ||
(isset($atts['ids']) && current_user_can('mepr-active', $atts['ids'])) ||
(isset($atts['rule']) && current_user_can('mepr-active', "rule: {$atts['rule']}")) ||
(isset($atts['rules']) && current_user_can('mepr-active', "rules: {$atts['rules']}")) ||
(isset($atts['product']) && current_user_can('mepr-active', "product: {$atts['product']}")) ||
(isset($atts['products']) && current_user_can('mepr-active', "products: {$atts['products']}")) ||
(isset($atts['membership']) && current_user_can('mepr-active', "membership: {$atts['membership']}")) ||
(isset($atts['memberships']) && current_user_can('mepr-active', "memberships: {$atts['memberships']}"))
);
}
}
}
$allowed = MeprHooks::apply_filters('mepr_pre_run_partial_rule', $allowed, $hide_if_allowed, $atts);
return ((($allowed && !$hide_if_allowed) || (!$allowed && $hide_if_allowed)) ? $content : $unauth);
}
/**
* This will only work once $post is in place in the wp request flow.
* Will support dashes, underscores, full plugin name, short plugin name and authorized or auth.
*
* @param array $caps The capabilities for the user.
* @param array $cap The capabilities being checked.
* @param array $args Additional arguments.
*
* @return array Modified capabilities
*/
public static function authorized_cap($caps, $cap, $args)
{
$regex = '(memberpress|mepr)[-_]auth(orized)?';
if (!isset($cap[0]) || !preg_match("/^{$regex}$/", $cap[0])) {
return $caps;
}
$caps[$cap[0]] = 1;
$current_post = MeprUtils::get_current_post();
// General MemberPress Authorized for this page.
if (
($current_post !== false && MeprRule::is_locked($current_post)) ||
MeprRule::is_uri_locked(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'] ?? '')))
) {
unset($caps[$cap[0]]);
}
return $caps;
}
/**
* Handles product authorized capabilities (deprecated).
*
* @param array $caps The capabilities for the user.
* @param string $cap The capability being checked.
* @param array $args Additional arguments.
*
* @return array Modified capabilities
*/
public static function product_authorized_cap($caps, $cap, $args)
{
$regex = '(memberpress|mepr)[-_](product|membership)[-_]auth(orized)?[-_](\d+)';
if (!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) {
return $caps;
}
// User is most likely a guest, so they don't have access to whatever we're doing here.
if (!isset($args[1]) || !$args[1]) {
return $caps;
}
$user = new MeprUser($args[1]);
$ids = $user->active_product_subscriptions();
if (MeprUtils::is_mepr_admin() || in_array((int) $m[4], array_map('intval', $ids), true)) {
$caps[$cap[0]] = 1;
}
return $caps;
}
/**
* Deprecated
* Handles rule authorized capabilities (deprecated).
*
* @param array $caps The capabilities for the user.
* @param string $cap The capability being checked.
* @param array $args Additional arguments.
*
* @return array Modified capabilities
*/
public static function rule_authorized_cap($caps, $cap, $args)
{
$regex = '(memberpress|mepr)[-_]rule[-_]auth(orized)?[-_](\d+)';
if (!isset($cap[0]) || !preg_match("/^{$regex}$/i", $cap[0], $m)) {
return $caps;
}
// User is most likely a guest, so they don't have access to whatever we're doing here.
if (!isset($args[1]) || !$args[1]) {
return $caps;
}
$rule_id = $m[3];
$user = new MeprUser($args[1]);
$rule = new MeprRule($rule_id);
if ($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) {
return $caps;
}
if ($user->has_access_from_rule($rule_id)) {
$caps[$cap[0]] = 1;
}
return $caps;
}
/**
* Is the user active on any membership, one specific rule or one specific membership?
*
* @param array $caps The capabilities for the user.
* @param string $cap The capability being checked.
* @param array $args Additional arguments.
*
* @return array Modified capabilities
*/
public static function active_cap($caps, $cap, $args)
{
$active_str = 'mepr-active';
if (!isset($cap[0]) || !preg_match("/^{$active_str}$/", $cap[0])) {
return $caps;
}
// Check for login_state access conditions on rules before the guest early return.
// This allows "Logged Out (Guest)" and "Logged In" rules to work with current_user_can.
if (isset($args[2]) && preg_match('/^rules?\s*[=:_-]?\s*((\d+\s*,\s*)*\d+)$/i', $args[2], $m)) {
$rule_ids = array_map('trim', explode(',', $m[1]));
foreach ($rule_ids as $rule_id) {
$rule = new MeprRule($rule_id);
if ($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) {
continue;
}
foreach ($rule->access_conditions() as $condition) {
if ('login_state' === $condition->access_type) {
// Access condition is either 'logged_in' or 'guest'.
if ('logged_in' === $condition->access_condition) {
if (MeprUtils::is_user_logged_in()) {
$caps[$active_str] = 1;
}
} else {
if (!MeprUtils::is_user_logged_in()) {
$caps[$active_str] = 1;
}
}
return $caps;
}
}
}
}
// User is most likely a guest, so they don't have access to whatever we're doing here.
if (!isset($args[1]) || !$args[1]) {
return $caps;
}
$user = new MeprUser($args[1]);
$ids = $user->active_product_subscriptions();
if (MeprUtils::is_mepr_admin($user->ID)) {
$caps[$active_str] = 1;
} else {
// Membership specific active.
if (isset($args[2])) {
// If it's a membership then check that it's in the active membership subscriptions array.
if (is_numeric($args[2]) && is_array($ids) && !empty($ids)) {
if (in_array((int) $args[2], array_map('intval', $ids), true)) {
$caps[$active_str] = 1;
}
} elseif (preg_match('/^((product|membership)s?\s*[=:_-]?\s*)?((\d+\s*,\s*)*\d+)$/i', $args[2], $m) && is_array($ids) && !empty($ids)) {
// If it's spelled out as a product or membership do the same thing here.
$product_ids = array_map('trim', explode(',', $m[3]));
if (is_array($product_ids) && !empty($product_ids)) {
$intersect = array_intersect($product_ids, $ids);
if (!empty($intersect)) {
$caps[$active_str] = 1;
}
}
} elseif (preg_match('/^rules?\s*[=:_-]?\s*((\d+\s*,\s*)*\d+)$/i', $args[2], $m)) {
// If it's an array then check that it's in the active membership subscriptions array.
$product_ids = [];
$rule_ids = array_map('trim', explode(',', $m[1]));
if (is_array($rule_ids) && !empty($rule_ids)) {
foreach ($rule_ids as $rule_id) {
$rule = new MeprRule($rule_id);
if ($rule->ID <= 0 || !$rule->has_dripped() || $rule->has_expired()) {
continue;
}
if ($user->has_access_from_rule($rule_id)) {
$caps[$active_str] = 1;
break;
}
}
}
}
} else {
$caps[$active_str] = 1;
}
}
return $caps;
}
/**
* AJAX content search
*
* @return void
*/
public static function ajax_content_search()
{
// Array( [action] => mepr_rule_content_search [type] => single_post [term] => you).
check_ajax_referer('content_search', 'content_search_nonce');
$type = sanitize_text_field(wp_unslash($_REQUEST['type'] ?? ''));
$term = sanitize_text_field(wp_unslash($_REQUEST['term'] ?? ''));
$data = MeprRule::search_content($type, $term);
die(json_encode($data));
}
/**
* Override WooCommerce is_purchasable
*
* @param boolean $is The current is_purchasable status.
* @param object $prd The product object.
*
* @return boolean Modified is_purchasable status
*/
public static function override_wc_is_purchasable($is, $prd)
{
// Bail if already locked, or is admin page, or is REST REQUEST.
if (!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) {
return $is;
}
if (is_object($prd) && $prd->is_type('variation')) {
$post = get_post($prd->get_parent_id());
} else {
$post = get_post($prd->get_id());
}
return !MeprRule::is_locked($post);
}
/**
* Override WooCommerce product visibility.
*
* @param boolean $is The current visibility status.
* @param integer $prd_id The product ID.
* @param integer|false $prd_parent_id The parent product ID, or false if not applicable.
* @param object|false $prd The product object, or false if not applicable.
* @return boolean
*/
public static function override_wc_is_visible($is, $prd_id, $prd_parent_id = false, $prd = false)
{
// Bail if already locked, or is admin page, or is REST REQUEST.
if (!$is || is_admin() || (defined('REST_REQUEST') && REST_REQUEST)) {
return $is;
}
if ($prd && $prd->is_type('variation')) {
$parent_product = wc_get_product($prd->get_parent_id());
// If the parent product (product variable) is not purchasable (i.e. it's hidden), hide the variation's attributes.
if (!$parent_product->is_purchasable()) {
add_filter('woocommerce_product_get_attributes', 'MeprRulesCtrl::hide_product_attributes');
}
}
$post = get_post($prd_id);
return !MeprRule::is_locked($post);
}
/**
* Hide product attributes for WooCommerce variations.
*
* @param array $attributes The product attributes.
* @return array Modified product attributes.
*/
public static function hide_product_attributes($attributes)
{
unset($attributes);
$attributes = []; // Prevent warnings from WC.
return $attributes;
}
/**
* Never hide WooCommerce the_content
*
* @param boolean $protect Whether to protect the content.
* @param WP_Post $post The post object.
* @param string $uri The URI of the request.
*
* @return boolean Modified protection status
*/
public static function dont_hide_wc_product_content($protect, $post, $uri)
{
if (isset($post) && isset($post->post_type) && $post->post_type === 'product') {
return false;
}
return $protect;
}
/**
* Validates rule content and force the post status to draft if it's empty.
*
* @param MeprRule $rule The rule object.
* @param integer $post_id The post ID.
*
* @return void
*/
public static function validate_rule_content($rule, $post_id)
{
// If the rule requires exclusion - Bailout.
if (0 === (int) $post_id || $rule->mepr_type === 'all' || (strstr($rule->mepr_type, 'all_') !== false && !preg_match('#^all_tax_#', $rule->mepr_type)) || $rule->mepr_type === 'partial') {
return;
}
$mepr_rules_content = isset($_POST[MeprRule::$mepr_content_str]) ? trim(sanitize_text_field(wp_unslash($_POST[MeprRule::$mepr_content_str]))) : '';
// If no content is added, force the status to draft.
if (empty($mepr_rules_content)) {
// Unhook so it doesn't loop infinitely.
remove_action('save_post', 'MeprRulesCtrl::save_postdata');
// Update the post status to draft.
$rule_post = [
'ID' => $post_id,
'post_status' => 'draft',
];
wp_update_post($rule_post);
// Hook it again.
add_action('save_post', 'MeprRulesCtrl::save_postdata');
MeprUtils::debug_log("Rule (#{$post_id}) content can't be empty. Post status forced to 'draft'");
}
}
}