400 lines
13 KiB
PHP
400 lines
13 KiB
PHP
<?php
|
|
// Copyright 2026 Umbra Tools (umbratools.com)
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
/**
|
|
* Main Plugin Class
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class BD_BookShelfHelper {
|
|
private static $instance = null;
|
|
private $db;
|
|
|
|
public static function instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
private function __construct() {
|
|
$this->init_hooks();
|
|
}
|
|
|
|
private function init_hooks() {
|
|
add_action('init', array($this, 'register_post_types'));
|
|
add_action('init', array($this, 'register_taxonomies'));
|
|
add_action('init', array($this, 'register_shortlinks'));
|
|
add_action('wp_head', array($this, 'output_seo_tags'));
|
|
|
|
add_filter('nav_menu_items', array($this, 'filter_series_menu_item'), 10, 2);
|
|
|
|
if (is_admin()) {
|
|
BD_BookShelfHelper_Admin::instance();
|
|
}
|
|
}
|
|
|
|
public function register_post_types() {
|
|
register_post_type('book', array(
|
|
'labels' => array(
|
|
'name' => __('Books', 'bd-bookshelfhelper'),
|
|
'singular_name' => __('Book', 'bd-bookshelfhelper'),
|
|
'add_new' => __('Add New', 'bd-bookshelfhelper'),
|
|
'add_new_item' => __('Add New Book', 'bd-bookshelfhelper'),
|
|
'edit_item' => __('Edit Book', 'bd-bookshelfhelper'),
|
|
'new_item' => __('New Book', 'bd-bookshelfhelper'),
|
|
'view_item' => __('View Book', 'bd-bookshelfhelper'),
|
|
'search_items' => __('Search Books', 'bd-bookshelfhelper'),
|
|
),
|
|
'public' => true,
|
|
'has_archive' => true,
|
|
'show_in_menu' => 'bd-bookshelf',
|
|
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'),
|
|
'rewrite' => array('slug' => 'book'),
|
|
'menu_icon' => 'dashicons-book',
|
|
'show_in_rest' => true,
|
|
));
|
|
}
|
|
|
|
public function register_taxonomies() {
|
|
register_taxonomy('series', 'book', array(
|
|
'labels' => array(
|
|
'name' => __('Series', 'bd-bookshelfhelper'),
|
|
'singular_name' => __('Series', 'bd-bookshelfhelper'),
|
|
'add_new_item' => __('Add New Series', 'bd-bookshelfhelper'),
|
|
'new_item_name' => __('New Series Name', 'bd-bookshelfhelper'),
|
|
),
|
|
'hierarchical' => true,
|
|
'public' => true,
|
|
'show_admin_column' => true,
|
|
'rewrite' => array('slug' => 'series'),
|
|
'show_in_rest' => true,
|
|
));
|
|
}
|
|
|
|
public function register_shortlinks() {
|
|
add_rewrite_rule('^([a-z0-9-]+)/?$', 'index.php?book_slug=$matches[1]', 'top');
|
|
add_rewrite_rule('^([a-z0-9-]+)r([a-z])$', 'index.php?book_slug=$matches[1]&review=$matches[2]', 'top');
|
|
}
|
|
|
|
public function filter_series_menu_item($items, $args) {
|
|
if ($args->theme_location !== 'primary') {
|
|
return $items;
|
|
}
|
|
|
|
$series_count = self::series_count();
|
|
if ($series_count > 0) {
|
|
$series_item = '<li class="menu-item series-item"><a href="' . home_url('/series/') . '">' . __('Series', 'bd-bookshelfhelper') . '</a></li>';
|
|
|
|
if (preg_match('/(Books<li.*?<\/li>)/s', $items, $matches)) {
|
|
$items = str_replace($matches[1], $matches[1] . $series_item, $items);
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function output_seo_tags() {
|
|
if (!is_singular('book')) {
|
|
return;
|
|
}
|
|
|
|
$book_id = get_the_ID();
|
|
|
|
$title = get_post_meta($book_id, 'seo_title', true);
|
|
if (!$title) {
|
|
$title = get_the_title($book_id) . ' | ' . get_bloginfo('name');
|
|
}
|
|
|
|
$description = get_post_meta($book_id, 'seo_description', true);
|
|
if (!$description) {
|
|
$excerpt = get_post_meta($book_id, 'blurb_excerpt', true);
|
|
$description = substr($excerpt, 0, 160);
|
|
}
|
|
|
|
echo '<meta name="description" content="' . esc_attr($description) . '">' . "\n";
|
|
echo '<meta property="og:title" content="' . esc_attr($title) . '">' . "\n";
|
|
echo '<meta property="og:description" content="' . esc_attr($description) . '">' . "\n";
|
|
echo '<meta property="og:type" content="book">' . "\n";
|
|
echo '<meta name="twitter:card" content="summary_large_image">' . "\n";
|
|
|
|
$schema = self::generate_schema($book_id);
|
|
echo '<script type="application/ld+json">' . json_encode($schema) . '</script>' . "\n";
|
|
}
|
|
|
|
public function generate_schema($book_id) {
|
|
$book = get_post($book_id);
|
|
|
|
$schema = array(
|
|
'@context' => 'https://schema.org',
|
|
'@type' => 'Book',
|
|
'name' => $book->post_title,
|
|
'author' => array(
|
|
'@type' => 'Person',
|
|
'name' => get_bloginfo('name'),
|
|
),
|
|
);
|
|
|
|
$asin = get_post_meta($book_id, 'asin', true);
|
|
if ($asin) {
|
|
$schema['isbn'] = $asin;
|
|
}
|
|
|
|
$cover_id = get_post_thumbnail_id($book_id);
|
|
if ($cover_id) {
|
|
$cover_url = wp_get_attachment_url($cover_id);
|
|
$schema['image'] = $cover_url;
|
|
}
|
|
|
|
$description = get_post_meta($book_id, 'blurb', true);
|
|
if ($description) {
|
|
$schema['description'] = $description;
|
|
}
|
|
|
|
$series_term = get_the_terms($book_id, 'series');
|
|
if ($series_term && !is_wp_error($series_term)) {
|
|
$schema['isPartOf'] = array(
|
|
'@type' => 'BookSeries',
|
|
'name' => $series_term[0]->name,
|
|
);
|
|
}
|
|
|
|
return $schema;
|
|
}
|
|
|
|
public static function get_featured_books($limit = 5) {
|
|
$args = array(
|
|
'post_type' => 'book',
|
|
'posts_per_page' => $limit,
|
|
'post_status' => 'publish',
|
|
'orderby' => 'meta_value',
|
|
'meta_key' => 'publish_date',
|
|
'order' => 'DESC',
|
|
);
|
|
|
|
$query = new WP_Query($args);
|
|
return $query->posts;
|
|
}
|
|
|
|
public static function get_cover_url($book_id, $size = 'thumbnail', $lang = 'en') {
|
|
$meta_key = 'cover_' . $lang;
|
|
$cover_id = get_post_meta($book_id, $meta_key, true);
|
|
|
|
if (!$cover_id) {
|
|
$cover_id = get_post_thumbnail_id($book_id);
|
|
}
|
|
|
|
if ($cover_id) {
|
|
$image = wp_get_attachment_image_url($cover_id, $size);
|
|
if ($image) {
|
|
return $image;
|
|
}
|
|
}
|
|
|
|
return BD_BHS_URI . 'assets/default-cover.png';
|
|
}
|
|
|
|
public static function get_book_url($book_id) {
|
|
$slug = get_post_meta($book_id, 'short_link_slug', true);
|
|
if ($slug) {
|
|
return home_url('/' . $slug);
|
|
}
|
|
return get_permalink($book_id);
|
|
}
|
|
|
|
public static function get_ratings($book_id) {
|
|
$ratings = array();
|
|
|
|
$amazon_rating = get_post_meta($book_id, 'amazon_rating', true);
|
|
if ($amazon_rating) {
|
|
$ratings['amazon'] = $amazon_rating;
|
|
}
|
|
|
|
$goodreads_rating = get_post_meta($book_id, 'goodreads_rating', true);
|
|
if ($goodreads_rating) {
|
|
$ratings['goodreads'] = $goodreads_rating;
|
|
}
|
|
|
|
return $ratings;
|
|
}
|
|
|
|
public static function get_retailer_links($book_id, $format = 'ebook') {
|
|
$links = array();
|
|
|
|
$retailers = array(
|
|
'amazon' => array(
|
|
'name' => 'Amazon',
|
|
'base_url' => 'https://www.amazon.com/dp/',
|
|
'param' => '?tag=',
|
|
),
|
|
'apple' => array(
|
|
'name' => 'Apple Books',
|
|
'base_url' => 'https://books.apple.com/',
|
|
'param' => '?at=',
|
|
),
|
|
'barnes' => array(
|
|
'name' => 'Barnes & Noble',
|
|
'base_url' => 'https://www.barnesandnoble.com/',
|
|
'param' => '?aid=',
|
|
),
|
|
'kobo' => array(
|
|
'name' => 'Kobo',
|
|
'base_url' => 'https://www.kobo.com/',
|
|
'param' => '?aid=',
|
|
),
|
|
'google' => array(
|
|
'name' => 'Google Play',
|
|
'base_url' => 'https://play.google.com/store/apps/details?id=',
|
|
'param' => '&partnerCode=',
|
|
),
|
|
);
|
|
|
|
$affiliates = get_option('bdm_affiliate_codes', array());
|
|
|
|
foreach ($retailers as $key => $retailer) {
|
|
$url_meta = $format . '_url_' . $key;
|
|
$url = get_post_meta($book_id, $url_meta, true);
|
|
|
|
if (!$url && $key === 'amazon') {
|
|
$asin = get_post_meta($book_id, 'asin', true);
|
|
if ($asin) {
|
|
$affiliate = isset($affiliates[$key]) ? $affiliates[$key] : '';
|
|
$url = $retailer['base_url'] . $asin . $retailer['param'] . $affiliate;
|
|
}
|
|
}
|
|
|
|
if ($url) {
|
|
$links[] = array(
|
|
'name' => $retailer['name'],
|
|
'url' => $url,
|
|
'retailer' => $key,
|
|
);
|
|
}
|
|
}
|
|
|
|
return $links;
|
|
}
|
|
|
|
public static function display_retailer_links($book_id, $format = 'ebook') {
|
|
$links = self::get_retailer_links($book_id, $format);
|
|
|
|
foreach ($links as $link) {
|
|
echo '<a href="' . esc_url($link['url']) . '" class="retailer-link retailer-' . esc_attr($link['retailer']) . '" target="_blank" rel="noopener noreferrer">' . esc_html($link['name']) . '</a>';
|
|
}
|
|
}
|
|
|
|
public static function get_localized_title($book_id, $lang = 'en') {
|
|
$title = get_post_meta($book_id, 'title_' . $lang, true);
|
|
if (!$title) {
|
|
$title = get_the_title($book_id);
|
|
}
|
|
return $title;
|
|
}
|
|
|
|
public static function get_localized_blurb($book_id, $lang = 'en') {
|
|
return get_post_meta($book_id, 'blurb_' . $lang, true);
|
|
}
|
|
|
|
public static function get_blurb_excerpt($book_id, $length = 150) {
|
|
$blurb = get_post_meta($book_id, 'blurb', true);
|
|
if ($blurb) {
|
|
return substr($blurb, 0, $length) . '...';
|
|
}
|
|
return '';
|
|
}
|
|
|
|
public static function get_preview_excerpt($book_id, $lang = 'en') {
|
|
return get_post_meta($book_id, 'preview_excerpt_' . $lang, true);
|
|
}
|
|
|
|
public static function get_pull_quotes($book_id, $lang = 'en') {
|
|
$quotes = array();
|
|
for ($i = 1; $i <= 5; $i++) {
|
|
$quote = get_post_meta($book_id, 'pull_quote_' . $lang . '_' . $i, true);
|
|
if ($quote) {
|
|
$quotes[] = $quote;
|
|
}
|
|
}
|
|
return $quotes;
|
|
}
|
|
|
|
public static function get_reviews($book_id) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'bd_reviews';
|
|
|
|
$reviews = $wpdb->get_results($wpdb->prepare(
|
|
"SELECT * FROM {$table_name} WHERE book_id = %d ORDER BY display_order ASC",
|
|
$book_id
|
|
));
|
|
|
|
return $reviews ?: array();
|
|
}
|
|
|
|
public static function get_pen_name($book_id) {
|
|
$pen_name_id = get_post_meta($book_id, 'pen_name_id', true);
|
|
|
|
if ($pen_name_id) {
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'bd_pen_names';
|
|
$pen_name = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT pen_name FROM {$table_name} WHERE id = %d AND active = 1",
|
|
$pen_name_id
|
|
));
|
|
return $pen_name ?: 'B.D. Murphy';
|
|
}
|
|
|
|
return 'B.D. Murphy';
|
|
}
|
|
|
|
public static function get_series_info($book_id) {
|
|
$terms = get_the_terms($book_id, 'series');
|
|
|
|
if ($terms && !is_wp_error($terms)) {
|
|
$series = $terms[0];
|
|
$volume = get_post_meta($book_id, 'volume_number', true);
|
|
|
|
return array(
|
|
'id' => $series->term_id,
|
|
'name' => $series->name,
|
|
'slug' => $series->slug,
|
|
'volume' => $volume ?: 1,
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static function series_count() {
|
|
$terms = get_terms(array(
|
|
'taxonomy' => 'series',
|
|
'hide_empty' => true,
|
|
'fields' => 'count',
|
|
));
|
|
|
|
return $terms ?: 0;
|
|
}
|
|
|
|
public static function get_supported_languages() {
|
|
return array(
|
|
'en' => 'English',
|
|
'fr' => 'Fran\u00e7ais',
|
|
'de' => 'Deutsch',
|
|
'es' => 'Espa\u00f1ol',
|
|
'it' => 'Italiano',
|
|
'pt' => 'Portugu\u00eas',
|
|
'ru' => 'Russian',
|
|
'ja' => 'Japanese',
|
|
'zh' => 'Chinese',
|
|
);
|
|
}
|
|
|
|
public static function get_awards() {
|
|
return get_option('bdm_awards', array());
|
|
}
|
|
}
|