614 lines
22 KiB
PHP
614 lines
22 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Iks Menu
|
|
*
|
|
*
|
|
* @package Iks Menu
|
|
* @author IksStudio
|
|
* @license GPL-3.0
|
|
* @link https://iks-menu.com
|
|
* @copyright 2019 IksStudio
|
|
*/
|
|
namespace IksStudio\IKSM\render;
|
|
|
|
use IksStudio\IKSM_CORE\Plugin;
|
|
use IksStudio\IKSM_CORE\settings\SettingsManager;
|
|
use IksStudio\IKSM_CORE\utils\Utils;
|
|
use IksStudio\IKSM;
|
|
use IksStudio\IKSM\utils\UtilsLocal;
|
|
use WP_Post;
|
|
class MenuDataManager {
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $data = [];
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $error = null;
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
private $args = [];
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $source = null;
|
|
|
|
/**
|
|
* @var SettingsManager|null
|
|
*/
|
|
private $settings_manager = null;
|
|
|
|
/**
|
|
* @var string|null
|
|
*/
|
|
private $page_url = null;
|
|
|
|
/**
|
|
* @var object|null
|
|
*/
|
|
private $queried_object = null;
|
|
|
|
/**
|
|
* @var integer
|
|
*/
|
|
private $initial_parent_id = 0;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $post_id_prefix = "post-";
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $faq_id_prefix = "faq-";
|
|
|
|
/**
|
|
* MenuDataManager constructor.
|
|
*
|
|
* @param $settings_manager SettingsManager
|
|
*/
|
|
public function __construct( $settings_manager ) {
|
|
$this->settings_manager = $settings_manager;
|
|
$this->source = $this->settings_manager->get_value( "source" );
|
|
$this->prepare_current_query_params();
|
|
$this->prepare_initial_parent_id();
|
|
if ( $this->is_source_menu() ) {
|
|
$this->get_WP_menu();
|
|
} else {
|
|
if ( $this->is_source_taxonomy() ) {
|
|
$this->get_WP_terms();
|
|
} else {
|
|
if ( $this->is_source_faqs() ) {
|
|
$this->get_faqs();
|
|
}
|
|
}
|
|
}
|
|
if ( !$this->get_error() ) {
|
|
$this->append_children();
|
|
$this->process_includes();
|
|
}
|
|
// Utils::pretty_dump( $this->get_data() );
|
|
}
|
|
|
|
private function prepare_current_query_params() {
|
|
// Query Object
|
|
$this->queried_object = get_queried_object();
|
|
// Page Url
|
|
$term_id = $this->get_queried_object_term_id();
|
|
if ( !!$term_id ) {
|
|
$this->page_url = get_term_link( $term_id );
|
|
} else {
|
|
if ( is_single() || is_page() ) {
|
|
$this->page_url = get_permalink();
|
|
} else {
|
|
global $wp;
|
|
$this->page_url = home_url( $wp->request );
|
|
}
|
|
}
|
|
$this->page_url = Utils::url_with_slash( $this->page_url );
|
|
}
|
|
|
|
private function prepare_initial_parent_id() {
|
|
if ( $this->is_source_taxonomy() ) {
|
|
if ( $this->settings_manager->has_setting( "child_of" ) ) {
|
|
$this->initial_parent_id = $this->settings_manager->get_value( "child_of" );
|
|
} else {
|
|
if ( $this->settings_manager->has_setting( "parent" ) ) {
|
|
$this->initial_parent_id = $this->settings_manager->get_value( "parent" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function get_WP_menu() {
|
|
$menu_id = $this->settings_manager->get_value( "menu_id" );
|
|
if ( $menu_id ) {
|
|
$this->args = [
|
|
"output" => ARRAY_A,
|
|
'output_key' => 'menu_order',
|
|
];
|
|
$items = wp_get_nav_menu_items( $menu_id, $this->args );
|
|
if ( is_array( $items ) ) {
|
|
foreach ( $items as $key => $item ) {
|
|
$id = (int) $item->ID;
|
|
$object_id = (int) $item->object_id;
|
|
$link = $item->url;
|
|
$is_term = $item->type === 'taxonomy';
|
|
$item_data = [
|
|
"id" => $id,
|
|
"initial_id" => $id,
|
|
"object_id" => $object_id,
|
|
"title" => $item->title,
|
|
"link" => $link,
|
|
"parent" => (int) $item->menu_item_parent,
|
|
"is_current" => $this->is_current_page_url( $link ),
|
|
"is_term_includes_post" => ( !$is_term ? false : $this->is_term_includes_post( $object_id, $item->object ) ),
|
|
"is_page_includes_post" => false,
|
|
"is_expanded" => false,
|
|
"posts_count" => null,
|
|
"is_post" => false,
|
|
"target" => $item->target,
|
|
];
|
|
$this->data[] = $item_data;
|
|
}
|
|
} else {
|
|
$this->error = Utils::t( "Cannot get WordPress menu" ) . " (id = {$menu_id})";
|
|
}
|
|
}
|
|
}
|
|
|
|
private function get_WP_terms() {
|
|
$taxonomy = $this->get_taxonomy();
|
|
if ( $taxonomy ) {
|
|
$this->args = [
|
|
"taxonomy" => $taxonomy,
|
|
"pad_counts" => $this->settings_manager->get_value( 'posts_count_calc_type' ) === 'pad_count',
|
|
];
|
|
$this->save_args( [
|
|
"orderby",
|
|
"order",
|
|
"hide_empty",
|
|
"hierarchical",
|
|
"include",
|
|
"exclude",
|
|
"search",
|
|
"child_of",
|
|
"parent",
|
|
"childless"
|
|
] );
|
|
if ( !!$this->settings_manager->get_value( "show_current_terms_tree" ) ) {
|
|
$queried_term_id = $this->get_queried_object_term_id();
|
|
if ( $queried_term_id ) {
|
|
$args = array_merge( [], $this->args );
|
|
$args["parent"] = $queried_term_id;
|
|
unset($args["child_of"]);
|
|
$sub_terms = get_terms( $args );
|
|
if ( count( $sub_terms ) ) {
|
|
// Displaying entire menu, when no items detected for current element
|
|
// All checks passed: set term ID + un-parent to avoid conflicts
|
|
$this->args['child_of'] = $queried_term_id;
|
|
// child_of = with sub items
|
|
unset($this->args['parent']);
|
|
// Save initial parent ID
|
|
$this->initial_parent_id = $queried_term_id;
|
|
}
|
|
}
|
|
}
|
|
if ( version_compare( get_bloginfo( 'version' ), '4.5', '>=' ) ) {
|
|
$terms = get_terms( $this->args );
|
|
} else {
|
|
$terms = get_terms( $taxonomy, $this->args );
|
|
}
|
|
if ( is_array( $terms ) ) {
|
|
if ( !empty( $terms ) ) {
|
|
$index = 0;
|
|
$show_posts = $this->settings_manager->get_value( "show_posts" );
|
|
/* Posts */
|
|
if ( $show_posts ) {
|
|
$post_type = Utils::get_post_type_by_taxonomy( $taxonomy );
|
|
$include = $this->settings_manager->get_value( "include" );
|
|
$has_include = !empty( $include );
|
|
$posts_order_by = $this->settings_manager->get_value( 'posts_order_by' );
|
|
$posts_order = $this->settings_manager->get_value( 'posts_order' );
|
|
$posts = get_posts( [
|
|
'post_type' => $post_type,
|
|
'posts_per_page' => -1,
|
|
'orderby' => $posts_order_by,
|
|
'order' => $posts_order,
|
|
'tax_query' => [[
|
|
'taxonomy' => $taxonomy,
|
|
'operator' => ( $has_include ? 'IN' : 'EXISTS' ),
|
|
'terms' => ( $has_include ? Utils::get_terms_ids( $terms ) : [] ),
|
|
]],
|
|
'suppress_filters' => false,
|
|
] );
|
|
$posts_by_terms = [];
|
|
foreach ( $posts as $post ) {
|
|
$post_terms = get_the_terms( $post, $taxonomy );
|
|
if ( !empty( $post_terms ) ) {
|
|
foreach ( $post_terms as $post_term ) {
|
|
if ( !isset( $posts_by_terms[$post_term->term_id] ) ) {
|
|
$posts_by_terms[$post_term->term_id] = [];
|
|
}
|
|
$posts_by_terms[$post_term->term_id][] = $post;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach ( $terms as $key => $term ) {
|
|
$id = (int) $term->term_id;
|
|
$link = get_term_link( $id );
|
|
$term_data = [
|
|
"id" => $id,
|
|
"initial_id" => $id,
|
|
"title" => $term->name,
|
|
"link" => $link,
|
|
"parent" => (int) $term->parent,
|
|
"is_current" => $this->is_current_page_url( $link ) || $this->get_queried_object_term_id() === $id,
|
|
"is_term_includes_post" => $this->is_term_includes_post( $id, $this->get_taxonomy() ),
|
|
"is_page_includes_post" => false,
|
|
"index" => $index,
|
|
"is_expanded" => false,
|
|
"posts_count" => $term->count,
|
|
"is_post" => false,
|
|
"target" => null,
|
|
];
|
|
$index++;
|
|
$this->data[] = $term_data;
|
|
if ( $show_posts ) {
|
|
if ( isset( $posts_by_terms[$id] ) ) {
|
|
$result_posts = $posts_by_terms[$id];
|
|
foreach ( $result_posts as $post ) {
|
|
$post_id = $post->ID;
|
|
$result_id = $this->post_id_prefix . $post_id;
|
|
// Sometimes WP has the same IDs for terms & posts (for some reason), so it's better to handle this situation
|
|
$post_link = get_permalink( $post_id );
|
|
$this->data[] = [
|
|
"id" => $result_id,
|
|
"initial_id" => $post_id,
|
|
"title" => $post->post_title,
|
|
"link" => $post_link,
|
|
"parent" => $id,
|
|
"is_current" => $this->is_current_page_url( $post_link ),
|
|
"is_term_includes_post" => false,
|
|
"is_page_includes_post" => false,
|
|
"index" => $index,
|
|
"is_expanded" => false,
|
|
"posts_count" => false,
|
|
"is_post" => true,
|
|
"target" => null,
|
|
];
|
|
$index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Processing exclude without children
|
|
* @since 1.8.0
|
|
*/
|
|
$this->process_exclude_without_children();
|
|
} else {
|
|
$this->error = Utils::t( "Cannot get WordPress terms for taxonomy" ) . " ({$taxonomy})";
|
|
}
|
|
}
|
|
/* Sorting items */
|
|
usort( $this->data, function ( $a, $b ) {
|
|
$aIsPost = Utils::get( $a, "is_post" ) === true;
|
|
$bIsPost = Utils::get( $b, "is_post" ) === true;
|
|
if ( $aIsPost === $bIsPost ) {
|
|
// Saving order
|
|
return Utils::get( $a, "index" ) - Utils::get( $b, "index" );
|
|
} else {
|
|
return $aIsPost - $bIsPost;
|
|
// Terms first
|
|
// TODO: New Setting for ordering posts and terms + posts
|
|
//return $bIs - $aIs; // Posts first
|
|
}
|
|
} );
|
|
// Removing unnecessary index
|
|
foreach ( $this->data as $index => $item ) {
|
|
unset($this->data[$index]["index"]);
|
|
}
|
|
}
|
|
|
|
private function get_faqs() {
|
|
$args = [
|
|
'post_type' => UtilsLocal::get_faqs_type(),
|
|
'posts_per_page' => -1,
|
|
'orderby' => $this->settings_manager->get_value( 'faqs_order_by' ),
|
|
'order' => $this->settings_manager->get_value( 'faqs_order' ),
|
|
'suppress_filters' => false,
|
|
];
|
|
$group = $this->settings_manager->get_value( 'faq_groups' );
|
|
if ( $group && $group !== 'all' ) {
|
|
$args['tax_query'] = [[
|
|
'taxonomy' => UtilsLocal::get_faqs_taxonomy_type(),
|
|
'field' => 'id',
|
|
'terms' => $group,
|
|
]];
|
|
}
|
|
$posts = get_posts( $args );
|
|
$index = 0;
|
|
foreach ( $posts as $key => $post ) {
|
|
$post_id = $post->ID;
|
|
$question_id = $this->faq_id_prefix . $post_id . "-question";
|
|
$answer_id = $this->faq_id_prefix . $post_id . "-answer";
|
|
// Question
|
|
$this->data[] = [
|
|
"id" => $question_id,
|
|
"initial_id" => $post_id,
|
|
"title" => $post->post_title,
|
|
"link" => null,
|
|
"parent" => 0,
|
|
"is_current" => false,
|
|
"is_term_includes_post" => false,
|
|
"is_page_includes_post" => false,
|
|
"index" => $index,
|
|
"is_expanded" => false,
|
|
"posts_count" => false,
|
|
"is_post" => true,
|
|
"sub_type" => "faq_question",
|
|
"target" => null,
|
|
];
|
|
// Answer
|
|
$index++;
|
|
$this->data[] = [
|
|
"id" => $answer_id,
|
|
"initial_id" => $post_id,
|
|
"title" => $post->post_content,
|
|
"link" => null,
|
|
"parent" => $question_id,
|
|
"is_current" => false,
|
|
"is_term_includes_post" => false,
|
|
"is_page_includes_post" => false,
|
|
"index" => $index,
|
|
"is_expanded" => false,
|
|
"posts_count" => false,
|
|
"is_post" => true,
|
|
"sub_type" => "faq_answer",
|
|
"target" => null,
|
|
];
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exclude without children
|
|
* @since 1.8.0
|
|
*/
|
|
private function process_exclude_without_children() {
|
|
$exclude_without_children = Utils::split_numbers_by_comma_space( $this->settings_manager->get_value( "exclude_without_children" ) );
|
|
if ( !empty( $exclude_without_children ) ) {
|
|
foreach ( $this->data as $index => $item ) {
|
|
// Backward compatibility: users trying to remove posts without setting the prefix, so removing it before check:
|
|
$id = $item["initial_id"];
|
|
$exclude_term = in_array( $id, $exclude_without_children );
|
|
if ( $exclude_term ) {
|
|
if ( !$item["is_post"] ) {
|
|
foreach ( $this->data as $inner_index => $inner_item ) {
|
|
if ( (int) $inner_item["parent"] === $id ) {
|
|
// For each child
|
|
if ( $inner_item["is_post"] ) {
|
|
// Removing posts
|
|
unset($this->data[$inner_index]);
|
|
} else {
|
|
// New parent for children - is not excluded parent of item
|
|
$new_parent_id = $this->get_not_excluded_parent_id( $item, $exclude_without_children );
|
|
$this->data[$inner_index]["parent"] = $new_parent_id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset($this->data[$index]);
|
|
// Removing item
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively finds parent of item, that is not in $excludes array
|
|
*
|
|
* @param array $item
|
|
* @param array $excludes
|
|
*
|
|
* @return int|mixed
|
|
* @since 1.8.0
|
|
*/
|
|
private function get_not_excluded_parent_id( $item, $excludes ) {
|
|
$parent_id = $item["parent"];
|
|
if ( in_array( $parent_id, $excludes ) ) {
|
|
$parent = $this->find_term( $parent_id );
|
|
if ( $parent ) {
|
|
return $this->get_not_excluded_parent_id( $parent, $excludes );
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
return $parent_id;
|
|
}
|
|
}
|
|
|
|
private function append_children() {
|
|
foreach ( $this->data as $index => $item ) {
|
|
$children = $this->get_term_children( $item["id"], true );
|
|
$this->data[$index]["children"] = ( count( $children ) > 0 ? $children : null );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks "include" setting: If the term doesn't have a retrieved parent, then change it's parent to "0"
|
|
*
|
|
* @since 1.7.7
|
|
*/
|
|
private function process_includes() {
|
|
$include = $this->settings_manager->get_value( "include" );
|
|
if ( $include ) {
|
|
$include_array = Utils::split_numbers_by_comma_space( $include );
|
|
foreach ( $include_array as $id ) {
|
|
$term = $this->find_term( $id );
|
|
if ( $term && $term["parent"] !== 0 && !$this->is_term_has_retrieved_parent( $term ) ) {
|
|
$this->change_term_data( $id, [
|
|
"parent" => 0,
|
|
] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function get_term_level( $item, $current_level = 1 ) {
|
|
$parent_id = $item["parent"];
|
|
if ( !$parent_id ) {
|
|
return $current_level;
|
|
} else {
|
|
$parent = $this->find_term( $parent_id );
|
|
if ( $parent ) {
|
|
return $this->get_term_level( $parent, $current_level + 1 );
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function get_term_children( $item_id, $only_ids = false ) {
|
|
$children = [];
|
|
foreach ( $this->data as $item ) {
|
|
if ( $item["parent"] === $item_id ) {
|
|
array_push( $children, ( $only_ids ? $item["id"] : $item ) );
|
|
}
|
|
}
|
|
return $children;
|
|
}
|
|
|
|
public function is_term_has_retrieved_parent( $term ) {
|
|
return !!$this->find_term( $term["parent"] );
|
|
}
|
|
|
|
public function is_term_has_children( $term ) {
|
|
return !empty( $term["children"] );
|
|
}
|
|
|
|
/**
|
|
* Finds term data or index by id
|
|
*
|
|
* @param int $id
|
|
* @param bool $return_index
|
|
*
|
|
* @return int|string|array|null
|
|
*/
|
|
public function find_term( $id, $return_index = false ) {
|
|
foreach ( $this->data as $index => $item ) {
|
|
if ( $item["id"] === $id ) {
|
|
return ( $return_index ? $index : $item );
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Checks, term is parent of current post (WP_Post)
|
|
*
|
|
* @param integer $term_id ID of the term
|
|
* @param string $taxonomy Taxonomy of the term
|
|
*
|
|
* @return bool
|
|
* @since 1.8.0
|
|
*/
|
|
private function is_term_includes_post( $term_id, $taxonomy ) {
|
|
if ( $this->queried_object instanceof WP_Post ) {
|
|
$post_id = $this->queried_object->ID;
|
|
$terms = wp_get_post_terms( $post_id, $taxonomy );
|
|
$terms_ids = Utils::get_terms_ids( $terms );
|
|
return in_array( $term_id, $terms_ids );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks, is menu item's link is the same as current page url
|
|
*
|
|
* @param string $link
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function is_current_page_url( $link ) {
|
|
return $this->page_url === Utils::url_with_slash( $link );
|
|
}
|
|
|
|
public function get_queried_object_term_id() {
|
|
return ( Utils::object_has_property( $this->queried_object, 'term_id' ) ? $this->queried_object->term_id : null );
|
|
}
|
|
|
|
public function is_source_menu() {
|
|
return $this->source === "menu";
|
|
}
|
|
|
|
public function is_source_taxonomy() {
|
|
return $this->source === "taxonomy";
|
|
}
|
|
|
|
public function is_source_faqs() {
|
|
return $this->source === "faqs";
|
|
}
|
|
|
|
public function get_taxonomy() {
|
|
return $this->settings_manager->get_value( "taxonomy" );
|
|
}
|
|
|
|
public function get_source() {
|
|
return $this->source;
|
|
}
|
|
|
|
public function get_initial_parent_id() {
|
|
return $this->initial_parent_id;
|
|
}
|
|
|
|
/**
|
|
* @param $id integer
|
|
* @param $changes array
|
|
*/
|
|
public function change_term_data( $id, $changes ) {
|
|
$index = $this->find_term( $id, true );
|
|
$this->data[$index] = array_merge( $this->data[$index], $changes );
|
|
}
|
|
|
|
private function save_args( $args ) {
|
|
foreach ( $args as $key ) {
|
|
$value = $this->settings_manager->get_value( $key );
|
|
if ( $value !== null ) {
|
|
$this->args[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function get_args() {
|
|
return $this->args;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function get_data() {
|
|
return $this->data;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function get_error() {
|
|
return $this->error;
|
|
}
|
|
|
|
}
|