Closes the three problems that blocked the consolidation and records the rest
as tasks.
Licence: GPLv2 or later (WordPress.org compatible)
LICENSE is now the full GPLv2 text and NOTICE is gone (it was Apache
specific). All 101 SPDX headers read GPL-2.0-or-later and every shipped
header says "GPLv2 or later".
This also fixes an inconsistency that predated the consolidation: the theme's
style.css declared GPLv2 while its PHP files carried Apache headers.
Old/ and archive/pre-umbra-releases/ keep their original Apache headers on
purpose -- they are frozen snapshots, not shipped code.
Schema: the catalogue schema is canonical for bd_books and bd_series
Adds Umbra_Schema_Migration. The catalogue and library modules previously
both created those two tables with different columns, so whichever plugin
activated first won and the other read and wrote against the wrong shape.
The migration detects a library-shaped table, RENAMES it aside (never drops
it), creates the canonical table and copies rows across:
- book IDs preserved, keeping bd_book_meta, bd_reviews, bd_ratings and
bd_review_shortlinks valid
- title/slug/description resolved from the linked post
- the library's single isbn/asin land on the ebook columns; amazon_asin is
also set so existing product links keep working
- series migrated first so books.series_id still resolves
- post linkage rewritten as _bd_bookshelf_book_id (preserved meta key)
- publish_date kept as _bd_bookshelf_publish_date (no catalogue column)
- bd_ratings goodreads/amazon folded into rating_goodreads/rating_amazon
- rows whose post is gone are skipped with a log line, not turned into
unusable books
- duplicate slugs de-duplicated, since catalogue slug is UNIQUE
It runs before dbDelta, and on admin_init as well as activation, because
plugin updates do not re-run activation hooks. Ordering matters: dbDelta adds
columns but never drops them, so pointed at a library-shaped table it would
graft on catalogue columns and leave a table satisfying neither schema.
Umbra_Bookshelf_Helper_Install no longer creates bd_books or bd_series.
Scraping: Goodreads only, one implementation
There were three Goodreads implementations and two Amazon ones; both library
"scrapers" were empty stubs attached to cron events that fired on schedule to
do nothing, and both real scraper classes were never called at all.
Umbra_Amazon_Scraper is deleted and the duplicate HTML parsing is gone from
Umbra_Rating_Sync, which now delegates to Umbra_Goodreads_Scraper. The two
library stubs and their cron registrations are removed. One schedule
survives: bd_bookshelf_sync_ratings, every 12 hours.
The rating_amazon column is retained (it holds data and is populated during
migration) and Amazon retail links are unaffected.
A new rating fetch failure keeps the last known value instead of writing zero.
The admin ratings notice no longer warns about Amazon scraping.
Umbra_SEO is now instantiated
It registers its hooks from a constructor and the bootstrap never constructed
it, so the class was dead code and no SEO output was produced. Its meta, Open
Graph, Twitter card and sitemap hooks now attach. Completing SEO for generated
book detail pages is docs/TASKS.md #2.
Assets that 404'd
- assets/images/default-cover.png now holds a real 512x815 cover
(WorkingTitleCover_512x815.png). The helper returned this path but the
repository contained no images at all.
- email/assets/js/admin.js is committed as an empty placeholder. It was
enqueued on every email admin page but had never been committed.
Tests
- tests/schema_migration_test.php: 42 checks against a real database, driven
through the sqlite3 CLI on a throwaway file. Covers shape detection, row
mapping, ID preservation, orphan handling, rating folding, idempotency and
slug collisions.
- tests/run_all.sh runs every suite.
- The declared version is now checked against the plugin header rather than a
literal, so a version bump cannot silently break the suite.
Docs
- KNOWN-ISSUES.md rewritten: three resolved, three remaining.
- TASKS.md added: email admin behaviour, book detail page SEO, batching the
rating sync, reclaiming legacy tables, reconciling the two book models,
retiring the Old/ archive.
- SPEC and ARCHITECTURE updated for schema ownership, the migration ordering
constraint, and the single scraper.
Version 1.1.0.
576 lines
22 KiB
PHP
576 lines
22 KiB
PHP
<?php
|
|
// Copyright 2026 Umbra Tools (umbratools.com)
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
/**
|
|
* 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;
|
|
}
|