Files
Bookshelf/Old/bd-bookshelfhelper/inc/class-bookhelper.php
bdm 38cdebd0c6 Track Bookshelf WordPress theme, plugins and specs
Initial import of the bdmurphy Bookshelf work: the bd-bookshelf theme,
bd-bookshelf-plugin, bd-bookshelfhelper and bd-simpleemail plugins, plus
the specification documents, mockups and release archives.
2026-09-28 06:24:25 -07:00

396 lines
13 KiB
PHP

<?php
/**
* 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());
}
}