Files
Bookshelf/bd-bookshelf/inc/nav-menu.php
2026-10-18 19:01:01 -07:00

271 lines
6.9 KiB
PHP

<?php
// Copyright 2026 Umbra Tools (umbratools.com)
// SPDX-License-Identifier: Apache-2.0
/**
* Navigation Menu Hooks
*
* @package BD_Murphy
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Filter nav menu items to add dynamic submenus
*
* @param string $items Menu items HTML.
* @param stdClass $args Menu arguments.
* @return string
*/
function bdm_filter_nav_menu_items($items, $args) {
if ($args->theme_location !== 'primary') {
return $items;
}
// Get books for MyBooks submenu
$books = bdm_get_books_for_menu();
if (!empty($books)) {
$books_submenu = bdm_build_book_submenu($books);
// Find or create MyBooks menu item
if (preg_match('/(<li[^>]*>.*?My\s*Books.*?<\/li>)/s', $items, $matches)) {
$mybooks_item = $matches[1];
// Add submenu class to the li
$mybooks_item_with_sub = preg_replace(
'/(<li[^>]*class=["\'])/',
'$1menu-item-has-children ',
$mybooks_item
);
// Close current li and add submenu
$mybooks_item_with_sub = preg_replace(
'/<\/li>\s*$/',
$books_submenu . '</li>',
$mybooks_item_with_sub
);
$items = str_replace($mybooks_item, $mybooks_item_with_sub, $items);
}
}
// Get series for Series submenu
$series = bdm_get_series_for_menu();
if (!empty($series)) {
$series_submenu = bdm_build_series_submenu($series);
if (preg_match('/(<li[^>]*>.*?Series.*?<\/li>)/s', $items, $matches)) {
$series_item = $matches[1];
$series_item_with_sub = preg_replace(
'/(<li[^>]*class=["\'])/',
'$1menu-item-has-children ',
$series_item
);
$series_item_with_sub = preg_replace(
'/<\/li>\s*$/',
$series_submenu . '</li>',
$series_item_with_sub
);
$items = str_replace($series_item, $series_item_with_sub, $items);
}
}
// Get books with reviews for Reviews submenu
$review_books = bdm_get_books_with_reviews();
if (!empty($review_books)) {
$reviews_submenu = bdm_build_reviews_submenu($review_books);
if (preg_match('/(<li[^>]*>.*?Reviews.*?<\/li>)/s', $items, $matches)) {
$reviews_item = $matches[1];
$reviews_item_with_sub = preg_replace(
'/(<li[^>]*class=["\'])/',
'$1menu-item-has-children ',
$reviews_item
);
$reviews_item_with_sub = preg_replace(
'/<\/li>\s*$/',
$reviews_submenu . '</li>',
$reviews_item_with_sub
);
$items = str_replace($reviews_item, $reviews_item_with_sub, $items);
}
}
return $items;
}
add_filter('wp_nav_menu_items', 'bdm_filter_nav_menu_items', 10, 2);
/**
* Get books for menu
*
* @return array
*/
function bdm_get_books_for_menu() {
$books = get_posts(array(
'post_type' => 'book',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
));
return $books;
}
/**
* Get series for menu
*
* @return array
*/
function bdm_get_series_for_menu() {
if (!taxonomy_exists('series')) {
return array();
}
$terms = get_terms(array(
'taxonomy' => 'series',
'hide_empty' => true,
'orderby' => 'name',
'order' => 'ASC',
));
return is_wp_error($terms) ? array() : $terms;
}
/**
* Get books with reviews
*
* @return array
*/
function bdm_get_books_with_reviews() {
$books = get_posts(array(
'post_type' => 'book',
'post_status' => 'publish',
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
));
$review_books = array();
foreach ($books as $book) {
$goodreads = get_post_meta($book->ID, 'goodreads_url', true);
$amazon = get_post_meta($book->ID, 'amazon_review_url', true);
if (!empty($goodreads) || !empty($amazon)) {
$review_books[] = $book;
}
}
return $review_books;
}
/**
* Build book submenu HTML
*
* @param array $books Books.
* @return string
*/
function bdm_build_book_submenu($books) {
if (empty($books)) {
return '';
}
$submenu = "\n" . '<ul class="sub-menu">' . "\n";
foreach ($books as $book) {
$url = get_permalink($book->ID);
$title = $book->post_title;
$submenu .= '<li class="menu-item"><a href="' . esc_url($url) . '">' . esc_html($title) . '</a></li>' . "\n";
}
$submenu .= '</ul>';
return $submenu;
}
/**
* Build series submenu HTML
*
* @param array $series Series.
* @return string
*/
function bdm_build_series_submenu($series) {
if (empty($series)) {
return '';
}
$submenu = "\n" . '<ul class="sub-menu">' . "\n";
foreach ($series as $term) {
$url = get_term_link($term);
$submenu .= '<li class="menu-item"><a href="' . esc_url($url) . '">' . esc_html($term->name) . '</a></li>' . "\n";
}
$submenu .= '</ul>';
return $submenu;
}
/**
* Build reviews submenu HTML
*
* @param array $books Books with reviews.
* @return string
*/
function bdm_build_reviews_submenu($books) {
if (empty($books)) {
return '';
}
$submenu = "\n" . '<ul class="sub-menu">' . "\n";
foreach ($books as $book) {
$title = $book->post_title;
// Determine the review URL (prefer Goodreads if available)
$goodreads = get_post_meta($book->ID, 'goodreads_url', true);
$amazon = get_post_meta($book->ID, 'amazon_review_url', true);
$url = !empty($goodreads) ? $goodreads : $amazon;
if (!empty($url)) {
$submenu .= '<li class="menu-item"><a href="' . esc_url($url) . '" target="_blank" rel="noopener noreferrer">' . esc_html($title) . '</a></li>' . "\n";
}
}
$submenu .= '</ul>';
return $submenu;
}
/**
* Check if series exist
*
* @return bool
*/
function bdm_series_exist() {
if (class_exists('BD_BookShelfHelper')) {
$terms = get_terms(array(
'taxonomy' => 'series',
'hide_empty' => true,
'fields' => 'count',
));
return $terms > 0;
}
return false;
}
/**
* Add series menu item class
*
* @param array $classes CSS classes.
* @param WP_Post $item Menu item.
* @param array $args Menu arguments.
* @param int $depth Item depth.
* @return array
*/
function bdm_nav_menu_css_class($classes, $item, $args, $depth) {
if ($item->url && false !== strpos($item->url, '/series/')) {
$classes[] = 'series-menu-item';
}
return $classes;
}
add_filter('nav_menu_css_class', 'bdm_nav_menu_css_class', 10, 4);