Files

1252 lines
43 KiB
PHP

<?php
/**
* @package WP Content Aware Engine
* @author Joachim Jensen <joachim@dev.institute>
* @license GPLv3
* @copyright 2020 by Joachim Jensen
*/
defined('ABSPATH') || exit;
if (!class_exists('WPCACore')) {
$domain = explode('/', plugin_basename(__FILE__));
define('WPCA_DOMAIN', $domain[0]);
define('WPCA_PATH', plugin_dir_path(__FILE__));
/**
* Core for WordPress Content Aware Engine
*/
final class WPCACore
{
/**
* Using class prefix instead of namespace
* for PHP5.2 compatibility
*/
const CLASS_PREFIX = 'WPCA';
/**
* Prefix for data (keys) stored in database
*/
const PREFIX = '_ca_';
/**
* Post Type for condition groups
*/
const TYPE_CONDITION_GROUP = 'condition_group';
/**
* Post Statuses for condition groups
*/
/**
* @deprecated
*/
const STATUS_NEGATED = 'negated';
/**
* @deprecated
*/
const STATUS_PUBLISHED = 'publish';
const STATUS_OR = 'wpca_or';
const STATUS_EXCEPT = 'wpca_except';
/**
* Exposures for condition groups
*/
const EXP_SINGULAR = 0;
const EXP_SINGULAR_ARCHIVE = 1;
const EXP_ARCHIVE = 2;
/**
* @deprecated 7.0
*/
const CAPABILITY = 'edit_theme_options';
/**
* Name for generated nonces
*/
const NONCE = '_ca_nonce';
const OPTION_CONDITION_TYPE_CACHE = '_ca_condition_type_cache';
const OPTION_POST_TYPE_OPTIONS = '_ca_post_type_options';
/**
* Post Types that use the engine
* @var WPCAPostTypeManager
*/
private static $type_manager;
/**
* Conditions retrieved from database
* @var array
*/
private static $condition_cache = array();
/**
* Objects retrieved from database
* @var array
*/
private static $post_cache = array();
/**
* Constructor
*/
public static function init()
{
spl_autoload_register(array(__CLASS__,'_autoload_class_files'));
if (is_admin()) {
add_action(
'admin_enqueue_scripts',
array(__CLASS__,'add_group_script_styles'),
9
);
add_action(
'delete_post',
array(__CLASS__,'sync_group_deletion')
);
add_action(
'trashed_post',
array(__CLASS__,'sync_group_trashed')
);
add_action(
'untrashed_post',
array(__CLASS__,'sync_group_untrashed')
);
add_action(
'add_meta_boxes',
array(__CLASS__,'add_group_meta_box'),
10,
2
);
add_action(
'wpca/modules/save-data',
array(__CLASS__,'save_condition_options'),
10,
3
);
add_action(
'wp_ajax_wpca/add-rule',
array(__CLASS__,'ajax_update_group')
);
}
add_action(
'init',
array(__CLASS__,'add_group_post_type'),
99
);
add_action(
'init',
array(__CLASS__,'schedule_cache_condition_types'),
99
);
add_action(
'wpca/cache_condition_types',
array(__CLASS__,'cache_condition_types'),
999
);
}
/**
* Get post type manager
*
* @deprecated 4.0
* @since 1.0
* @return WPCAPostTypeManager
*/
public static function post_types()
{
return self::types();
}
/**
* Get type manager
*
* @since 4.0
* @return WPCAPostTypeManager
*/
public static function types()
{
if (!isset(self::$type_manager)) {
self::$type_manager = new WPCATypeManager();
}
return self::$type_manager;
}
/**
* @since 8.0
*
* @return void
*/
public static function schedule_cache_condition_types()
{
if (wp_next_scheduled('wpca/cache_condition_types') !== false) {
return;
}
wp_schedule_event(get_gmt_from_date('today 02:00:00', 'U'), 'daily', 'wpca/cache_condition_types');
}
/**
* Cache condition types currently in use
*
* @since 8.0
*
* @return void
*/
public static function cache_condition_types()
{
$all_modules = array();
$modules_by_type = array();
$ignored_modules = array('taxonomy' => 1);
$cache = array();
$types = self::types();
foreach ($types as $type => $modules) {
$modules_by_type[$type] = array();
$cache[$type] = array();
foreach ($modules as $module) {
if (isset($ignored_modules[$module->get_id()])) {
continue;
}
$modules_by_type[$type][$module->get_data_key()] = $module->get_id();
$all_modules[$module->get_data_key()] = $module->get_data_key();
}
}
if (!$all_modules) {
update_option(self::OPTION_CONDITION_TYPE_CACHE, array());
return;
}
global $wpdb;
$query = '
SELECT p.post_type, m.meta_key
FROM '.$wpdb->posts.' p
INNER JOIN '.$wpdb->posts.' c ON c.post_parent = p.ID
INNER JOIN '.$wpdb->postmeta.' m ON m.post_id = c.ID
WHERE p.post_type IN ('.self::sql_prepare_in(array_keys($modules_by_type)).')
AND m.meta_key IN ('.self::sql_prepare_in($all_modules).')
GROUP BY p.post_type, m.meta_key
';
$results = (array) $wpdb->get_results($query);
foreach ($results as $result) {
if (isset($modules_by_type[$result->post_type][$result->meta_key])) {
$cache[$result->post_type][] = $modules_by_type[$result->post_type][$result->meta_key];
}
}
update_option(self::OPTION_CONDITION_TYPE_CACHE, $cache);
}
/**
* Register group post type
*
* @since 1.0
* @return void
*/
public static function add_group_post_type()
{
//This is just a safety placeholder,
//authorization will be done with parent object's cap
$capability = 'edit_theme_options';
$capabilities = array(
'edit_post' => $capability,
'read_post' => $capability,
'delete_post' => $capability,
'edit_posts' => $capability,
'delete_posts' => $capability,
'edit_others_posts' => $capability,
'publish_posts' => $capability,
'read_private_posts' => $capability
);
register_post_type(self::TYPE_CONDITION_GROUP, array(
'labels' => array(
'name' => __('Condition Groups', WPCA_DOMAIN),
'singular_name' => __('Condition Group', WPCA_DOMAIN),
),
'capabilities' => $capabilities,
'public' => false,
'hierarchical' => false,
'exclude_from_search' => true,
'publicly_queryable' => false,
'show_ui' => false,
'show_in_menu' => false,
'show_in_nav_menus' => false,
'show_in_admin_bar' => false,
'has_archive' => false,
'rewrite' => false,
'query_var' => false,
'supports' => array('author'),
'can_export' => false,
'delete_with_user' => false
));
register_post_status(self::STATUS_NEGATED, array(
'label' => _x('Negated', 'condition status', WPCA_DOMAIN),
'public' => false,
'exclude_from_search' => true,
'show_in_admin_all_list' => false,
'show_in_admin_status_list' => false,
));
register_post_status(self::STATUS_EXCEPT, array(
'label' => _x('Exception', 'condition status', WPCA_DOMAIN),
'public' => false,
'exclude_from_search' => true,
'show_in_admin_all_list' => false,
'show_in_admin_status_list' => false,
));
register_post_status(self::STATUS_OR, array(
'label' => _x('Or', 'condition status', WPCA_DOMAIN),
'public' => false,
'exclude_from_search' => true,
'show_in_admin_all_list' => false,
'show_in_admin_status_list' => false,
));
}
/**
* Get group IDs by their parent ID
*
* @since 1.0
* @param int $parent_id
* @return array
*/
private static function get_group_ids_by_parent($parent_id)
{
if (!self::types()->has(get_post_type($parent_id))) {
return array();
}
global $wpdb;
return (array)$wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = '%d'", $parent_id));
}
/**
* Delete groups from database when their parent is deleted
*
* @since 1.0
* @param int $post_id
* @return void
*/
public static function sync_group_deletion($post_id)
{
$groups = self::get_group_ids_by_parent($post_id);
if ($groups) {
foreach ($groups as $group_id) {
//Takes care of metadata and terms too
wp_delete_post($group_id, true);
}
}
}
/**
* Trash groups when their parent is trashed
*
* @since 1.0
* @param int $post_id
* @return void
*/
public static function sync_group_trashed($post_id)
{
$groups = self::get_group_ids_by_parent($post_id);
if ($groups) {
foreach ($groups as $group_id) {
wp_trash_post($group_id);
}
}
}
/**
* Untrash groups when their parent is untrashed
*
* @since 1.0
* @param int $post_id
* @return void
*/
public static function sync_group_untrashed($post_id)
{
$groups = self::get_group_ids_by_parent($post_id);
if ($groups) {
foreach ($groups as $group_id) {
wp_untrash_post($group_id);
}
}
}
private static $wp_query_original = array();
/**
* Get filtered condition groups
*
* @since 2.0
* @return array
*/
public static function get_conditions($post_type)
{
global $wpdb, $wp_query, $post;
if ((!$wp_query->query && !$post) || is_admin()) {
return array();
}
// Return cache if present
if (isset(self::$condition_cache[$post_type])) {
return self::$condition_cache[$post_type];
}
$excluded = array();
$where = array();
$join = array();
$cache = array(
$post_type
);
$modules = self::types()->get($post_type)->get_all();
$modules = self::filter_condition_type_cache($post_type, $modules);
//avoid combining as long as negated conditions are being deprecated
// foreach (self::types() as $other_type => $other_modules) {
// if ($other_type == $post_type) {
// continue;
// }
// if (self::filter_condition_type_cache($other_type, $other_modules->get_all()) === $modules) {
// $cache[] = $other_type;
// }
// }
self::fix_wp_query();
foreach ($modules as $module) {
$id = $module->get_id();
$name = $module->get_query_name();
if (apply_filters("wpca/module/$id/in-context", $module->in_context())) {
$join[$id] = apply_filters("wpca/module/$id/db-join", $module->db_join());
$data = $module->get_context_data();
if (is_array($data)) {
$data = "($name.meta_value IS NULL OR $name.meta_value IN ('".implode("','", $data) ."'))";
}
$where[$id] = apply_filters("wpca/module/$id/db-where", $data);
} else {
$excluded[] = $module;
}
}
$use_negated_conditions = self::get_option($post_type, 'legacy.negated_conditions', false);
// Check if there are any conditions for current content
$groups_in_context = array();
if (!empty($where)) {
$post_status = array(
self::STATUS_PUBLISHED,
self::STATUS_OR,
self::STATUS_EXCEPT
);
if ($use_negated_conditions) {
$post_status[] = self::STATUS_NEGATED;
}
if (defined('CAS_SQL_CHUNK_SIZE') && CAS_SQL_CHUNK_SIZE > 0) {
$chunk_size = CAS_SQL_CHUNK_SIZE;
} else {
//Syntax changed in MySQL 5.5 and MariaDB 10.0 (reports as version 5.5)
$wpdb->query('SET'.(version_compare($wpdb->db_version(), '5.5', '>=') ? ' SESSION' : ' OPTION').' SQL_BIG_SELECTS = 1');
$chunk_size = count($join);
}
$joins = array_chunk($join, $chunk_size);
$joins_max = count($joins) - 1;
$wheres = array_chunk($where, $chunk_size);
$group_ids = array();
$groups_in_context = array();
$where2 = array();
$where2[] = "p.post_type = '".self::TYPE_CONDITION_GROUP."'";
$where2[] = "p.post_status IN ('".implode("','", $post_status)."')";
//exposure
$where2[] = 'p.menu_order '.(is_archive() || is_home() ? '>=' : '<=').' 1';
foreach ($joins as $i => $join) {
if ($i == $joins_max) {
$groups_in_context = $wpdb->get_results(
'SELECT p.ID, p.post_parent, p.post_status '.
"FROM $wpdb->posts p ".
implode(' ', $join).'
WHERE
'.implode(' AND ', $wheres[$i]).'
AND '.implode(' AND ', $where2).
(!empty($group_ids) ? ' AND p.id IN ('.implode(',', $group_ids).')' : ''),
OBJECT_K
);
break;
}
$group_ids = array_merge($group_ids, $wpdb->get_col(
'SELECT p.ID '.
"FROM $wpdb->posts p ".
implode(' ', $join).'
WHERE
'.implode(' AND ', $wheres[$i]).'
AND '.implode(' AND ', $where2)
));
}
}
$groups_negated = array();
if ($use_negated_conditions) {
$groups_negated = $wpdb->get_results($wpdb->prepare(
'SELECT p.ID, p.post_parent '.
"FROM $wpdb->posts p ".
"WHERE p.post_type = '%s' ".
"AND p.post_status = '%s' ",
self::TYPE_CONDITION_GROUP,
self::STATUS_NEGATED
), OBJECT_K);
}
if (!empty($groups_in_context) || !empty($groups_negated)) {
//Force update of meta cache to prevent lazy loading
update_meta_cache('post', array_keys($groups_in_context + $groups_negated));
}
//condition group => type
$valid = array();
foreach ($groups_in_context as $group) {
$valid[$group->ID] = $group->post_parent;
}
//Exclude types that have unrelated content in same group
foreach ($excluded as $module) {
$valid = $module->filter_excluded_context($valid);
}
//exclude exceptions
$excepted = array();
foreach ($valid as $group_id => $parent_id) {
//sanity
if (!isset($groups_in_context[$group_id])) {
continue;
}
if ($groups_in_context[$group_id]->post_status == self::STATUS_EXCEPT) {
$excepted[$parent_id] = 1;
}
}
foreach ($valid as $group_id => $parent_id) {
if (isset($excepted[$parent_id])) {
unset($valid[$group_id]);
}
}
if ($use_negated_conditions) {
//Filter negated groups
//type => group
$handled_already = array_flip($valid);
foreach ($groups_negated as $group) {
if (isset($valid[$group->ID])) {
unset($valid[$group->ID]);
} else {
$valid[$group->ID] = $group->post_parent;
}
if (isset($handled_already[$group->post_parent])) {
unset($valid[$group->ID]);
}
$handled_already[$group->post_parent] = 1;
}
}
self::restore_wp_query();
foreach ($cache as $cache_type) {
self::$condition_cache[$cache_type] = $valid;
}
return self::$condition_cache[$post_type];
}
/**
* Get filtered posts from a post type
*
* @since 1.0
* @global type $wpdb
* @global WP_Query $wp_query
* @global WP_Post $post
* @return array
*/
public static function get_posts($post_type)
{
global $wp_query, $post;
// Return cache if present
if (isset(self::$post_cache[$post_type])) {
return self::$post_cache[$post_type];
}
if (!self::types()->has($post_type) || (!$wp_query->query && !$post) || is_admin()) {
return false;
}
$valid = self::get_conditions($post_type);
self::$post_cache[$post_type] = array();
$results = array();
if (!empty($valid)) {
$data = new WP_Query(array(
'post__in' => array_values($valid),
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => -1,
'ignore_sticky_posts' => true,
'update_post_term_cache' => false,
'update_post_meta_cache' => true,
'suppress_filters' => true,
'no_found_rows' => true,
'orderby' => 'none'
));
$results = array_merge($results, $data->posts);
}
//legacy sorting
uasort($results, function (WP_Post $post_a, WP_Post $post_b) {
//asc
if ($post_a->menu_order != $post_b->menu_order) {
return $post_a->menu_order < $post_b->menu_order ? -1 : 1;
}
$post_a_handle = get_post_meta($post_a->ID, '_ca_handle', true);
$post_b_handle = get_post_meta($post_b->ID, '_ca_handle', true);
//desc
if ($post_a_handle != $post_b_handle) {
return $post_a_handle > $post_b_handle ? -1 : 1;
}
//desc
if ($post_a->post_date != $post_b->post_date) {
return $post_a->post_date > $post_b->post_date ? -1 : 1;
}
return 0;
});
$results = array_reduce($results, function ($carry, $post) {
$carry[$post->ID] = (object)array(
'ID' => $post->ID,
'post_type' => $post->post_type,
'handle' => get_post_meta($post->ID, '_ca_handle', true),
'menu_order' => $post->menu_order,
'post_date' => $post->post_date
);
return $carry;
}, array());
self::$post_cache[$post_type] = apply_filters("wpca/posts/{$post_type}", $results);
return self::$post_cache[$post_type];
}
/**
* Add meta box to manage condition groups
*
* @since 1.0
* @param string $post_type
* @param WP_Post $post
*/
public static function add_group_meta_box($post_type, $post)
{
if(is_null($post)) {
return;
}
self::render_group_meta_box($post, $post_type, 'normal', 'default');
}
public static function render_group_meta_box($post, $screen, $context = 'normal', $priority = 'default')
{
if (is_null($post) || !self::types()->has($post->post_type)) {
return;
}
$post_type_obj = get_post_type_object($post->post_type);
if (!current_user_can($post_type_obj->cap->edit_post, $post->ID)) {
return;
}
$template = WPCAView::make('condition_options', array(
'post_type' => $post->post_type
));
add_action('wpca/group/settings', array($template,'render'), -1, 2);
$template = WPCAView::make('group_template', array(
'post_type' => $post->post_type
));
add_action('admin_footer', array($template,'render'));
$template = WPCAView::make('condition_template');
add_action('admin_footer', array($template,'render'));
$view = WPCAView::make('meta_box', array(
'post_type' => $post->post_type,
'nonce' => wp_nonce_field(self::PREFIX.$post->ID, self::NONCE, true, false),
));
$title = isset($post_type_obj->labels->ca_title) ? $post_type_obj->labels->ca_title : __('Conditional Logic', WPCA_DOMAIN);
add_meta_box(
'cas-rules',
$title,
array($view,'render'),
$screen,
$context,
$priority
);
}
/**
* Insert new condition group for a post type
* Uses current post per default
*
* @since 1.0
* @param WP_Post|int $post
* @return int
*/
public static function add_condition_group($post_id = null)
{
$post = get_post($post_id);
//Make sure to go from auto-draft to draft
if ($post->post_status == 'auto-draft') {
wp_update_post(array(
'ID' => $post->ID,
'post_title' => '',
'post_status' => 'draft'
));
}
return wp_insert_post(array(
'post_status' => self::STATUS_OR,
'menu_order' => self::EXP_SINGULAR_ARCHIVE,
'post_type' => self::TYPE_CONDITION_GROUP,
'post_author' => $post->post_author,
'post_parent' => $post->ID,
));
}
/**
* Get condition groups for a post type
* Uses current post per default
*
* @since 1.0
* @param WP_Post|int $post_id
* @return array
*/
private static function get_condition_groups($post_id = null)
{
$post = get_post($post_id);
$groups = array();
if ($post) {
$groups = get_posts(array(
'posts_per_page' => -1,
'post_type' => self::TYPE_CONDITION_GROUP,
'post_parent' => $post->ID,
'post_status' => array(self::STATUS_PUBLISHED,self::STATUS_NEGATED,self::STATUS_EXCEPT, self::STATUS_OR),
'order' => 'DESC',
'orderby' => 'post_status'
));
}
return $groups;
}
/**
* AJAX callback to update a condition group
*
* @since 1.0
* @return void
*/
public static function ajax_update_group()
{
if (!isset($_POST['current_id']) ||
!check_ajax_referer(self::PREFIX.$_POST['current_id'], 'token', false)) {
wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
}
$parent_id = (int)$_POST['current_id'];
$parent_type = get_post_type_object($_POST['post_type']);
if (! current_user_can($parent_type->cap->edit_post, $parent_id)) {
wp_send_json_error(__('Unauthorized request', WPCA_DOMAIN), 403);
}
$response = array(
'message' => __('Conditions updated', WPCA_DOMAIN)
);
//Make sure some rules are sent
if (!isset($_POST['conditions'])) {
//Otherwise we delete group
if ($_POST['id'] && wp_delete_post(intval($_POST['id']), true) === false) {
wp_send_json_error(__('Could not delete conditions', WPCA_DOMAIN), 500);
}
$response['removed'] = true;
wp_send_json($response);
}
//If ID was not sent at this point, it is a new group
if (!$_POST['id']) {
$post_id = self::add_condition_group($parent_id);
$response['new_post_id'] = $post_id;
} else {
$post_id = (int)$_POST['id'];
}
wp_update_post(array(
'ID' => $post_id,
'post_status' => self::sanitize_status($_POST['status']),
'menu_order' => (int)$_POST['exposure']
));
//Prune condition type cache, will rebuild within 24h
update_option(self::OPTION_CONDITION_TYPE_CACHE, array());
foreach (self::types()->get($parent_type->name)->get_all() as $module) {
//send $_POST here
$module->save_data($post_id);
}
do_action('wpca/modules/save-data', $post_id, $parent_type->name);
wp_send_json($response);
}
/**
* @param string $status
*
* @return string
*/
private static function sanitize_status($status)
{
switch ($status) {
case self::STATUS_NEGATED:
return self::STATUS_NEGATED;
case self::STATUS_EXCEPT:
return self::STATUS_EXCEPT;
case self::STATUS_OR:
case self::STATUS_PUBLISHED:
default:
return self::STATUS_OR;
}
}
/**
* Save registered meta for condition group
*
* @since 3.2
* @param int $group_id
* @return void
*/
public static function save_condition_options($group_id, $post_type)
{
$meta_keys = self::get_condition_meta_keys($post_type);
foreach ($meta_keys as $key => $default_value) {
$value = isset($_POST[$key]) ? $_POST[$key] : false;
if ($value) {
update_post_meta($group_id, $key, $value);
} elseif (get_post_meta($group_id, $key, true)) {
delete_post_meta($group_id, $key);
}
}
}
public static function add_group_script_styles($hook)
{
$current_screen = get_current_screen();
wp_register_style(
self::PREFIX.'condition-groups',
plugins_url('/assets/css/condition_groups.css', __FILE__),
array(),
WPCA_VERSION
);
if (self::types()->has($current_screen->post_type) && $current_screen->base == 'post') {
self::enqueue_scripts_styles($current_screen->post_type);
}
}
/**
* Get condition option defaults
*
* @since 3.2
* @param string $post_type
* @return array
*/
public static function get_condition_meta_keys($post_type)
{
$group_meta = array(
'_ca_autoselect' => 0
);
return apply_filters('wpca/condition/meta', $group_meta, $post_type);
}
/**
* Register and enqueue scripts and styles
* for post edit screen
*
* @since 1.0
* @param string $hook
* @return void
*/
public static function enqueue_scripts_styles($post_type = '')
{
$post_type = empty($post_type) ? get_post_type() : $post_type;
$group_meta = self::get_condition_meta_keys($post_type);
$groups = self::get_condition_groups();
$data = array();
$i = 0;
foreach ($groups as $group) {
$data[$i] = array(
'id' => $group->ID,
'status' => $group->post_status,
'exposure' => $group->menu_order,
'conditions' => array()
);
foreach (self::types()->get($post_type)->get_all() as $module) {
$data[$i]['conditions'] = $module->get_group_data($data[$i]['conditions'], $group->ID);
}
foreach ($group_meta as $meta_key => $default_value) {
$value = get_post_meta($group->ID, $meta_key, true);
if ($value === false) {
$value = $default_value;
}
$data[$i][$meta_key] = $value;
}
$i++;
}
$conditions = array(
'general' => array(
'text' => __('General'),
'children' => array()
),
'post_type' => array(
'text' => __('Post Types'),
'children' => array()
),
'taxonomy' => array(
'text' => __('Taxonomies'),
'children' => array()
),
'plugins' => array(
'text' => __('Plugins'),
'children' => array()
)
);
foreach (self::types()->get($post_type)->get_all() as $module) {
$category = $module->get_category();
if (!isset($conditions[$category])) {
$category = 'general';
}
//array_values used for backwards compatibility
$conditions[$category]['children'] = array_values($module->list_module($conditions[$category]['children']));
}
foreach ($conditions as $key => $condition) {
if (empty($condition['children'])) {
unset($conditions[$key]);
}
}
//Make sure to use packaged version
if (wp_script_is('select2', 'registered')) {
wp_deregister_script('select2');
wp_deregister_style('select2');
}
$plugins_url = plugins_url('', __FILE__);
//Add to head to take priority
//if being added under other name
wp_register_script(
'select2',
$plugins_url . '/assets/js/select2.min.js',
array('jquery'),
'4.0.3',
false
);
wp_register_script(
'backbone.trackit',
$plugins_url . '/assets/js/backbone.trackit.min.js',
array('backbone'),
'0.1.0',
true
);
wp_register_script(
'backbone.epoxy',
$plugins_url . '/assets/js/backbone.epoxy.min.js',
array('backbone'),
'1.3.3',
true
);
wp_register_script(
self::PREFIX.'condition-groups',
$plugins_url . '/assets/js/condition_groups.min.js',
array('jquery','select2','backbone.trackit','backbone.epoxy'),
WPCA_VERSION,
true
);
wp_enqueue_script(self::PREFIX.'condition-groups');
wp_localize_script(self::PREFIX.'condition-groups', 'WPCA', array(
'searching' => __('Searching', WPCA_DOMAIN),
'noResults' => __('No results found.', WPCA_DOMAIN),
'loadingMore' => __('Loading more results', WPCA_DOMAIN),
'unsaved' => __('Conditions have unsaved changes. Do you want to continue and discard these changes?', WPCA_DOMAIN),
'newGroup' => __('New condition group', WPCA_DOMAIN),
'newCondition' => __('Meet ALL of these conditions', WPCA_DOMAIN),
'conditions' => array_values($conditions),
'groups' => $data,
'meta_default' => $group_meta,
'post_type' => $post_type,
'text_direction' => is_rtl() ? 'rtl' : 'ltr',
'condition_not' => __('Not', WPCA_DOMAIN),
'condition_or' => __('Or', WPCA_DOMAIN),
'condition_except' => __('Except', WPCA_DOMAIN)
));
wp_enqueue_style(self::PREFIX.'condition-groups');
//@todo remove when ultimate member includes fix
wp_dequeue_style('um_styles');
//@todo remove when events calendar pro plugin includes fix
wp_dequeue_script('tribe-select2');
}
/**
* Modify wp_query for plugin compatibility
*
* @since 5.0
* @return void
*/
private static function fix_wp_query()
{
$query = array();
//When themes don't declare WooCommerce support,
//conditionals are not working properly for Shop
if (defined('WOOCOMMERCE_VERSION') && function_exists('is_shop') && is_shop() && !is_post_type_archive('product')) {
$query = array(
'is_archive' => true,
'is_post_type_archive' => true,
'is_page' => false,
'is_singular' => false,
'query_vars' => array(
'post_type' => 'product'
)
);
}
self::set_wp_query($query);
}
/**
* Restore original wp_query
*
* @since 5.0
* @return void
*/
private static function restore_wp_query()
{
self::set_wp_query(self::$wp_query_original);
self::$wp_query_original = array();
}
/**
* Set properties in wp_query and save original value
*
* @since 5.0
* @param array $query
*/
private static function set_wp_query($query)
{
global $wp_query;
foreach ($query as $key => $val) {
$is_array = is_array($val);
if (!isset(self::$wp_query_original[$key])) {
self::$wp_query_original[$key] = $is_array ? array() : $wp_query->$key;
}
if ($is_array) {
foreach ($val as $k1 => $v1) {
if (!isset(self::$wp_query_original[$key][$k1])) {
self::$wp_query_original[$key][$k1] = $wp_query->{$key}[$k1];
}
$wp_query->{$key}[$k1] = $v1;
}
} else {
$wp_query->$key = $val;
}
}
}
/**
* Autoload class files
*
* @since 1.0
* @param string $class
* @return void
*/
private static function _autoload_class_files($class)
{
if (strpos($class, self::CLASS_PREFIX) === 0) {
$class = str_replace(self::CLASS_PREFIX, '', $class);
$class = self::str_replace_first('_', '/', $class);
$class = strtolower($class);
$file = WPCA_PATH . $class . '.php';
if (file_exists($file)) {
include($file);
}
}
}
/**
* Helper function to replace first
* occurence of substring
*
* @since 1.0
* @param string $search
* @param string $replace
* @param string $subject
* @return string
*/
private static function str_replace_first($search, $replace, $subject)
{
$pos = strpos($subject, $search);
if ($pos !== false) {
$subject = substr_replace($subject, $replace, $pos, strlen($search));
}
return $subject;
}
/**
* @since 8.0
* @param array $input
*
* @return string
*/
private static function sql_prepare_in($input)
{
$output = array_map(function ($value) {
return "'" . esc_sql($value) . "'";
}, $input);
return implode(',', $output);
}
/**
* @since 8.0
* @param string $type
* @param array $modules
*
* @return array
*/
private static function filter_condition_type_cache($type, $modules)
{
$included_conditions = get_option(self::OPTION_CONDITION_TYPE_CACHE, array());
if (!$included_conditions || !isset($included_conditions[$type])) {
return $modules;
}
$ignored_modules = array('taxonomy' => 1);
$included_conditions_lookup = array_flip($included_conditions[$type]);
$filtered_modules = array();
foreach ($modules as $module) {
if (isset($ignored_modules[$module->get_id()]) || isset($included_conditions_lookup[$module->get_id()])) {
$filtered_modules[] = $module;
}
}
return $filtered_modules;
}
/**
* @param string $post_type
* @param string $name
* @param mixed|null $default_value
*
* @return mixed|null
*/
public static function get_option($post_type, $name, $default_value = null)
{
if (!self::types()->has($post_type)) {
return $default_value;
}
$value = get_option(self::OPTION_POST_TYPE_OPTIONS, array());
$levels = explode('.', $post_type.'.'.$name);
foreach ($levels as $option_level) {
if (!is_array($value) || !isset($value[$option_level])) {
return $default_value;
}
$value = $value[$option_level];
}
return $value;
}
/**
* @param string $post_type
* @param string $name
* @param mixed $value
*
* @return bool
*/
public static function save_option($post_type, $name, $value)
{
if (!self::types()->has($post_type)) {
return false;
}
$options = get_option(self::OPTION_POST_TYPE_OPTIONS, array());
$keys = explode('.', $post_type.'.'.$name);
$array = &$options;
foreach ($keys as $key) {
if (!isset($array[$key]) || !is_array($array[$key])) {
$array[$key] = array();
}
$array = &$array[$key];
}
$array = $value;
return update_option(self::OPTION_POST_TYPE_OPTIONS, $options);
}
}
}