Files
Umbra-Bookshelf/theme/inc/theme-admin.php
bdm 5b5303186a Establish Umbra-Bookshelf as the successor repository
Continues the Bookshelf history. The three plugins tracked there are
consolidated into umbra-bookshelf/ and this repository becomes the canonical
home for the plugin and the theme.

  bd-bookshelf/  ->  theme/     separate artifact, name unchanged
  (root specs)   ->  umbra-bookshelf/spec/  aligned to Umbra Bookshelf
  old plugin zips -> archive/pre-umbra-releases/

Adds a repository README and drops the Bookshelf closure notice.
2026-10-18 19:56:26 -07:00

576 lines
22 KiB
PHP

<?php
// Copyright 2026 Umbra Tools (umbratools.com)
// SPDX-License-Identifier: Apache-2.0
/**
* Theme Admin Menu
*
* @package BD_Murphy
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* Helper function to render the intro message
*/
function bdm_render_intro_message() {
?>
<div style="background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%); padding: 20px; border-radius: 8px; border-left: 4px solid #1B3A6B; margin-bottom: 25px;">
<p style="margin: 0; font-size: 1rem; line-height: 1.6;">
<strong>About this theme:</strong> This theme was created for authors to have a place for blogs, and most importantly pages to show off their books.
The plugin <strong>BD Bookshelf</strong> is the repository for all the book information so you enter info once, manage it in one place.
</p>
</div>
<?php
}
/**
* Check if BD Bookshelf plugin is active
*/
function bdm_is_plugin_active() {
return class_exists('BD_BookShelfHelper') || class_exists('BD_Bookshelf');
}
/**
* Add theme admin menu
*/
function bdm_add_admin_menu() {
$plugin_active = bdm_is_plugin_active();
add_menu_page(
esc_html__('BD Bookshelf', 'bd-bookshelf'),
esc_html__('BD Bookshelf', 'bd-bookshelf'),
'manage_options',
'bdm-theme',
$plugin_active ? 'bdm_theme_settings_page' : 'bdm_theme_welcome_page',
'dashicons-book-alt',
30
);
add_submenu_page(
'bdm-theme',
esc_html__('Settings', 'bd-bookshelf'),
esc_html__('Settings', 'bd-bookshelf'),
'manage_options',
'bdm-theme',
$plugin_active ? 'bdm_theme_settings_page' : 'bdm_theme_welcome_page'
);
add_submenu_page(
'bdm-theme',
esc_html__('Update Scan', 'bd-bookshelf'),
esc_html__('Update Scan', 'bd-bookshelf'),
'manage_options',
'bdm-update-scan',
'bdm_update_scan_page'
);
add_submenu_page(
'bdm-theme',
esc_html__('Theme Transition', 'bd-bookshelf'),
esc_html__('Theme Transition', 'bd-bookshelf'),
'manage_options',
'bdm-theme-transition',
'bdm_theme_transition_page'
);
}
add_action('admin_menu', 'bdm_add_admin_menu');
/**
* Theme welcome/welcome page
*/
function bdm_theme_welcome_page() {
bdm_render_intro_message();
?>
<div class="wrap">
<div style="background: linear-gradient(135deg, #1B3A6B 0%, #2E6DA4 100%); padding: 40px; border-radius: 8px; color: #fff; margin-bottom: 30px;">
<h1 style="color: #fff; font-size: 2rem; margin: 0 0 10px 0;">BD Bookshelf Theme</h1>
<p style="font-size: 1.1rem; margin: 0;">Welcome to your author book showcase theme!</p>
</div>
<div style="background: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<h2 style="color: #1B3A6B;">Required Plugin</h2>
<p style="margin-bottom: 20px;">This theme requires the <strong>BD Bookshelf</strong> plugin to manage your books and retailer links.</p>
<?php
$plugin_active = bdm_is_plugin_active();
?>
<?php if (!$plugin_active) : ?>
<div style="background: #fff3cd; padding: 20px; border-radius: 8px; border-left: 4px solid #ffc107; margin-bottom: 20px;">
<h3 style="margin: 0 0 10px 0; color: #856404;">Plugin Not Installed</h3>
<p style="margin: 0 0 15px 0;">Please install and activate the BD Bookshelf plugin to manage your books.</p>
<a href="<?php echo admin_url('plugins.php'); ?>" style="display: inline-block; background: #ffc107; color: #000; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">
Go to Plugins
</a>
</div>
<?php else : ?>
<div style="background: #d4edda; padding: 20px; border-radius: 8px; border-left: 4px solid #28a745; margin-bottom: 20px;">
<h3 style="margin: 0 0 10px 0; color: #155724;">Plugin Installed</h3>
<p style="margin: 0;">The BD Bookshelf plugin is installed and active.</p>
</div>
<?php endif; ?>
<h3 style="color: #1B3A6B; margin-top: 30px;">Quick Links</h3>
<ul style="list-style: none; padding: 0;">
<li style="margin-bottom: 10px;"><a href="?page=bdm-theme">Settings</a></li>
<li style="margin-bottom: 10px;"><a href="?page=bdm-update-scan">Update Scan</a></li>
</ul>
</div>
<?php bdm_render_donate_section(); ?>
</div>
<?php
}
/**
* Theme settings page
*/
function bdm_theme_settings_page() {
if (isset($_POST['bdm_settings_nonce']) && wp_verify_nonce($_POST['bdm_settings_nonce'], 'bdm_save_settings')) {
update_option('bdm_goodreads_author_id', sanitize_text_field($_POST['goodreads_author_id']));
update_option('bdm_amazon_author_id', sanitize_text_field($_POST['amazon_author_id']));
echo '<div class="notice notice-success is-dismissible"><p>Settings saved!</p></div>';
}
$goodreads_id = get_option('bdm_goodreads_author_id', '');
$amazon_id = get_option('bdm_amazon_author_id', '');
bdm_render_intro_message();
?>
<div class="wrap">
<h1>BD Bookshelf Settings</h1>
<form method="post">
<?php wp_nonce_field('bdm_save_settings', 'bdm_settings_nonce'); ?>
<table class="form-table">
<tr>
<th>Goodreads Author ID</th>
<td>
<input type="text" name="goodreads_author_id" value="<?php echo esc_attr($goodreads_id); ?>" class="regular-text" placeholder="e.g., 30469651">
<p class="description">Your Goodreads author page ID (found in your author URL)</p>
</td>
</tr>
<tr>
<th>Amazon Author Central ID</th>
<td>
<input type="text" name="amazon_author_id" value="<?php echo esc_attr($amazon_id); ?>" class="regular-text">
<p class="description">Your Amazon Author Central author page ID</p>
</td>
</tr>
</table>
<p class="submit"><input type="submit" class="button-primary" value="Save Settings"></p>
</form>
<hr style="margin: 30px 0;">
<h2>Theme Information</h2>
<p><strong>Version:</strong> <?php echo BDM_VERSION; ?></p>
<p><strong>Created by</strong> <a href="<?php echo esc_url(home_url('/')); ?>" target="_blank">B.D. Murphy</a></p>
<p><a href="?page=bdm-update-scan">Go to Update Scan</a></p>
<?php bdm_render_donate_section(); ?>
</div>
<?php
}
/**
* Update scan page
*/
function bdm_update_scan_page() {
bdm_render_intro_message();
?>
<div class="wrap">
<h1>BD Bookshelf - Update Scan</h1>
<div style="background: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); margin-bottom: 30px;">
<p>This feature allows you to scan book retailer links to check for updates.</p>
<p style="margin-top: 20px;">
<button type="button" class="button button-primary" id="bdm-scan-btn" onclick="bdm_run_scan()">
Scan Books for Updates
</button>
</p>
<div id="bdm-scan-results" style="margin-top: 20px; display: none;">
<h3>Scan Results</h3>
<div id="bdm-scan-output"></div>
</div>
</div>
<script>
function bdm_run_scan() {
var btn = document.getElementById('bdm-scan-btn');
var results = document.getElementById('bdm-scan-results');
var output = document.getElementById('bdm-scan-output');
btn.disabled = true;
btn.textContent = 'Scanning...';
results.style.display = 'block';
output.innerHTML = '<p>Scanning your books for retailer link updates...</p>';
// Simulated scan - in real implementation this would check each retailer link
setTimeout(function() {
btn.disabled = false;
btn.textContent = 'Scan Books for Updates';
output.innerHTML = '<p>No updates found. All retailer links are current.</p>';
}, 2000);
}
</script>
<?php bdm_render_donate_section(); ?>
</div>
<?php
}
/**
* Render donate section
*/
function bdm_render_donate_section() {
$venmo = bdm_get_theme_option('venmo_url');
$paypal = bdm_get_theme_option('paypal_url');
$buymeacoffee = bdm_get_theme_option('buymeacoffee_url', 'https://buymeacoffee.com/bdmurph73I');
?>
<div style="background: #f8f9fa; padding: 25px; border-radius: 8px; margin-top: 40px; text-align: center;">
<h3 style="margin: 0 0 15px 0; color: #1B3A6B;">Support the Author</h3>
<p style="margin: 0 0 20px 0;">If you enjoy this theme, consider supporting the author!</p>
<div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
<?php if ($venmo) : ?>
<a href="<?php echo esc_url($venmo); ?>" target="_blank" style="display: inline-block; background: #3D95CE; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">
Venmo
</a>
<?php endif; ?>
<?php if ($paypal) : ?>
<a href="<?php echo esc_url($paypal); ?>" target="_blank" style="display: inline-block; background: #003087; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">
PayPal
</a>
<?php endif; ?>
<?php if ($buymeacoffee) : ?>
<a href="<?php echo esc_url($buymeacoffee); ?>" target="_blank" style="display: inline-block; background: #28a745; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">
Buy Me a Coffee
</a>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* Check if books need updates (could be called via AJAX)
*/
function bdm_check_for_updates() {
check_ajax_referer('bdm_scan_nonce', 'nonce');
$books = get_posts(array('post_type' => 'book', 'posts_per_page' => -1, 'post_status' => 'publish'));
$updates = array();
foreach ($books as $book) {
$formats = array('ebook', 'paperback', 'hardback');
$book_links = array();
foreach ($formats as $format) {
$links = BD_BookShelfHelper::get_retailer_links($book->ID, $format);
foreach ($links as $link) {
$book_links[$link['retailer']][$format] = $link['url'];
}
}
$updates[] = array(
'id' => $book->ID,
'title' => $book->post_title,
'links' => $book_links
);
}
wp_send_json_success(array('books' => $updates));
}
add_action('wp_ajax_bdm_scan_books', 'bdm_check_for_updates');
/**
* Theme transition page
*/
function bdm_theme_transition_page() {
$plugin_active = bdm_is_plugin_active();
bdm_render_intro_message();
if (!$plugin_active) {
?>
<div class="wrap">
<h1>BD Bookshelf - Theme Transition</h1>
<div style="background: #fff3cd; padding: 20px; border-radius: 8px; border-left: 4px solid #ffc107;">
<h3 style="margin: 0 0 10px 0; color: #856404;">BD Bookshelf Plugin Required</h3>
<p style="margin: 0;">Please install and activate the BD Bookshelf plugin before migrating books.</p>
</div>
</div>
<?php
return;
}
$transition_data = get_transient('bdm_transition_data');
$scan_results = get_transient('bdm_scan_results');
if (isset($_POST['bdm_scan_nonce']) && wp_verify_nonce($_POST['bdm_scan_nonce'], 'bdm_scan_books')) {
$scan_results = bdm_scan_existing_content();
set_transient('bdm_scan_results', $scan_results, HOUR_IN_SECONDS);
}
if (isset($_POST['bdm_import_nonce']) && wp_verify_nonce($_POST['bdm_import_nonce'], 'bdm_import_books')) {
if (!empty($_POST['found_books'])) {
$imported = bdm_import_books_to_bookhelper($_POST['found_books']);
echo '<div class="notice notice-success is-dismissible"><p>Imported ' . count($imported) . ' books!</p></div>';
}
}
if (isset($_POST['bdm_create_shortlinks_nonce']) && wp_verify_nonce($_POST['bdm_create_shortlinks_nonce'], 'bdm_create_shortlinks')) {
$created = bdm_create_shortlinks();
echo '<div class="notice notice-success is-dismissible"><p>Created ' . $created . ' short links!</p></div>';
delete_transient('bdm_scan_results');
}
?>
<div class="wrap">
<h1>BD Bookshelf - Theme Transition</h1>
<p style="margin-bottom: 30px; font-size: 1.1rem;">
This tool helps migrate books from your existing theme to BD Bookshelf. It scans your site for book-related content and imports them into the BD Bookshelf plugin.
</p>
<form method="post" style="margin-bottom: 30px;">
<?php wp_nonce_field('bdm_scan_books', 'bdm_scan_nonce'); ?>
<h2 style="color: #1B3A6B;">Step 1: Scan for Existing Content</h2>
<p>Scan pages and posts for book listings, cover images, and retailer links.</p>
<p class="submit"><input type="submit" class="button button-primary" value="Scan Site for Books"></p>
</form>
<?php if (!empty($scan_results)) : ?>
<form method="post" style="margin-bottom: 30px;">
<?php wp_nonce_field('bdm_import_books', 'bdm_import_nonce'); ?>
<h2 style="color: #1B3A6B;">Step 2: Import Books to BookHelper</h2>
<table class="widefat" style="margin-bottom: 20px;">
<thead>
<tr>
<th style="width: 30px;"><input type="checkbox" id="select-all" checked></th>
<th>Title</th>
<th>Found Data</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($scan_results as $book) : ?>
<tr>
<td><input type="checkbox" name="found_books[]" value="<?php echo esc_attr($book['id']); ?>" checked></td>
<td><?php echo esc_html($book['title']); ?></td>
<td>
<?php
$data = array();
if (!empty($book['cover'])) $data[] = 'Cover';
if (!empty($book['retailer_links'])) $data[] = 'Links: ' . count($book['retailer_links']);
echo !empty($data) ? implode(', ', $data) : 'Basic';
?>
</td>
<td><?php echo $book['exists'] ? 'Already in BookHelper' : 'New'; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p class="submit"><input type="submit" class="button button-primary" value="Import Selected Books"></p>
</form>
<form method="post" style="margin-bottom: 30px;">
<?php wp_nonce_field('bdm_create_shortlinks', 'bdm_create_shortlinks_nonce'); ?>
<h2 style="color: #1B3A6B;">Step 3: Create Short Links</h2>
<p>Generate short links (e.g., /tld, /swtd) for each book.</p>
<p class="submit"><input type="submit" class="button button-primary" value="Create Short Links"></p>
</form>
<?php endif; ?>
<div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin-top: 40px;">
<h3 style="margin: 0 0 15px 0; color: #1B3A6B;">Transition Notes</h3>
<ul style="margin: 0; padding-left: 20px;">
<li>This scan looks for pages with book-related content</li>
<li>Detected cover images, retailer links, and book details will be imported</li>
<li>Short links will be auto-generated based on book titles</li>
<li>After transition, update book template to "Individual Book" for proper layout</li>
</ul>
</div>
<?php bdm_render_donate_section(); ?>
</div>
<script>
document.getElementById('select-all').addEventListener('change', function() {
document.querySelectorAll('input[name="found_books[]"]').forEach(function(cb) {
cb.checked = document.getElementById('select-all').checked;
});
});
</script>
<?php
}
/**
* Scan existing content for book data
*/
function bdm_scan_existing_content() {
$books = get_posts(array('post_type' => array('page', 'post'), 'posts_per_page' => -1));
$found = array();
foreach ($books as $post) {
$content = $post->post_content;
$has_book_data = false;
$book_data = array(
'id' => $post->ID,
'title' => $post->post_title,
'exists' => false,
'cover' => '',
'retailer_links' => array()
);
// Check if already a book post
if ($post->post_type === 'book') {
$book_data['exists'] = true;
$has_book_data = true;
// Get cover
$cover_id = get_post_thumbnail_id($post->ID);
if ($cover_id) {
$book_data['cover'] = wp_get_attachment_url($cover_id);
}
// Get existing retailer links
$link_keys = array('amazon', 'apple', 'barnes', 'kobo', 'google');
$formats = array('ebook', 'paperback', 'hardback');
foreach ($link_keys as $retailer) {
foreach ($formats as $format) {
$url = get_post_meta($post->ID, $format . '_url_' . $retailer, true);
if ($url) {
$book_data['retailer_links'][] = $retailer . ':' . $format;
}
}
}
}
// Check for book-related content in pages/posts
$book_keywords = array('book', 'novel', 'ebook', 'paperback', 'hardback', 'author', 'isbn', 'asin', 'amazon', 'barnes', 'kobo');
$content_lower = strtolower($content);
foreach ($book_keywords as $keyword) {
if (strpos($content_lower, $keyword) !== false) {
$has_book_data = true;
break;
}
}
// Check for cover image
if (has_post_thumbnail($post->ID)) {
$book_data['cover'] = get_post_thumbnail_url($post->ID);
$has_book_data = true;
}
// Check for known retailer URLs
$retailer_domains = array(
'amazon.com' => 'amazon',
'barnesandnoble.com' => 'barnes',
'kobo.com' => 'kobo',
'books.apple.com' => 'apple',
'play.google.com' => 'google'
);
foreach ($retailer_domains as $domain => $retailer) {
if (strpos($content, $domain) !== false) {
if (!in_array($retailer, array_column($book_data['retailer_links'], 'retailer'))) {
$book_data['retailer_links'][] = $retailer;
}
$has_book_data = true;
}
}
if ($has_book_data || $post->post_type === 'book') {
$found[] = $book_data;
}
}
return $found;
}
/**
* Import found books to BookHelper
*/
function bdm_import_books_to_bookhelper($book_ids) {
$imported = 0;
foreach ($book_ids as $book_id) {
$post = get_post($book_id);
if (!$post) continue;
// Check if already a book
if ($post->post_type === 'book') {
continue; // Already in BookHelper
}
// Create new book post
$new_book = array(
'post_title' => $post->post_title,
'post_content' => $post->post_content,
'post_status' => $post->post_status,
'post_type' => 'book'
);
$new_id = wp_insert_post($new_book);
if ($new_id) {
// Copy cover
$cover_id = get_post_thumbnail_id($book_id);
if ($cover_id) {
set_post_thumbnail($new_id, $cover_id);
}
// Copy meta fields
$meta = get_post_meta($book_id);
foreach ($meta as $key => $value) {
if (is_array($value) && isset($value[0])) {
update_post_meta($new_id, $key, $value[0]);
}
}
$imported++;
}
}
return $imported;
}
/**
* Create short links for books
*/
function bdm_create_shortlinks() {
$books = get_posts(array('post_type' => 'book', 'posts_per_page' => -1));
$created = 0;
foreach ($books as $book) {
// Check if already has short link
$existing = get_post_meta($book->ID, 'short_link_slug', true);
if ($existing) continue;
// Generate short slug from title
$title = sanitize_title($book->post_title);
// Take first part and make itshort (max 6 chars)
$parts = explode('-', $title);
$slug = substr($parts[0], 0, 4);
// Add two random chars for uniqueness
$slug .= substr(md5($book->ID), 0, 2);
update_post_meta($book->ID, 'short_link_slug', $slug);
$created++;
}
return $created;
}