Files
Bookshelf/Old/bd-bookshelfhelper/inc/class-admin.php
2026-10-18 19:01:01 -07:00

484 lines
23 KiB
PHP

<?php
// Copyright 2026 Umbra Tools (umbratools.com)
// SPDX-License-Identifier: Apache-2.0
/**
* Admin Class
*/
if (!defined('ABSPATH')) {
exit;
}
class BD_BookShelfHelper_Admin {
private static $instance = null;
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_action('admin_menu', array($this, 'add_menu_pages'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_assets'));
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('save_post', array($this, 'save_meta_boxes'));
add_action('wp_ajax_bdm_update_links', array($this, 'ajax_update_links'));
}
public function add_menu_pages() {
add_menu_page(
__('BD BookHelper', 'bd-bookshelfhelper'),
__('BD BookHelper', 'bd-bookshelfhelper'),
'manage_options',
'bd-bookshelf',
'',
'dashicons-book',
30
);
add_submenu_page(
'bd-bookshelf',
__('Books', 'bd-bookshelfhelper'),
__('All Books', 'bd-bookshelfhelper'),
'manage_options',
'edit.php?post_type=book'
);
add_submenu_page(
'bd-bookshelf',
__('Add Book', 'bd-bookshelfhelper'),
__('Add Book', 'bd-bookshelfhelper'),
'manage_options',
'post-new.php?post_type=book'
);
add_submenu_page(
'bd-bookshelf',
__('Series', 'bd-bookshelfhelper'),
__('Series', 'bd-bookshelfhelper'),
'manage_options',
'edit-tags.php?taxonomy=series&post_type=book'
);
add_submenu_page(
'bd-bookshelf',
__('Pen Names', 'bd-bookshelfhelper'),
__('Pen Names', 'bd-bookshelfhelper'),
'manage_options',
'bd-bookshelf-pen-names',
array($this, 'pen_names_page')
);
add_submenu_page(
'bd-bookshelf',
__('Settings', 'bd-bookshelfhelper'),
__('Settings', 'bd-bookshelfhelper'),
'manage_options',
'bd-bookshelf-settings',
array($this, 'settings_page')
);
}
public function enqueue_assets($hook) {
$screen = get_current_screen();
if ($screen->post_type === 'book' || $screen->taxonomy === 'series') {
wp_enqueue_media();
}
}
public function add_meta_boxes() {
add_meta_box('bd_book_details', __('Book Details', array($this, 'book_details_meta_box'), 'book', 'normal', 'high');
add_meta_box('bd_book_seo', __('SEO Settings', array($this, 'book_seo_meta_box'), 'book', 'side', 'low');
}
public function book_details_meta_box($post) {
$languages = BD_BookShelfHelper::get_supported_languages();
wp_nonce_field('bd_book_meta', 'bd_book_nonce');
echo '<div class="bd-book-meta-box">';
echo '<h3>' . esc_html__('Core Fields', 'bd-bookshelfhelper') . '</h3>';
echo '<p><label>' . esc_html__('Pen Name', 'bd-bookshelfhelper') . '</label>';
echo '<select name="pen_name_id">';
echo '<option value="">' . esc_html__('B.D. Murphy', 'bd-bookshelfhelper') . '</option>';
global $wpdb;
$table_name = $wpdb->prefix . 'bd_pen_names';
$pen_names = $wpdb->get_results("SELECT id, pen_name FROM {$table_name} WHERE active = 1 ORDER BY pen_name");
$pen_name_id = get_post_meta($post->ID, 'pen_name_id', true);
if ($pen_names) {
foreach ($pen_names as $pn) {
echo '<option value="' . esc_attr($pn->id) . '"' . selected($pen_name_id, $pn->id, false) . '>' . esc_html($pn->pen_name) . '</option>';
}
}
echo '</select></p>';
echo '<p><label>' . esc_html__('ASIN (Amazon)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="asin" value="' . esc_attr(get_post_meta($post->ID, 'asin', true)) . '" class="regular-text"></p>';
echo '<p><label>' . esc_html__('ISBN-13 (Print)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="isbn" value="' . esc_attr(get_post_meta($post->ID, 'isbn', true)) . '" class="regular-text"></p>';
echo '<p><label>' . esc_html__('Short Link Slug', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="short_link_slug" value="' . esc_attr(get_post_meta($post->ID, 'short_link_slug', true)) . '" class="regular-text">';
echo '<p class="description">' . esc_html__('Auto-generated if empty', 'bd-bookshelfhelper') . '</p></p>';
$series_terms = get_terms(array('taxonomy' => 'series', 'hide_empty' => false));
if ($series_terms) {
echo '<p><label>' . esc_html__('Series', 'bd-bookshelfhelper') . '</label>';
echo '<select name="series_id">';
echo '<option value="">' . esc_html__('None / Standalone', 'bd-bookshelfhelper') . '</option>';
$current_series = get_the_terms($post->ID, 'series');
$current_series_id = $current_series && !is_wp_error($current_series) ? $current_series[0]->term_id : 0;
foreach ($series_terms as $term) {
echo '<option value="' . esc_attr($term->term_id) . '"' . selected($current_series_id, $term->term_id, false) . '>' . esc_html($term->name) . '</option>';
}
echo '</select></p>';
if ($current_series_id) {
echo '<p><label>' . esc_html__('Volume Number', 'bd-bookshelfhelper') . '</label>';
echo '<input type="number" name="volume_number" value="' . esc_attr(get_post_meta($post->ID, 'volume_number', true)) . '" min="1"></p>';
}
}
echo '<h3>' . esc_html__('Language Content', 'bd-bookshelfhelper') . '</h3>';
foreach ($languages as $code => $name) {
echo '<fieldset class="bd-lang-fieldset">';
echo '<legend>' . esc_html($name) . ' (' . strtoupper($code) . ')</legend>';
echo '<p><label>' . esc_html__('Title', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="title_' . esc_attr($code) . '" value="' . esc_attr(get_post_meta($post->ID, 'title_' . $code, true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Back Cover Blurb', 'bd-bookshelfhelper') . '</label>';
echo '<textarea name="blurb_' . esc_attr($code) . '" rows="4" class="large-text">' . esc_textarea(get_post_meta($post->ID, 'blurb_' . $code, true)) . '</textarea></p>';
echo '<p><label>' . esc_html__('Preview Excerpt', 'bd-bookshelfhelper') . '</label>';
echo '<textarea name="preview_excerpt_' . esc_attr($code) . '" rows="3" class="large-text">' . esc_textarea(get_post_meta($post->ID, 'preview_excerpt_' . $code, true)) . '</textarea></p>';
echo '<p><label>' . esc_html__('Pull Quotes', 'bd-bookshelfhelper') . ' (1-5)</label>';
for ($i = 1; $i <= 5; $i++) {
echo '<input type="text" name="pull_quote_' . esc_attr($code) . '_' . $i . '" value="' . esc_attr(get_post_meta($post->ID, 'pull_quote_' . $code . '_' . $i, true)) . '" class="large-text" placeholder="' . esc_attr__('Quote ' . $i, 'bd-bookshelfhelper') . '">';
}
echo '</p>';
echo '<p><label>' . esc_html__('Cover Image', 'bd-bookshelfhelper') . '</label>';
$cover_id = get_post_meta($post->ID, 'cover_' . $code, true);
echo '<input type="hidden" name="cover_' . esc_attr($code) . '" value="' . esc_attr($cover_id) . '">';
echo '<button class="button bd-cover-upload" data-target="cover_' . esc_attr($code) . '">' . esc_html__('Upload Cover', 'bd-bookshelfhelper') . '</button>';
if ($cover_id) {
echo wp_get_attachment_image($cover_id, 'thumbnail');
}
echo '</p>';
echo '</fieldset>';
}
echo '<h3>' . esc_html__('Retailer Links', 'bd-bookshelfhelper') . '</h3>';
echo '<p><label>' . esc_html__('Amazon (Ebook)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="ebook_url_amazon" value="' . esc_attr(get_post_meta($post->ID, 'ebook_url_amazon', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Apple Books', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="ebook_url_apple" value="' . esc_attr(get_post_meta($post->ID, 'ebook_url_apple', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Google Play', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="ebook_url_google" value="' . esc_attr(get_post_meta($post->ID, 'ebook_url_google', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Barnes & Noble (Ebook)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="ebook_url_barnes" value="' . esc_attr(get_post_meta($post->ID, 'ebook_url_barnes', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Kobo', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="ebook_url_kobo" value="' . esc_attr(get_post_meta($post->ID, 'ebook_url_kobo', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Barnes & Noble (Paperback)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="paperback_url_barnes" value="' . esc_attr(get_post_meta($post->ID, 'paperback_url_barnes', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('Barnes & Noble (Hardback)', 'bd-bookshelfhelper') . '</label>';
echo '<input type="url" name="hardback_url_barnes" value="' . esc_attr(get_post_meta($post->ID, 'hardback_url_barnes', true)) . '" class="large-text"></p>';
echo '<h3>' . esc_html__('Ratings', 'bd-bookshelfhelper') . '</h3>';
echo '<p><label>' . esc_html__('Amazon Rating', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="amazon_rating" value="' . esc_attr(get_post_meta($post->ID, 'amazon_rating', true)) . '" placeholder="4.5"></p>';
echo '<p><label>' . esc_html__('Goodreads Rating', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="goodreads_rating" value="' . esc_attr(get_post_meta($post->ID, 'goodreads_rating', true)) . '" placeholder="4.2"></p>';
echo '</div>';
}
public function book_seo_meta_box($post) {
echo '<p><label>' . esc_html__('SEO Title', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="seo_title" value="' . esc_attr(get_post_meta($post->ID, 'seo_title', true)) . '" class="large-text"></p>';
echo '<p><label>' . esc_html__('SEO Description', 'bd-bookshelfhelper') . '</label>';
echo '<textarea name="seo_description" rows="4" class="large-text">' . esc_textarea(get_post_meta($post->ID, 'seo_description', true)) . '</textarea></p>';
}
public function save_meta_boxes($post_id) {
if (!isset($_POST['bd_book_nonce']) || !wp_verify_nonce($_POST['bd_book_nonce'], 'bd_book_meta')) {
return $post_id;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return $post_id;
}
if (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
$fields = array(
'pen_name_id', 'asin', 'isbn', 'short_link_slug', 'volume_number',
'amazon_rating', 'goodreads_rating', 'seo_title', 'seo_description'
);
foreach ($fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, $field, sanitize_text_field($_POST[$field]));
}
}
$languages = BD_BookShelfHelper::get_supported_languages();
foreach ($languages as $code => $name) {
$title_key = 'title_' . $code;
$blurb_key = 'blurb_' . $code;
$preview_key = 'preview_excerpt_' . $code;
$cover_key = 'cover_' . $code;
if (isset($_POST[$title_key])) {
update_post_meta($post_id, $title_key, sanitize_text_field($_POST[$title_key]));
}
if (isset($_POST[$blurb_key])) {
update_post_meta($post_id, $blurb_key, sanitize_textarea_field($_POST[$blurb_key]));
}
if (isset($_POST[$preview_key])) {
update_post_meta($post_id, $preview_key, sanitize_textarea_field($_POST[$preview_key]));
}
if (isset($_POST[$cover_key])) {
update_post_meta($post_id, $cover_key, absint($_POST[$cover_key]));
}
for ($i = 1; $i <= 5; $i++) {
$quote_key = 'pull_quote_' . $code . '_' . $i;
if (isset($_POST[$quote_key])) {
update_post_meta($post_id, $quote_key, sanitize_text_field($_POST[$quote_key]));
}
}
}
$retailer_fields = array(
'ebook_url_amazon', 'ebook_url_apple', 'ebook_url_google',
'ebook_url_barnes', 'ebook_url_kobo',
'paperback_url_barnes', 'hardback_url_barnes'
);
foreach ($retailer_fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, $field, esc_url_raw($_POST[$field]));
}
}
if (isset($_POST['series_id'])) {
$series_id = absint($_POST['series_id']);
if ($series_id > 0) {
wp_set_post_terms($post_id, array($series_id), 'series');
} else {
wp_set_post_terms($post_id, array(), 'series');
}
}
if (empty($_POST['short_link_slug'])) {
$title = get_the_title($post_id);
$slug = $this->generate_short_slug($title);
update_post_meta($post_id, 'short_link_slug', $slug);
}
}
public function generate_short_slug($title) {
$title = preg_replace('/[^a-zA-Z0-9\s]/', '', $title);
$words = explode(' ', $title);
$stop_words = array('the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for');
$significant_words = array_filter($words, function($word) use ($stop_words) {
return !in_array(strtolower($word), $stop_words) && strlen($word) > 2;
});
$slug = '';
foreach ($significant_words as $word) {
$slug .= strtolower(substr($word, 0, 1));
}
if (strlen($slug) < 2) {
$slug = substr(implode('', $significant_words), 0, 4);
}
global $wpdb;
$original_slug = $slug;
$counter = 0;
while (true) {
$exists = $wpdb->get_var($wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = 'short_link_slug' AND meta_value = %s",
$slug
));
if (!$exists) {
break;
}
$counter++;
$slug = $original_slug . $counter;
}
return $slug;
}
public function pen_names_page() {
global $wpdb;
$table_name = $wpdb->prefix . 'bd_pen_names';
if (isset($_POST['bd_add_pen_name']) && check_admin_referer('bd_add_pen_name')) {
$pen_name = sanitize_text_field($_POST['new_pen_name']);
$notes = sanitize_textarea_field($_POST['new_notes']);
if ($pen_name) {
$wpdb->insert($table_name, array(
'pen_name' => $pen_name,
'notes' => $notes,
'active' => 1,
'created_at' => current_time('mysql'),
));
echo '<div class="updated"><p>' . esc_html__('Pen name added.', 'bd-bookshelfhelper') . '</p></div>';
}
}
if (isset($_POST['bd_update_pen_status'])) {
$id = absint($_POST['pen_name_id']);
$active = isset($_POST['pen_active']) ? 1 : 0;
$wpdb->update($table_name, array('active' => $active), array('id' => $id));
}
if (isset($_POST['bd_delete_pen_name'])) {
$id = absint($_POST['pen_name_id']);
$wpdb->delete($table_name, array('id' => $id));
}
$pen_names = $wpdb->get_results("SELECT * FROM {$table_name} ORDER BY pen_name");
echo '<div class="wrap">';
echo '<h1>' . esc_html__('Pen Names', 'bd-bookshelfhelper') . '</h1>';
echo '<form method="post">';
wp_nonce_field('bd_add_pen_name');
echo '<table class="form-table">';
echo '<tr><th>' . esc_html__('Add New Pen Name', 'bd-bookshelfhelper') . '</th>';
echo '<td><input type="text" name="new_pen_name" class="regular-text" required>';
echo '<p class="description">' . esc_html__('e.g., J.K. Murphy', 'bd-bookshelfhelper') . '</p></td></tr>';
echo '<tr><th>' . esc_html__('Notes', 'bd-bookshelfhelper') . '</th>';
echo '<td><textarea name="new_notes" rows="3" class="regular-text"></textarea></td></tr>';
echo '</table>';
submit_button(__('Add Pen Name', 'bd-bookshelfhelper'), 'primary', 'bd_add_pen_name');
echo '</form>';
echo '<h2>' . esc_html__('Existing Pen Names', 'bd-bookshelfhelper') . '</h2>';
echo '<table class="widefat">';
echo '<thead><tr><th>ID</th><th>Pen Name</th><th>Notes</th><th>Status</th><th>Actions</th></tr></thead>';
echo '<tbody>';
if ($pen_names) {
foreach ($pen_names as $pn) {
echo '<tr>';
echo '<td>' . esc_html($pn->id) . '</td>';
echo '<td>' . esc_html($pn->pen_name) . '</td>';
echo '<td>' . esc_html($pn->notes) . '</td>';
echo '<td>' . ($pn->active ? esc_html__('Active', 'bd-bookshelfhelper') : esc_html__('Inactive', 'bd-bookshelfhelper')) . '</td>';
echo '<td>';
echo '<form method="post" style="display:inline;">';
wp_nonce_field('bd_update_pen_status');
echo '<input type="hidden" name="pen_name_id" value="' . esc_attr($pn->id) . '">';
echo '<input type="checkbox" name="pen_active" value="1"' . checked($pn->active, 1, false) . '>';
submit_button(__('Update', 'bd-bookshelfhelper'), 'secondary', 'bd_update_pen_status', false);
echo '</form> ';
echo '<form method="post" style="display:inline;">';
wp_nonce_field('bd_delete_pen_name');
echo '<input type="hidden" name="pen_name_id" value="' . esc_attr($pn->id) . '">';
submit_button(__('Delete', 'bd-bookshelfhelper'), 'delete', 'bd_delete_pen_name', false, array('onclick' => 'return confirm("Delete this pen name?");'));
echo '</form>';
echo '</td>';
echo '</tr>';
}
}
echo '</tbody></table></div>';
}
public function settings_page() {
if (isset($_POST['bd_save_settings'])) {
$affiliates = array(
'amazon' => sanitize_text_field($_POST['affiliate_amazon']),
'apple' => sanitize_text_field($_POST['affiliate_apple']),
'barnes' => sanitize_text_field($_POST['affiliate_barnes']),
'kobo' => sanitize_text_field($_POST['affiliate_kobo']),
'google' => sanitize_text_field($_POST['affiliate_google']),
);
update_option('bdm_affiliate_codes', $affiliates);
$goodreads_id = sanitize_text_field($_POST['goodreads_author_id']);
update_option('bdm_goodreads_author_id', $goodreads_id);
echo '<div class="updated"><p>' . esc_html__('Settings saved.', 'bd-bookshelfhelper') . '</p></div>';
}
$affiliates = get_option('bdm_affiliate_codes', array());
$goodreads_id = get_option('bdm_goodreads_author_id', '30469651');
echo '<div class="wrap">';
echo '<h1>' . esc_html__('BD BookHelper Settings', 'bd-bookshelfhelper') . '</h1>';
echo '<form method="post">';
echo '<h2>' . esc_html__('Affiliate Codes', 'bd-bookshelfhelper') . '</h2>';
echo '<p>' . esc_html__('Enter your affiliate IDs for each retailer. Links include these IDs when available.', 'bd-bookshelfhelper') . '</p>';
echo '<table class="form-table">';
echo '<tr><th>Amazon</th><td><input type="text" name="affiliate_amazon" value="' . esc_attr(isset($affiliates['amazon']) ? $affiliates['amazon'] : '') . '" class="regular-text"></td></tr>';
echo '<tr><th>Apple Books</th><td><input type="text" name="affiliate_apple" value="' . esc_attr(isset($affiliates['apple']) ? $affiliates['apple'] : '') . '" class="regular-text"></td></tr>';
echo '<tr><th>Barnes & Noble</th><td><input type="text" name="affiliate_barnes" value="' . esc_attr(isset($affiliates['barnes']) ? $affiliates['barnes'] : '') . '" class="regular-text"></td></tr>';
echo '<tr><th>Kobo</th><td><input type="text" name="affiliate_kobo" value="' . esc_attr(isset($affiliates['kobo']) ? $affiliates['kobo'] : '') . '" class="regular-text"></td></tr>';
echo '<tr><th>Google Play</th><td><input type="text" name="affiliate_google" value="' . esc_attr(isset($affiliates['google']) ? $affiliates['google'] : '') . '" class="regular-text"></td></tr>';
echo '</table>';
echo '<h2>' . esc_html__('Goodreads Integration', 'bd-bookshelfhelper') . '</h2>';
echo '<p><label>' . esc_html__('Goodreads Author ID', 'bd-bookshelfhelper') . '</label>';
echo '<input type="text" name="goodreads_author_id" value="' . esc_attr($goodreads_id) . '" class="regular-text">';
echo '<p class="description">' . esc_html__('Default: 30469651 (B_D_Murphy)', 'bd-bookshelfhelper') . '</p></p>';
submit_button(__('Save Settings', 'bd-bookshelfhelper'), 'primary', 'bd_save_settings');
echo '</form></div>';
}
public function ajax_update_links() {
check_ajax_referer('bd_update_links');
$book_id = absint($_POST['book_id']);
echo json_encode(array('success' => true));
wp_die();
}
}