1252 lines
43 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|