490 lines
14 KiB
PHP
490 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* Menu Item Conditions functionality.
|
|
*
|
|
* @package GenerateBlocksPro
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit; // Exit if accessed directly.
|
|
}
|
|
|
|
/**
|
|
* Class GenerateBlocks_Pro_Menu_Item_Conditions
|
|
*/
|
|
class GenerateBlocks_Pro_Menu_Item_Conditions extends GenerateBlocks_Pro_Singleton {
|
|
|
|
/**
|
|
* Cache for evaluated conditions to avoid repeated checks.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $condition_cache = [];
|
|
|
|
/**
|
|
* Initialize the class.
|
|
*/
|
|
public function init() {
|
|
// Don't initialize if block conditions are disabled.
|
|
if ( ! generateblocks_pro_block_conditions_enabled() ) {
|
|
return;
|
|
}
|
|
|
|
// Register post meta for menu items.
|
|
add_action( 'init', [ $this, 'register_post_meta' ] );
|
|
|
|
// Add fields to menu item editor.
|
|
add_action( 'wp_nav_menu_item_custom_fields', [ $this, 'add_condition_fields' ], 20, 5 );
|
|
|
|
// Save menu item conditions.
|
|
add_action( 'wp_update_nav_menu_item', [ $this, 'save_menu_item_conditions' ], 10, 2 );
|
|
|
|
// Filter menu items based on conditions.
|
|
add_filter( 'wp_nav_menu_objects', [ $this, 'filter_menu_items_by_conditions' ], 10, 2 );
|
|
|
|
// Add to condition usage tracking.
|
|
add_filter( 'generateblocks_condition_usage_handlers', [ $this, 'add_menu_usage_handler' ], 10, 2 );
|
|
|
|
// Add admin scripts for dynamic UI.
|
|
add_action( 'admin_enqueue_scripts', [ $this, 'add_admin_scripts' ] );
|
|
|
|
// AJAX handler for condition search.
|
|
add_action( 'wp_ajax_gb_search_conditions', [ $this, 'ajax_search_conditions' ] );
|
|
|
|
// AJAX handler for loading more conditions.
|
|
add_action( 'wp_ajax_gb_load_more_conditions', [ $this, 'ajax_load_more_conditions' ] );
|
|
}
|
|
|
|
/**
|
|
* Register the post meta.
|
|
*/
|
|
public function register_post_meta() {
|
|
register_post_meta(
|
|
'nav_menu_item',
|
|
'_gb_menu_condition',
|
|
[
|
|
'show_in_rest' => true,
|
|
'single' => true,
|
|
'type' => 'string',
|
|
]
|
|
);
|
|
|
|
register_post_meta(
|
|
'nav_menu_item',
|
|
'_gb_menu_condition_invert',
|
|
[
|
|
'show_in_rest' => true,
|
|
'single' => true,
|
|
'type' => 'boolean',
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the conditions limit per page.
|
|
*/
|
|
private function get_conditions_limit() {
|
|
return apply_filters( 'generateblocks_menu_conditions_limit', 100 );
|
|
}
|
|
|
|
/**
|
|
* Add condition fields to menu items.
|
|
*
|
|
* @param string $item_id Menu item ID as a numeric string.
|
|
* @param WP_Post $menu_item Menu item data object.
|
|
* @param int $depth Depth of menu item. Used for padding.
|
|
* @param stdClass|null $args An object of menu item arguments.
|
|
* @param int $current_object_id Nav menu ID.
|
|
*/
|
|
public function add_condition_fields( $item_id, $menu_item, $depth, $args, $current_object_id ) {
|
|
$condition_id = get_post_meta( $item_id, '_gb_menu_condition', true );
|
|
$invert = get_post_meta( $item_id, '_gb_menu_condition_invert', true );
|
|
|
|
// Get conditions for dropdown.
|
|
static $conditions_cache = null;
|
|
|
|
if ( null === $conditions_cache ) {
|
|
// Get the most recent conditions.
|
|
$conditions_cache = get_posts(
|
|
[
|
|
'post_type' => 'gblocks_condition',
|
|
'posts_per_page' => $this->get_conditions_limit(),
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
'post_status' => 'publish',
|
|
'suppress_filters' => false,
|
|
'no_found_rows' => true,
|
|
]
|
|
);
|
|
}
|
|
|
|
$conditions = $conditions_cache;
|
|
|
|
// Always include the currently selected condition if it's not in the list.
|
|
if ( $condition_id && 'gblocks_condition' === get_post_type( $condition_id ) ) {
|
|
$found = false;
|
|
foreach ( $conditions as $condition ) {
|
|
if ( $condition->ID === $condition_id ) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ! $found ) {
|
|
$selected_condition = get_post( $condition_id );
|
|
if ( $selected_condition ) {
|
|
// Add it to the list for this menu item only.
|
|
$conditions = array_merge( [ $selected_condition ], $conditions );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if there are more conditions than we're showing.
|
|
$total_conditions = wp_count_posts( 'gblocks_condition' )->publish;
|
|
$has_more = $total_conditions > count( $conditions_cache );
|
|
?>
|
|
<p class="field-gb-menu-condition description description-wide">
|
|
<label for="gb-menu-condition-<?php echo esc_attr( $item_id ); ?>">
|
|
<?php esc_html_e( 'Display Condition', 'generateblocks-pro' ); ?>
|
|
</label>
|
|
<div class="gb-menu-condition-wrapper" style="display: flex; align-items: center; gap: 10px;">
|
|
<select
|
|
name="gb-menu-condition[<?php echo esc_attr( $item_id ); ?>]"
|
|
id="gb-menu-condition-<?php echo esc_attr( $item_id ); ?>"
|
|
class="gb-menu-condition-select"
|
|
style="flex: 1;"
|
|
data-item-id="<?php echo esc_attr( $item_id ); ?>"
|
|
data-page="1"
|
|
>
|
|
<option value=""><?php esc_html_e( 'No condition', 'generateblocks-pro' ); ?></option>
|
|
<?php foreach ( $conditions as $condition ) : ?>
|
|
<option value="<?php echo esc_attr( $condition->ID ); ?>" <?php selected( $condition_id, $condition->ID ); ?>>
|
|
<?php echo esc_html( $condition->post_title ); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<?php if ( $has_more ) : ?>
|
|
<button
|
|
type="button"
|
|
class="button gb-load-more-conditions"
|
|
data-item-id="<?php echo esc_attr( $item_id ); ?>"
|
|
>
|
|
<?php esc_html_e( 'Load More', 'generateblocks-pro' ); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
<span class="description" style="display: block; margin-top: 5px;">
|
|
<?php esc_html_e( 'Choose a condition to control when this menu item appears.', 'generateblocks-pro' ); ?>
|
|
</span>
|
|
</p>
|
|
|
|
<p class="field-gb-menu-condition-invert description description-wide" style="<?php echo $condition_id ? '' : 'display: none;'; ?>">
|
|
<label>
|
|
<input
|
|
type="checkbox"
|
|
name="gb-menu-condition-invert[<?php echo esc_attr( $item_id ); ?>]"
|
|
value="1"
|
|
<?php checked( $invert, '1' ); ?>
|
|
/>
|
|
<?php esc_html_e( 'Invert condition', 'generateblocks-pro' ); ?>
|
|
</label>
|
|
<span class="description" style="display: block"><?php esc_html_e( 'Hide the menu item when the condition is true instead of false.', 'generateblocks-pro' ); ?></span>
|
|
</p>
|
|
|
|
<?php wp_nonce_field( 'update-menu-item-condition', 'menu-item-condition-nonce' ); ?>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Save the menu item conditions.
|
|
*
|
|
* @param int $menu_id The menu ID.
|
|
* @param int $menu_item_id The menu item ID.
|
|
* @return mixed
|
|
*/
|
|
public function save_menu_item_conditions( $menu_id, $menu_item_id ) {
|
|
// Verify nonce.
|
|
if (
|
|
! isset( $_POST['menu-item-condition-nonce'] )
|
|
|| ! wp_verify_nonce( $_POST['menu-item-condition-nonce'], 'update-menu-item-condition' )
|
|
) {
|
|
return $menu_id;
|
|
}
|
|
|
|
// Save condition from dropdown.
|
|
$condition_id = isset( $_POST['gb-menu-condition'][ $menu_item_id ] )
|
|
? sanitize_text_field( $_POST['gb-menu-condition'][ $menu_item_id ] )
|
|
: '';
|
|
|
|
if ( $condition_id ) {
|
|
update_post_meta( $menu_item_id, '_gb_menu_condition', $condition_id );
|
|
} else {
|
|
delete_post_meta( $menu_item_id, '_gb_menu_condition' );
|
|
}
|
|
|
|
// Save invert setting.
|
|
$invert = isset( $_POST['gb-menu-condition-invert'][ $menu_item_id ] ) ? '1' : '0';
|
|
|
|
if ( $condition_id && '1' === $invert ) {
|
|
update_post_meta( $menu_item_id, '_gb_menu_condition_invert', '1' );
|
|
} else {
|
|
delete_post_meta( $menu_item_id, '_gb_menu_condition_invert' );
|
|
}
|
|
|
|
return $menu_id;
|
|
}
|
|
|
|
/**
|
|
* Filter menu items based on conditions.
|
|
*
|
|
* @param array $sorted_menu_items The menu items, sorted by menu order.
|
|
* @param stdClass $args The menu arguments.
|
|
* @return array Filtered menu items.
|
|
*/
|
|
public function filter_menu_items_by_conditions( $sorted_menu_items, $args ) {
|
|
if ( empty( $sorted_menu_items ) ) {
|
|
return $sorted_menu_items;
|
|
}
|
|
|
|
$removed_items = [];
|
|
|
|
// Build list of all items that should be removed (condition fails or parent removed).
|
|
// Note: WordPress guarantees menu items are in hierarchical order (parents before children).
|
|
foreach ( $sorted_menu_items as $item ) {
|
|
$item_id = (int) $item->ID;
|
|
|
|
// Skip if already marked for removal.
|
|
if ( in_array( $item_id, $removed_items, true ) ) {
|
|
continue;
|
|
}
|
|
|
|
// Check if parent was removed.
|
|
$parent_id = ! empty( $item->menu_item_parent ) ? (int) $item->menu_item_parent : 0;
|
|
if ( $parent_id && in_array( $parent_id, $removed_items, true ) ) {
|
|
$removed_items[] = $item_id;
|
|
continue;
|
|
}
|
|
|
|
// Check condition.
|
|
$condition_id = get_post_meta( $item_id, '_gb_menu_condition', true );
|
|
if ( $condition_id && ! $this->should_show_menu_item( $item_id, $condition_id ) ) {
|
|
$removed_items[] = $item_id;
|
|
}
|
|
}
|
|
|
|
// If no items were removed, return the original array.
|
|
if ( empty( $removed_items ) ) {
|
|
return $sorted_menu_items;
|
|
}
|
|
|
|
// Filter out removed items.
|
|
$final_items = [];
|
|
foreach ( $sorted_menu_items as $item ) {
|
|
if ( ! in_array( (int) $item->ID, $removed_items, true ) ) {
|
|
$final_items[] = $item;
|
|
}
|
|
}
|
|
|
|
return $final_items;
|
|
}
|
|
|
|
/**
|
|
* Check if a menu item should be shown based on its condition.
|
|
*
|
|
* @param int $menu_item_id The menu item ID.
|
|
* @param string $condition_id The condition ID.
|
|
* @return bool Whether to show the menu item.
|
|
*/
|
|
private function should_show_menu_item( $menu_item_id, $condition_id ) {
|
|
$condition_id = absint( $condition_id );
|
|
|
|
if ( ! $condition_id ) {
|
|
return true;
|
|
}
|
|
|
|
$invert_condition = get_post_meta( $menu_item_id, '_gb_menu_condition_invert', true ) === '1';
|
|
|
|
// Check cache first to avoid repeated evaluations.
|
|
$cache_key = $condition_id . '_' . ( $invert_condition ? '1' : '0' );
|
|
|
|
if ( isset( $this->condition_cache[ $cache_key ] ) ) {
|
|
return $this->condition_cache[ $cache_key ];
|
|
}
|
|
|
|
// Default to showing the menu item.
|
|
$show = true;
|
|
|
|
// Check if the condition post exists and is published.
|
|
$condition_post = get_post( $condition_id );
|
|
|
|
if ( $condition_post && 'publish' === $condition_post->post_status ) {
|
|
// Get the condition data.
|
|
$display_conditions = get_post_meta( $condition_id, '_gb_conditions', true );
|
|
|
|
if ( ! empty( $display_conditions ) ) {
|
|
// Use the existing conditions system to evaluate.
|
|
$show = GenerateBlocks_Pro_Conditions::show( $display_conditions );
|
|
|
|
// If invert is enabled, flip the result.
|
|
if ( $invert_condition ) {
|
|
$show = ! $show;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cache the result.
|
|
$this->condition_cache[ $cache_key ] = $show;
|
|
|
|
return $show;
|
|
}
|
|
|
|
/**
|
|
* Add menu item conditions handler to usage search.
|
|
*
|
|
* @param array $handlers Existing handlers.
|
|
* @param int $condition_id The condition ID.
|
|
* @return array Modified handlers.
|
|
*/
|
|
public function add_menu_usage_handler( $handlers, $condition_id ) {
|
|
$handlers['menu_item_conditions'] = [
|
|
'method' => 'search_menu_item_conditions_usage',
|
|
'label' => __( 'Menu Item Conditions', 'generateblocks-pro' ),
|
|
];
|
|
|
|
return $handlers;
|
|
}
|
|
|
|
/**
|
|
* AJAX handler for searching conditions.
|
|
*/
|
|
public function ajax_search_conditions() {
|
|
// Verify nonce.
|
|
if ( ! wp_verify_nonce( $_GET['_ajax_nonce'], 'gb_search_conditions' ) ) {
|
|
wp_die();
|
|
}
|
|
|
|
$search = isset( $_GET['search'] ) ? sanitize_text_field( $_GET['search'] ) : '';
|
|
$page = isset( $_GET['page'] ) ? absint( $_GET['page'] ) : 1;
|
|
$per_page = $this->get_conditions_limit();
|
|
|
|
$args = [
|
|
'post_type' => 'gblocks_condition',
|
|
'posts_per_page' => $per_page,
|
|
'paged' => $page,
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
'post_status' => 'publish',
|
|
];
|
|
|
|
if ( $search ) {
|
|
$args['s'] = $search;
|
|
$args['orderby'] = 'relevance';
|
|
}
|
|
|
|
$conditions = get_posts( $args );
|
|
$total = wp_count_posts( 'gblocks_condition' )->publish;
|
|
|
|
$results = [];
|
|
foreach ( $conditions as $condition ) {
|
|
$results[] = [
|
|
'id' => $condition->ID,
|
|
'text' => $condition->post_title ? $condition->post_title : __( 'Untitled Condition', 'generateblocks-pro' ),
|
|
];
|
|
}
|
|
|
|
wp_send_json(
|
|
[
|
|
'results' => $results,
|
|
'pagination' => [
|
|
'more' => ( $page * $per_page ) < $total,
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* AJAX handler for loading more conditions.
|
|
*/
|
|
public function ajax_load_more_conditions() {
|
|
// Verify nonce.
|
|
if ( ! wp_verify_nonce( $_GET['_ajax_nonce'], 'gb_load_more_conditions' ) ) {
|
|
wp_die();
|
|
}
|
|
|
|
$page = isset( $_GET['page'] ) ? absint( $_GET['page'] ) : 2;
|
|
$limit = $this->get_conditions_limit();
|
|
$offset = ( $page - 1 ) * $limit;
|
|
|
|
$conditions = get_posts(
|
|
[
|
|
'post_type' => 'gblocks_condition',
|
|
'posts_per_page' => $limit,
|
|
'offset' => $offset,
|
|
'orderby' => 'date',
|
|
'order' => 'DESC',
|
|
'post_status' => 'publish',
|
|
'suppress_filters' => false,
|
|
'no_found_rows' => false,
|
|
]
|
|
);
|
|
|
|
$formatted_conditions = [];
|
|
foreach ( $conditions as $condition ) {
|
|
$formatted_conditions[] = [
|
|
'id' => $condition->ID,
|
|
'title' => $condition->post_title ? $condition->post_title : __( 'Untitled Condition', 'generateblocks-pro' ),
|
|
];
|
|
}
|
|
|
|
$total = wp_count_posts( 'gblocks_condition' )->publish;
|
|
$has_more = ( $page * $limit ) < $total;
|
|
|
|
wp_send_json_success(
|
|
[
|
|
'conditions' => $formatted_conditions,
|
|
'has_more' => $has_more,
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Add admin scripts for dynamic UI behavior.
|
|
*
|
|
* @param string $hook_suffix The current admin page.
|
|
*/
|
|
public function add_admin_scripts( $hook_suffix ) {
|
|
// Only load on nav-menus.php page.
|
|
if ( 'nav-menus.php' !== $hook_suffix ) {
|
|
return;
|
|
}
|
|
|
|
wp_enqueue_script(
|
|
'gb-menu-item-conditions',
|
|
GENERATEBLOCKS_PRO_DIR_URL . 'dist/menu-item-conditions.js',
|
|
array(),
|
|
GENERATEBLOCKS_PRO_VERSION,
|
|
true
|
|
);
|
|
|
|
wp_localize_script(
|
|
'gb-menu-item-conditions',
|
|
'gbMenuConditions',
|
|
array(
|
|
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
|
'nonces' => array(
|
|
'loadMore' => wp_create_nonce( 'gb_load_more_conditions' ),
|
|
'search' => wp_create_nonce( 'gb_search_conditions' ),
|
|
),
|
|
'strings' => array(
|
|
'loading' => __( 'Loading...', 'generateblocks-pro' ),
|
|
'loadMore' => __( 'Load More', 'generateblocks-pro' ),
|
|
),
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Initialize the class.
|
|
GenerateBlocks_Pro_Menu_Item_Conditions::get_instance()->init();
|