Files

504 lines
16 KiB
PHP

<?php
/**
* @package WP Content Aware Engine
* @author Joachim Jensen <joachim@dev.institute>
* @license GPLv3
* @copyright 2020 by Joachim Jensen
*/
defined('ABSPATH') || exit;
/**
*
* Taxonomy Module
*
* Detects if current content has/is:
* a) any term of specific taxonomy or specific term
* b) taxonomy archive or specific term archive
*
*/
class WPCAModule_taxonomy extends WPCAModule_Base
{
/**
* @var string
*/
protected $category = 'taxonomy';
/**
* Registered public taxonomies
*
* @var array
*/
private $taxonomy_objects = array();
/**
* Terms of a given singular
*
* @var array
*/
private $post_terms;
/**
* Taxonomies for a given singular
* @var array
*/
private $post_taxonomies;
/**
* Constructor
*/
public function __construct()
{
parent::__construct('taxonomy', __('Taxonomies', WPCA_DOMAIN));
$this->query_name = 'ct';
}
public function initiate()
{
parent::initiate();
add_action(
'created_term',
array($this,'term_ancestry_check'),
10,
3
);
if (is_admin()) {
foreach ($this->_get_taxonomies() as $taxonomy) {
add_action(
'wp_ajax_wpca/module/'.$this->id.'-'.$taxonomy->name,
array($this,'ajax_print_content')
);
}
}
}
/**
* Determine if content is relevant
*
* @since 1.0
* @return boolean
*/
public function in_context()
{
if (is_singular()) {
$tax = $this->_get_taxonomies();
$this->post_terms = array();
$this->post_taxonomies = array();
// Check if content has any taxonomies supported
foreach (get_object_taxonomies(get_post_type()) as $taxonomy) {
//Only want taxonomies selectable in admin
if (isset($tax[$taxonomy])) {
//Check term caches, Core most likely used it
$terms = get_object_term_cache(get_the_ID(), $taxonomy);
if ($terms === false) {
$terms = wp_get_object_terms(get_the_ID(), $taxonomy);
}
if ($terms) {
$this->post_taxonomies[] = $taxonomy;
$this->post_terms = array_merge($this->post_terms, $terms);
}
}
}
return !!$this->post_terms;
}
return is_tax() || is_category() || is_tag();
}
/**
* Remove posts if they have data from
* other contexts (meaning conditions arent met)
*
* @since 3.2
* @param array $posts
* @return array
*/
public function filter_excluded_context($posts)
{
$posts = parent::filter_excluded_context($posts);
if ($posts) {
global $wpdb;
$obj_w_tags = $wpdb->get_col("SELECT object_id FROM $wpdb->term_relationships WHERE object_id IN (".implode(',', array_keys($posts)).') GROUP BY object_id');
if ($obj_w_tags) {
$posts = array_diff_key($posts, array_flip($obj_w_tags));
}
}
return $posts;
}
/**
* Query join
*
* @since 1.0
* @return string
*/
public function db_join()
{
global $wpdb;
$joins = parent::db_join();
$joins .= "LEFT JOIN $wpdb->term_relationships term ON term.object_id = p.ID ";
return $joins;
}
/**
* Get data from context
*
* @since 1.0
* @return array
*/
public function get_context_data()
{
$name = $this->get_query_name();
//In more recent WP versions, term_id = term_tax_id
//but term_tax_id has always been unique
if (is_singular()) {
$terms = array();
foreach ($this->post_terms as $term) {
$terms[] = $term->term_taxonomy_id;
}
return '(term.term_taxonomy_id IS NULL OR term.term_taxonomy_id IN ('.implode(',', $terms).")) AND ($name.meta_value IS NULL OR $name.meta_value IN('".implode("','", $this->post_taxonomies)."'))";
}
$term = get_queried_object();
return "(term.term_taxonomy_id IS NULL OR term.term_taxonomy_id = '".$term->term_taxonomy_id."') AND ($name.meta_value IS NULL OR $name.meta_value = '".$term->taxonomy."')";
}
/**
* Get content for sidebar editor
*
* @since 1.0
* @param array $args
* @return array
*/
protected function _get_content($args = array())
{
$total_items = wp_count_terms($args['taxonomy'], array(
'hide_empty' => $args['hide_empty']
));
$start = $args['offset'];
$end = $start + $args['number'];
$walk_tree = false;
$retval = array();
if ($total_items) {
$taxonomy = get_taxonomy($args['taxonomy']);
if ($taxonomy->hierarchical && !$args['search'] && !$args['include']) {
$args['number'] = 0;
$args['offset'] = 0;
$walk_tree = true;
}
$terms = new WP_Term_Query($args);
if ($walk_tree) {
$sorted_terms = array();
foreach ($terms->terms as $term) {
$sorted_terms[$term->parent][] = $term;
}
$i = 0;
$this->_walk_tree($sorted_terms, $sorted_terms[0], $i, $start, $end, 0, $retval);
} else {
//Hierarchical taxonomies use ids instead of slugs
//see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
$value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
foreach ($terms->terms as $term) {
//term names are encoded
$retval[$term->$value_var] = htmlspecialchars_decode($term->name);
}
}
}
return $retval;
}
/**
* Get hierarchical content with level param
*
* @since 3.7.2
* @param array $all_terms
* @param array $terms
* @param int $i
* @param int $start
* @param int $end
* @param int $level
* @param array &$retval
* @return void
*/
protected function _walk_tree($all_terms, $terms, &$i, $start, $end, $level, &$retval)
{
foreach ($terms as $term) {
if ($i >= $end) {
break;
}
if ($i >= $start) {
$retval[] = array(
'id' => $term->term_id,
'text' => htmlspecialchars_decode($term->name),
'level' => $level
);
}
$i++;
if (isset($all_terms[$term->term_id])) {
$this->_walk_tree($all_terms, $all_terms[$term->term_id], $i, $start, $end, $level + 1, $retval);
}
}
}
/**
* Get registered public taxonomies
*
* @since 1.0
* @return array
*/
protected function _get_taxonomies()
{
// List public taxonomies
if (empty($this->taxonomy_objects)) {
foreach (get_taxonomies(array('public' => true), 'objects') as $tax) {
$this->taxonomy_objects[$tax->name] = $tax;
}
if (defined('POLYLANG_VERSION')) {
unset($this->taxonomy_objects['language']);
}
}
return $this->taxonomy_objects;
}
/**
* Get data for condition group
*
* @since 2.0
* @param array $group_data
* @param int $post_id
* @return array
*/
public function get_group_data($group_data, $post_id)
{
$ids = array_flip((array)get_post_custom_values(WPCACore::PREFIX . $this->id, $post_id));
//Fetch all terms and group by tax to prevent lazy loading
$terms = wp_get_object_terms(
$post_id,
array_keys($this->_get_taxonomies())
// array(
// 'update_term_meta_cache' => false
// )
);
$terms_by_tax = array();
foreach ($terms as $term) {
$terms_by_tax[$term->taxonomy][] = $term;
}
$title_count = $this->get_title_count();
foreach ($this->_get_taxonomies() as $taxonomy) {
$posts = isset($terms_by_tax[$taxonomy->name]) ? $terms_by_tax[$taxonomy->name] : 0;
if ($posts || isset($ids[$taxonomy->name])) {
$group_data[$this->id.'-'.$taxonomy->name] = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
$group_data[$this->id.'-'.$taxonomy->name]['label'] = $group_data[$this->id.'-'.$taxonomy->name]['text'];
if ($posts) {
$retval = array();
//Hierarchical taxonomies use ids instead of slugs
//see http://codex.wordpress.org/Function_Reference/wp_set_post_objects
$value_var = ($taxonomy->hierarchical ? 'term_id' : 'slug');
foreach ($posts as $post) {
$retval[$post->$value_var] = $post->name;
}
$group_data[$this->id.'-'.$taxonomy->name]['data'] = $retval;
}
}
}
return $group_data;
}
/**
* Count taxonomy labels to find shared ones
*
* @return array
*/
protected function get_title_count()
{
$title_count = array();
foreach ($this->_get_taxonomies() as $taxonomy) {
if (!isset($title_count[$taxonomy->label])) {
$title_count[$taxonomy->label] = 0;
}
$title_count[$taxonomy->label]++;
}
return $title_count;
}
protected function get_list_data($taxonomy, $title_count)
{
$placeholder = '/'.sprintf(__('%s Archives', WPCA_DOMAIN), $taxonomy->labels->singular_name);
$placeholder = $taxonomy->labels->all_items.$placeholder;
$label = $taxonomy->label;
if (count($taxonomy->object_type) === 1 && $title_count > 1) {
$post_type = get_post_type_object($taxonomy->object_type[0]);
$label .= ' (' . $post_type->label . ')';
}
return array(
'text' => $label,
'placeholder' => $placeholder,
'default_value' => $taxonomy->name
);
}
/**
* @since 2.0
* @param array $list
*
* @return array
*/
public function list_module($list)
{
$title_count = $this->get_title_count();
foreach ($this->_get_taxonomies() as $taxonomy) {
$data = $this->get_list_data($taxonomy, $title_count[$taxonomy->label]);
$data['id'] = $this->id.'-'.$taxonomy->name;
$list[] = $data;
}
return $list;
}
/**
* @param array $args
*
* @return array
*/
protected function parse_query_args($args)
{
if (isset($args['item_object'])) {
preg_match('/taxonomy-(.+)$/i', $args['item_object'], $matches);
$args['item_object'] = isset($matches[1]) ? $matches[1] : '___';
$taxonomy_name = $args['item_object'];
} else {
$taxonomy_name = 'category';
}
return array(
'include' => $args['include'],
'taxonomy' => $taxonomy_name,
'number' => $args['limit'],
'offset' => ($args['paged'] - 1) * $args['limit'],
'orderby' => 'name',
'order' => 'ASC',
'search' => $args['search'],
'hide_empty' => false,
'update_term_meta_cache' => false
);
}
/**
* Save data on POST
*
* @since 1.0
* @param int $post_id
* @return void
*/
public function save_data($post_id)
{
//parent::save_data($post_id);
$meta_key = WPCACore::PREFIX . $this->id;
$old = array_flip(get_post_meta($post_id, $meta_key, false));
$tax_input = $_POST['conditions'];
//Save terms
//Loop through each public taxonomy
foreach ($this->_get_taxonomies() as $taxonomy) {
//If no terms, maybe delete old ones
if (!isset($tax_input[$this->id.'-'.$taxonomy->name])) {
$terms = array();
if (isset($old[$taxonomy->name])) {
delete_post_meta($post_id, $meta_key, $taxonomy->name);
}
} else {
$terms = $tax_input[$this->id.'-'.$taxonomy->name];
$found_key = array_search($taxonomy->name, $terms);
//If meta key found maybe add it
if ($found_key !== false) {
if (!isset($old[$taxonomy->name])) {
add_post_meta($post_id, $meta_key, $taxonomy->name);
}
unset($terms[$found_key]);
//Otherwise maybe delete it
} elseif (isset($old[$taxonomy->name])) {
delete_post_meta($post_id, $meta_key, $taxonomy->name);
}
//Hierarchical taxonomies use ids instead of slugs
//see http://codex.wordpress.org/Function_Reference/wp_set_post_terms
if ($taxonomy->hierarchical) {
$terms = array_unique(array_map('intval', $terms));
}
}
wp_set_object_terms($post_id, $terms, $taxonomy->name);
}
}
/**
* Auto-select children of selected ancestor
*
* @since 1.0
* @param int $term_id
* @param int $tt_id
* @param string $taxonomy
* @return void
*/
public function term_ancestry_check($term_id, $tt_id, $taxonomy)
{
if (is_taxonomy_hierarchical($taxonomy)) {
$term = get_term($term_id, $taxonomy);
if ($term->parent != '0') {
// Get sidebars with term ancestor wanting to auto-select term
$query = new WP_Query(array(
'post_type' => WPCACore::TYPE_CONDITION_GROUP,
'post_status' => array(WPCACore::STATUS_OR,WPCACore::STATUS_EXCEPT,WPCACore::STATUS_PUBLISHED),
'meta_query' => array(
array(
'key' => WPCACore::PREFIX . 'autoselect',
'value' => 1,
'compare' => '='
)
),
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => get_ancestors($term_id, $taxonomy),
'include_children' => false
)
)
));
if ($query && $query->found_posts) {
foreach ($query->posts as $post) {
wp_set_post_terms($post->ID, $term_id, $taxonomy, true);
}
do_action('wpca/modules/auto-select/'.$this->category, $query->posts, $term);
}
}
}
}
}