844 lines
27 KiB
PHP
844 lines
27 KiB
PHP
<?php
|
|
// Copyright 2026 Umbra Tools (umbratools.com)
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
/**
|
|
* Main Plugin Class
|
|
*
|
|
* @package BD_BookShelfHelper
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class BD_BookShelfHelper {
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Get instance
|
|
*
|
|
* @return BD_BookShelfHelper
|
|
*/
|
|
public static function instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init_hooks();
|
|
}
|
|
|
|
/**
|
|
* Initialize 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('init', array($this, 'register_shortcodes'));
|
|
add_action('template_redirect', array($this, 'handle_shortlink_requests'));
|
|
|
|
// Hook for future plugin integration
|
|
add_filter('bdm_nav_menu_items', array($this, 'filter_series_menu_item'), 10, 2);
|
|
|
|
if (is_admin()) {
|
|
BD_BookShelfHelper_Admin::instance();
|
|
}
|
|
|
|
// Schedule cron jobs on init
|
|
add_action('init', array($this, 'schedule_cron_events'));
|
|
|
|
// Cron callbacks
|
|
add_action('bd_bookshelf_goodreads_refresh', array($this, 'refresh_goodreads_data'));
|
|
add_action('bd_bookshelf_amazon_scrape', array($this, 'scrape_amazon_ratings'));
|
|
}
|
|
|
|
/**
|
|
* Register custom post type
|
|
*/
|
|
public function register_post_types() {
|
|
register_post_type('book', array(
|
|
'labels' => array(
|
|
'name' => esc_html__('Books', 'bd-bookshelfhelper'),
|
|
'singular_name' => esc_html__('Book', 'bd-bookshelfhelper'),
|
|
'add_new' => esc_html__('Add New', 'bd-bookshelfhelper'),
|
|
'add_new_item' => esc_html__('Add New Book', 'bd-bookshelfhelper'),
|
|
'edit_item' => esc_html__('Edit Book', 'bd-bookshelfhelper'),
|
|
'new_item' => esc_html__('New Book', 'bd-bookshelfhelper'),
|
|
'view_item' => esc_html__('View Book', 'bd-bookshelfhelper'),
|
|
'search_items' => esc_html__('Search Books', 'bd-bookshelfhelper'),
|
|
'not_found' => esc_html__('No books found', 'bd-bookshelfhelper'),
|
|
'not_found_in_trash' => esc_html__('No books found in trash', '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-alt',
|
|
'show_in_rest' => true,
|
|
'capability_type' => 'post',
|
|
'map_meta_cap' => true,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Register taxonomies
|
|
*/
|
|
public function register_taxonomies() {
|
|
register_taxonomy('series', 'book', array(
|
|
'labels' => array(
|
|
'name' => esc_html__('Series', 'bd-bookshelfhelper'),
|
|
'singular_name' => esc_html__('Series', 'bd-bookshelfhelper'),
|
|
'search_items' => esc_html__('Search Series', 'bd-bookshelfhelper'),
|
|
'all_items' => esc_html__('All Series', 'bd-bookshelfhelper'),
|
|
'edit_item' => esc_html__('Edit Series', 'bd-bookshelfhelper'),
|
|
'update_item' => esc_html__('Update Series', 'bd-bookshelfhelper'),
|
|
'add_new_item' => esc_html__('Add New Series', 'bd-bookshelfhelper'),
|
|
'new_item_name' => esc_html__('New Series Name', 'bd-bookshelfhelper'),
|
|
'not_found' => esc_html__('No series found', 'bd-bookshelfhelper'),
|
|
),
|
|
'hierarchical' => true,
|
|
'public' => true,
|
|
'show_admin_column' => true,
|
|
'rewrite' => array('slug' => 'series'),
|
|
'show_in_rest' => true,
|
|
'show_ui' => true,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Register rewrite rules for short links
|
|
*/
|
|
public function register_shortlinks() {
|
|
add_rewrite_rule('^([a-z0-9]{2,6})/?$', 'index.php?book_short_slug=$matches[1]', 'top');
|
|
add_rewrite_rule('^([a-z0-9]{2,6})r([agbckpl])/?$', 'index.php?book_short_slug=$matches[1]&review_site=$matches[2]', 'top');
|
|
}
|
|
|
|
/**
|
|
* Handle short link requests
|
|
*/
|
|
public function handle_shortlink_requests() {
|
|
$short_slug = get_query_var('book_short_slug');
|
|
|
|
if (empty($short_slug)) {
|
|
return;
|
|
}
|
|
|
|
global $wpdb;
|
|
$book_id = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'short_link_slug' AND meta_value = %s",
|
|
$short_slug
|
|
));
|
|
|
|
if ($book_id) {
|
|
wp_redirect(get_permalink($book_id), 301);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filter series menu item visibility
|
|
*
|
|
* @param string $items Menu items HTML.
|
|
* @param stdClass $args Menu args.
|
|
* @return string
|
|
*/
|
|
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 menu-item-type-taxonomy series-menu-item"><a href="' . home_url('/series/') . '">' . esc_html__('Series', 'bd-bookshelfhelper') . '</a></li>';
|
|
|
|
if (preg_match('/(<li[^>]*>.*?Books.*?<\/li>)/s', $items, $matches)) {
|
|
$books_item = $matches[1];
|
|
$items = str_replace($books_item, $books_item . $series_item, $items);
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
/**
|
|
* Schedule cron events
|
|
*/
|
|
public function schedule_cron_events() {
|
|
if (!wp_next_scheduled('bd_bookshelf_goodreads_refresh')) {
|
|
wp_schedule_event(time(), 'daily', 'bd_bookshelf_goodreads_refresh');
|
|
}
|
|
|
|
if (!wp_next_scheduled('bd_bookshelf_amazon_scrape')) {
|
|
wp_schedule_event(time(), 'six_hours', 'bd_bookshelf_amazon_scrape');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refresh Goodreads data
|
|
*/
|
|
public function refresh_goodreads_data() {
|
|
// Implementation for Goodreads API refresh
|
|
}
|
|
|
|
/**
|
|
* Scrape Amazon ratings
|
|
*/
|
|
public function scrape_amazon_ratings() {
|
|
// Implementation for Amazon rating scrape
|
|
}
|
|
|
|
/**
|
|
* Register shortcodes
|
|
*/
|
|
public function register_shortcodes() {
|
|
add_shortcode('bd-book', array($this, 'shortcode_book'));
|
|
add_shortcode('bd-books', array($this, 'shortcode_books_list'));
|
|
}
|
|
|
|
/**
|
|
* Shortcode: [bd-book]
|
|
* Display single book by ID or title
|
|
*/
|
|
public function shortcode_book($atts) {
|
|
$atts = shortcode_atts(array(
|
|
'book' => '',
|
|
), $atts);
|
|
|
|
if (empty($atts['book'])) {
|
|
return '<p class="bd-book-error">Please specify a book ID or title in the shortcode.</p>';
|
|
}
|
|
|
|
$book_id = intval($atts['book']);
|
|
if ($book_id > 0) {
|
|
$book = get_post($book_id);
|
|
} else {
|
|
$books = get_posts(array(
|
|
'post_type' => 'book',
|
|
'title' => $atts['book'],
|
|
'posts_per_page' => 1,
|
|
));
|
|
$book = !empty($books) ? $books[0] : null;
|
|
}
|
|
|
|
if (!$book || 'book' !== $book->post_type) {
|
|
return '<p class="bd-book-error">Book not found.</p>';
|
|
}
|
|
|
|
return $this->render_book_display($book);
|
|
}
|
|
|
|
/**
|
|
* Shortcode: [bd-books]
|
|
* Display all books in a list
|
|
*/
|
|
public function shortcode_books_list($atts) {
|
|
$atts = shortcode_atts(array(
|
|
'limit' => -1,
|
|
'order' => 'title',
|
|
), $atts);
|
|
|
|
$books = get_posts(array(
|
|
'post_type' => 'book',
|
|
'posts_per_page' => intval($atts['limit']),
|
|
'post_status' => 'publish',
|
|
'orderby' => 'title',
|
|
'order' => 'ASC',
|
|
));
|
|
|
|
if (empty($books)) {
|
|
return '<p class="bd-book-error">No books available.</p>';
|
|
}
|
|
|
|
$output = '<div class="bd-books-list">';
|
|
foreach ($books as $book) {
|
|
$output .= $this->render_book_display($book);
|
|
}
|
|
$output .= '</div>';
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Render book display HTML
|
|
*
|
|
* @param WP_Post $book Book post object.
|
|
* @return string
|
|
*/
|
|
private function render_book_display($book) {
|
|
$cover_url = BD_BookShelfHelper::get_cover_url($book->ID, 'medium');
|
|
$pen_name = BD_BookShelfHelper::get_pen_name($book->ID);
|
|
$blurb = BD_BookShelfHelper::get_localized_blurb($book->ID);
|
|
$links = BD_BookShelfHelper::get_retailer_links($book->ID);
|
|
|
|
ob_start();
|
|
?>
|
|
<div class="bd-book-listing">
|
|
<div class="book-entry" style="display: flex; gap: 2rem; margin-bottom: 2rem; padding-bottom: 2rem; border-bottom: 1px solid #ccc;">
|
|
<?php if ($cover_url): ?>
|
|
<div class="book-cover" style="flex: 0 0 150px;">
|
|
<img src="<?php echo esc_url($cover_url); ?>" alt="<?php echo esc_attr($book->post_title); ?> Cover" style="width: 100%; max-width: 150px; height: auto; box-shadow: 0 4px 8px rgba(0,0,0,0.2);">
|
|
</div>
|
|
<?php endif; ?>
|
|
<div class="book-details" style="flex: 1;">
|
|
<h2 style="margin-top: 0; margin-bottom: 0.5rem;"><?php echo esc_html($book->post_title); ?></h2>
|
|
<p style="margin: 0 0 1rem 0; color: #666;">by <?php echo esc_html($pen_name); ?></p>
|
|
<?php if ($blurb): ?>
|
|
<div class="book-description" style="margin-bottom: 1rem; padding: 1rem; border: 1px solid #e0e0e0; background: #f9f9f9; border-radius: 4px;">
|
|
<?php echo wp_kses_post(wp_trim_words($blurb, 50)); ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if (!empty($links)): ?>
|
|
<div class="book-buttons" style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
<?php foreach ($links as $link): ?>
|
|
<a href="<?php echo esc_url($link['url']); ?>" target="_blank" rel="noopener" class="bd-book-btn" style="background-color: #FF9900; color: #000; padding: 0.5rem 1rem; text-decoration: none; border-radius: 4px; font-weight: bold;"><?php echo esc_html($link['name']); ?></a>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Migrate books from BD Books plugin
|
|
*
|
|
* @return array Migration result
|
|
*/
|
|
public static function migrate_from_bd_books() {
|
|
global $wpdb;
|
|
$old_table = $wpdb->prefix . 'bd_books';
|
|
|
|
// Check if old table exists
|
|
$table_exists = $wpdb->get_var("SHOW TABLES LIKE '$old_table'") === $old_table;
|
|
if (!$table_exists) {
|
|
return array('success' => false, 'message' => 'Old BD Books table not found.');
|
|
}
|
|
|
|
$old_books = $wpdb->get_results("SELECT * FROM {$old_table}");
|
|
if (empty($old_books)) {
|
|
return array('success' => true, 'message' => 'No books to migrate.', 'count' => 0);
|
|
}
|
|
|
|
$migrated = 0;
|
|
|
|
foreach ($old_books as $old_book) {
|
|
// Check if already migrated (by title)
|
|
$existing = get_posts(array(
|
|
'post_type' => 'book',
|
|
'title' => $old_book->title,
|
|
'posts_per_page' => 1,
|
|
));
|
|
|
|
if (!empty($existing)) {
|
|
continue;
|
|
}
|
|
|
|
// Create new book post
|
|
$post_id = wp_insert_post(array(
|
|
'post_type' => 'book',
|
|
'post_title' => sanitize_text_field($old_book->title),
|
|
'post_content' => '',
|
|
'post_status' => 'publish',
|
|
'post_author' => get_current_user_id(),
|
|
'post_date' => $old_book->created_at,
|
|
));
|
|
|
|
if (is_wp_error($post_id)) {
|
|
continue;
|
|
}
|
|
|
|
// Set author/pen name
|
|
if (!empty($old_book->author)) {
|
|
// Try to find or create pen name
|
|
global $wpdb;
|
|
$pen_names_table = $wpdb->prefix . 'bd_pen_names';
|
|
$pen_name_id = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT id FROM {$pen_names_table} WHERE pen_name = %s",
|
|
$old_book->author
|
|
));
|
|
|
|
if ($pen_name_id) {
|
|
update_post_meta($post_id, 'pen_name_id', $pen_name_id);
|
|
} else {
|
|
// Insert new pen name
|
|
$wpdb->insert($pen_names_table, array(
|
|
'pen_name' => sanitize_text_field($old_book->author),
|
|
'active' => 1,
|
|
'created_at' => current_time('mysql'),
|
|
));
|
|
$new_pen_id = $wpdb->insert_id;
|
|
if ($new_pen_id) {
|
|
update_post_meta($post_id, 'pen_name_id', $new_pen_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set description/blurb
|
|
if (!empty($old_book->description)) {
|
|
update_post_meta($post_id, 'blurb_en', wp_kses_post($old_book->description));
|
|
}
|
|
|
|
// Set cover image
|
|
if (!empty($old_book->cover_image_id)) {
|
|
set_post_thumbnail($post_id, intval($old_book->cover_image_id));
|
|
} elseif (!empty($old_book->cover_image_url)) {
|
|
// Download and attach external image
|
|
$attachment_id = self::import_external_image($old_book->cover_image_url, $old_book->title);
|
|
if ($attachment_id) {
|
|
set_post_thumbnail($post_id, $attachment_id);
|
|
}
|
|
}
|
|
|
|
// Parse and set purchase links
|
|
$links = json_decode($old_book->purchase_links, true);
|
|
if (!empty($links) && is_array($links)) {
|
|
foreach ($links as $link) {
|
|
$label = isset($link['label']) ? strtolower($link['label']) : '';
|
|
$url = isset($link['url']) ? $link['url'] : '';
|
|
|
|
if (strpos($label, 'amazon') !== false) {
|
|
update_post_meta($post_id, 'ebook_url_amazon', esc_url_raw($url));
|
|
} elseif (strpos($label, 'apple') !== false) {
|
|
update_post_meta($post_id, 'ebook_url_apple', esc_url_raw($url));
|
|
} elseif (strpos($label, 'barnes') !== false || strpos($label, 'b&n') !== false) {
|
|
update_post_meta($post_id, 'ebook_url_barnes', esc_url_raw($url));
|
|
} elseif (strpos($label, 'kobo') !== false) {
|
|
update_post_meta($post_id, 'ebook_url_kobo', esc_url_raw($url));
|
|
} elseif (strpos($label, 'google') !== false) {
|
|
update_post_meta($post_id, 'ebook_url_google', esc_url_raw($url));
|
|
}
|
|
}
|
|
}
|
|
|
|
$migrated++;
|
|
}
|
|
|
|
return array(
|
|
'success' => true,
|
|
'message' => sprintf('Migrated %d books.', $migrated),
|
|
'count' => $migrated,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Import external image as attachment
|
|
*
|
|
* @param string $url Image URL.
|
|
* @param string $title Image title.
|
|
* @return int|false Attachment ID
|
|
*/
|
|
private static function import_external_image($url, $title) {
|
|
require_once(ABSPATH . 'wp-admin/includes/file.php');
|
|
require_once(ABSPATH . 'wp-admin/includes/media.php');
|
|
require_once(ABSPATH . 'wp-admin/includes/image.php');
|
|
|
|
$tmp = download_url($url);
|
|
if (is_wp_error($tmp)) {
|
|
return false;
|
|
}
|
|
|
|
$file_array = array(
|
|
'name' => sanitize_file_name($title) . '.jpg',
|
|
'tmp_name' => $tmp,
|
|
);
|
|
|
|
$id = media_handle_sideload($file_array, 0, $title);
|
|
@unlink($tmp);
|
|
|
|
if (is_wp_error($id)) {
|
|
return false;
|
|
}
|
|
|
|
return $id;
|
|
}
|
|
|
|
// =====================
|
|
// Static Helper Methods
|
|
// =====================
|
|
|
|
/**
|
|
* Get featured books
|
|
*
|
|
* @param int $limit Number of books to return.
|
|
* @return array
|
|
*/
|
|
public static function get_featured_books($limit = 5) {
|
|
$query = new WP_Query(array(
|
|
'post_type' => 'book',
|
|
'posts_per_page' => $limit,
|
|
'post_status' => 'publish',
|
|
'orderby' => 'meta_value',
|
|
'meta_key' => 'publish_date',
|
|
'order' => 'DESC',
|
|
));
|
|
|
|
return $query->posts;
|
|
}
|
|
|
|
/**
|
|
* Get cover URL for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $size Image size.
|
|
* @param string $lang Language code.
|
|
* @return string
|
|
*/
|
|
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/images/default-cover.png';
|
|
}
|
|
|
|
/**
|
|
* Get book URL (short link)
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return string
|
|
*/
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Get ratings for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return array
|
|
*/
|
|
public static function get_ratings($book_id) {
|
|
$ratings = array();
|
|
|
|
$amazon = get_post_meta($book_id, 'amazon_rating', true);
|
|
if ($amazon) {
|
|
$ratings['amazon'] = $amazon;
|
|
}
|
|
|
|
$goodreads = get_post_meta($book_id, 'goodreads_rating', true);
|
|
if ($goodreads) {
|
|
$ratings['goodreads'] = $goodreads;
|
|
}
|
|
|
|
return $ratings;
|
|
}
|
|
|
|
/**
|
|
* Get retailer links for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $format Format (ebook, paperback, hardback).
|
|
* @return array
|
|
*/
|
|
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/'),
|
|
'apple' => array('name' => 'Apple Books', 'base_url' => 'https://books.apple.com/'),
|
|
'barnes' => array('name' => 'Barnes & Noble', 'base_url' => 'https://www.barnesandnoble.com/'),
|
|
'kobo' => array('name' => 'Kobo', 'base_url' => 'https://www.kobo.com/'),
|
|
'google' => array('name' => 'Google Play', 'base_url' => 'https://play.google.com/store/apps/details?id='),
|
|
);
|
|
|
|
$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 . '?tag=' . $affiliate;
|
|
}
|
|
}
|
|
|
|
if ($url) {
|
|
$links[] = array(
|
|
'name' => $retailer['name'],
|
|
'url' => $url,
|
|
'retailer' => $key,
|
|
);
|
|
}
|
|
}
|
|
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Display retailer links
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $format Format.
|
|
*/
|
|
public static function display_retailer_links($book_id, $format = 'ebook') {
|
|
$links = self::get_retailer_links($book_id, $format);
|
|
|
|
foreach ($links as $link) {
|
|
printf(
|
|
'<a href="%s" class="retailer-link retailer-%s" target="_blank" rel="noopener noreferrer">%s</a>',
|
|
esc_url($link['url']),
|
|
esc_attr($link['retailer']),
|
|
esc_html($link['name'])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get localized title
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $lang Language code.
|
|
* @return string
|
|
*/
|
|
public static function get_localized_title($book_id, $lang = 'en') {
|
|
$title = get_post_meta($book_id, 'title_' . $lang, true);
|
|
return $title ?: get_the_title($book_id);
|
|
}
|
|
|
|
/**
|
|
* Get localized blurb
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $lang Language code.
|
|
* @return string
|
|
*/
|
|
public static function get_localized_blurb($book_id, $lang = 'en') {
|
|
return get_post_meta($book_id, 'blurb_' . $lang, true) ?: '';
|
|
}
|
|
|
|
/**
|
|
* Get preview excerpt
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $lang Language code.
|
|
* @return string
|
|
*/
|
|
public static function get_preview_excerpt($book_id, $lang = 'en') {
|
|
return get_post_meta($book_id, 'preview_excerpt_' . $lang, true) ?: '';
|
|
}
|
|
|
|
/**
|
|
* Get pull quotes
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @param string $lang Language code.
|
|
* @return array
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Get reviews for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return array
|
|
*/
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* Get pen name for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return string
|
|
*/
|
|
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';
|
|
}
|
|
|
|
/**
|
|
* Get series info for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return array|null
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Count series
|
|
*
|
|
* @return int
|
|
*/
|
|
public static function series_count() {
|
|
$terms = get_terms(array(
|
|
'taxonomy' => 'series',
|
|
'hide_empty' => true,
|
|
'fields' => 'count',
|
|
));
|
|
|
|
return $terms ?: 0;
|
|
}
|
|
|
|
/**
|
|
* Get supported languages
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_supported_languages() {
|
|
return array(
|
|
'en' => 'English',
|
|
'fr' => 'Français',
|
|
'de' => 'Deutsch',
|
|
'es' => 'Español',
|
|
'it' => 'Italiano',
|
|
'pt' => 'Português',
|
|
'ru' => 'Русский',
|
|
'ja' => '日本語',
|
|
'zh' => '中文',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get awards list
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_awards() {
|
|
return get_option('bdm_awards', array());
|
|
}
|
|
|
|
/**
|
|
* Generate SEO tags for a book
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
*/
|
|
public static function output_seo_tags($book_id) {
|
|
$title = get_post_meta($book_id, 'seo_title', true) ?: get_the_title($book_id) . ' | ' . get_bloginfo('name');
|
|
$description = get_post_meta($book_id, 'seo_description', true) ?: get_post_meta($book_id, 'blurb', true);
|
|
|
|
if ($description) {
|
|
$description = substr($description, 0, 160);
|
|
}
|
|
|
|
printf('<meta name="description" content="%s">' . "\n", esc_attr($description));
|
|
printf('<meta property="og:title" content="%s">' . "\n", esc_attr($title));
|
|
printf('<meta property="og:description" content="%s">' . "\n", esc_attr($description));
|
|
printf('<meta property="og:type" content="book">' . "\n");
|
|
printf('<meta name="twitter:card" content="summary_large_image">' . "\n");
|
|
}
|
|
|
|
/**
|
|
* Generate schema.org data
|
|
*
|
|
* @param int $book_id Book post ID.
|
|
* @return array
|
|
*/
|
|
public static 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;
|
|
}
|
|
|
|
$blurb = get_post_meta($book_id, 'blurb', true);
|
|
if ($blurb) {
|
|
$schema['description'] = $blurb;
|
|
}
|
|
|
|
$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;
|
|
}
|
|
}
|