<?php
|
namespace JVBase\admin;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
use JVBase\registrar\Registrar;
|
use WP_Error;
|
use Exception;
|
|
/**
|
* Admin interface for managing content taxonomy tables
|
* Add this to your AdminPages.php or create as a separate manager
|
*/
|
class ContentTaxonomy
|
{
|
public function __construct()
|
{
|
if (empty(Registrar::getFeatured('is_content', 'term'))){
|
return;
|
}
|
add_filter(BASE.'handle_bulk_operation', [ $this, 'processOperation' ], 10, 3);
|
add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
|
add_action('admin_init', [$this, 'handleActions']);
|
}
|
|
public function addSubpage(array $subpages):array
|
{
|
$subpages[] = [
|
'page_title' => 'Content Taxonomy Tables', // page_title
|
'menu_title' => 'Taxonomy Tables', // menu_title
|
'capability' => 'manage_options', // capability
|
'menu_slug' => 'jvb-taxonomy-table', // menu_slug (will become BASE.'integrations')
|
'callback' => [$this, 'renderAdminPage'], // callback
|
];
|
|
return $subpages;
|
}
|
|
/**
|
* Handle admin actions
|
*/
|
public function handleActions(): void
|
{
|
if (!current_user_can('manage_options')) {
|
return;
|
}
|
|
// Handle individual taxonomy rebuild
|
if (isset($_POST['rebuild_taxonomy']) && wp_verify_nonce($_POST['_wpnonce'], 'rebuild_taxonomy')) {
|
$taxonomy = sanitize_text_field($_POST['taxonomy']);
|
$registrar = Registrar::getInstance($taxonomy);
|
if ($registrar && $registrar->hasFeature('is_content')) {
|
$results = $this->rebuildCustomTable($taxonomy);
|
// Store results in transient to display after redirect
|
set_transient('jvb_rebuild_results_' . $taxonomy, $results, 300); // 5 minutes
|
|
|
wp_redirect(add_query_arg('rebuilt', $taxonomy, wp_get_referer()));
|
exit;
|
}
|
// Display results if available
|
$results = get_transient('jvb_rebuild_results_' . $this->slug);
|
if ($results) {
|
delete_transient('jvb_rebuild_results_' . $this->slug);
|
add_action('admin_notices', function() use ($results) {
|
$class = $results['errors'] > 0 ? 'notice-warning' : 'notice-success';
|
echo '<div class="notice ' . $class . ' is-dismissible">';
|
echo '<p><strong>Taxonomy Rebuild Results:</strong></p>';
|
echo '<ul>';
|
foreach ($results['messages'] as $message) {
|
echo '<li>' . esc_html($message) . '</li>';
|
}
|
echo '</ul>';
|
echo '</div>';
|
});
|
}
|
}
|
|
// Handle rebuild all
|
if (isset($_POST['rebuild_all']) && wp_verify_nonce($_POST['_wpnonce'], 'rebuild_all')) {
|
$results = $this->rebuildAllContentTaxonomies();
|
|
$this->storeResults('rebuild_all', $results);
|
wp_redirect(add_query_arg('rebuilt', 'all', wp_get_referer()));
|
exit;
|
}
|
}
|
|
/**
|
* Store results in transient for display
|
*/
|
protected function storeResults(string $key, array $results): void
|
{
|
set_transient("jvb_admin_results_{$key}", $results, 300);
|
}
|
|
/**
|
* Render admin page
|
*/
|
public function renderAdminPage(): void
|
{
|
// Display any stored results
|
$this->displayResults();
|
|
?>
|
<div class="wrap">
|
<h1>Content Tables Management</h1>
|
|
<div class="card">
|
<h2>Content Taxonomies</h2>
|
<p>These taxonomies have custom content tables that can be rebuilt from existing term data.</p>
|
|
<table class="widefat fixed striped">
|
<thead>
|
<tr>
|
<th>Taxonomy</th>
|
<th>Table Status</th>
|
<th>Term Count</th>
|
<th>Table Count</th>
|
<th>Actions</th>
|
</tr>
|
</thead>
|
<tbody>
|
<?php
|
$taxonomies = Registrar::getFeatured('is_content', 'term');
|
foreach ($taxonomies as $slug):
|
$registrar = Registrar::getInstance($slug);
|
$taxonomy = BASE . $slug;
|
$table_info = $this->getTableInfo($slug);
|
$term_count = wp_count_terms($taxonomy, ['hide_empty' => false]);
|
?>
|
<tr>
|
<td>
|
<strong><?= esc_html($registrar->getPlural()) ?></strong><br>
|
<code><?= esc_html($taxonomy) ?></code>
|
</td>
|
<td>
|
<?php if ($table_info['exists']): ?>
|
<span class="dashicons dashicons-yes-alt" style="color: green;"></span>
|
Table exists
|
<?php else: ?>
|
<span class="dashicons dashicons-warning" style="color: orange;"></span>
|
Table missing
|
<?php endif; ?>
|
</td>
|
<td><?= esc_html($term_count) ?> terms</td>
|
<td>
|
<?php if ($table_info['exists']): ?>
|
<?= esc_html($table_info['count']) ?> records
|
<?php if ($term_count != $table_info['count']): ?>
|
<span class="dashicons dashicons-warning" style="color: orange;" title="Counts don't match"></span>
|
<?php endif; ?>
|
<?php else: ?>
|
N/A
|
<?php endif; ?>
|
</td>
|
<td>
|
<?php if ($table_info['exists']): ?>
|
<form method="post" style="display: inline;">
|
<?php wp_nonce_field('rebuild_taxonomy'); ?>
|
<input type="hidden" name="taxonomy" value="<?= esc_attr($slug) ?>">
|
<button type="submit" name="rebuild_taxonomy" class="button button-secondary"
|
onclick="return confirm('Are you sure you want to rebuild this table? This will replace all data.')">
|
Rebuild Table
|
</button>
|
</form>
|
<?php else: ?>
|
<span class="description">Create table first via plugin activation</span>
|
<?php endif; ?>
|
</td>
|
</tr>
|
<?php endforeach; ?>
|
</tbody>
|
</table>
|
|
<p class="submit">
|
<form method="post" style="display: inline;">
|
<?php wp_nonce_field('rebuild_all'); ?>
|
<button type="submit" name="rebuild_all" class="button button-primary"
|
onclick="return confirm('Are you sure you want to rebuild ALL content tables? This may take a while.')">
|
Rebuild All Tables
|
</button>
|
</form>
|
</p>
|
</div>
|
|
<div class="card">
|
<h2>Table Information</h2>
|
<p>Content tables store frequently accessed data from taxonomies for improved performance.</p>
|
|
<h3>When to Rebuild:</h3>
|
<ul>
|
<li>After importing terms via CSV or other bulk methods</li>
|
<li>If you notice discrepancies between term counts and table counts</li>
|
<li>After modifying custom table field definitions</li>
|
<li>If custom table data appears to be corrupted or missing</li>
|
</ul>
|
|
<h3>WP-CLI Commands:</h3>
|
<code>wp jvb rebuild-content-tables</code> - Rebuild all content tables<br>
|
<code>wp jvb rebuild-content-tables <taxonomy></code> - Rebuild specific taxonomy table
|
</div>
|
</div>
|
<?php
|
}
|
|
/**
|
* Get table information for a taxonomy
|
*/
|
protected function getTableInfo(string $slug): array
|
{
|
global $wpdb;
|
|
$table = $wpdb->prefix . BASE . 'content_' . $slug;
|
|
$exists = $wpdb->get_var("SHOW TABLES LIKE '{$table}'") === $table;
|
$count = 0;
|
|
if ($exists) {
|
$count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$table}");
|
}
|
|
return [
|
'exists' => $exists,
|
'count' => $count,
|
'table' => $table
|
];
|
}
|
|
/**
|
* Display results from previous actions
|
*/
|
protected function displayResults(): void
|
{
|
// Check for rebuild completion
|
if (isset($_GET['rebuilt'])) {
|
$rebuilt = sanitize_text_field($_GET['rebuilt']);
|
|
if ($rebuilt === 'all') {
|
$results = get_transient('jvb_admin_results_rebuild_all');
|
if ($results) {
|
delete_transient('jvb_admin_results_rebuild_all');
|
$this->displayAllRebuildResults($results);
|
}
|
} else {
|
$results = get_transient("jvb_admin_results_rebuild_{$rebuilt}");
|
if ($results) {
|
delete_transient("jvb_admin_results_rebuild_{$rebuilt}");
|
$this->displaySingleRebuildResults($rebuilt, $results);
|
}
|
}
|
}
|
}
|
|
/**
|
* Display results for single taxonomy rebuild
|
*/
|
protected function displaySingleRebuildResults(string $taxonomy, array $results): void
|
{
|
$class = $results['errors'] > 0 ? 'notice-warning' : 'notice-success';
|
?>
|
<div class="notice <?= $class ?> is-dismissible">
|
<h3>Rebuild Results: <?= esc_html(ucfirst($taxonomy)) ?></h3>
|
<ul>
|
<?php foreach ($results['messages'] as $message): ?>
|
<li><?= esc_html($message) ?></li>
|
<?php endforeach; ?>
|
</ul>
|
</div>
|
<?php
|
}
|
|
/**
|
* Display results for all taxonomies rebuild
|
*/
|
protected function displayAllRebuildResults(array $results): void
|
{
|
$class = $results['failed_rebuilds'] > 0 ? 'notice-warning' : 'notice-success';
|
?>
|
<div class="notice <?= $class ?> is-dismissible">
|
<h3>Rebuild All Results</h3>
|
<p>
|
<strong>Summary:</strong>
|
<?= $results['total_taxonomies'] ?> taxonomies processed,
|
<?= $results['successful_rebuilds'] ?> successful,
|
<?= $results['failed_rebuilds'] ?> failed
|
</p>
|
|
<?php foreach ($results['details'] as $taxonomy => $detail): ?>
|
<details>
|
<summary class="row x-btw">
|
<strong><?= esc_html(ucfirst($taxonomy)) ?></strong>
|
<?php if ($detail['errors'] === 0): ?>
|
<span style="color: green;">✓ Success</span>
|
<?php else: ?>
|
<span style="color: orange;">⚠ <?= $detail['errors'] ?> errors</span>
|
<?php endif; ?>
|
</summary>
|
<ul style="margin-left: 20px;">
|
<?php foreach ($detail['messages'] as $message): ?>
|
<li><?= esc_html($message) ?></li>
|
<?php endforeach; ?>
|
</ul>
|
</details>
|
<?php endforeach; ?>
|
</div>
|
<?php
|
}
|
|
/**
|
* Rebuild the entire custom table from existing terms
|
* @return array Results with success/error counts
|
*/
|
public function rebuildCustomTable(string $taxonomy):array
|
{
|
$results = [
|
'success' => 0,
|
'errors' => 0,
|
'total' => 0,
|
'messages' => []
|
];
|
|
// Check if this is a content taxonomy
|
$registrar = Registrar::getInstance($taxonomy);
|
if (!$registrar->hasFeature('is_content')) {
|
$results['messages'][] = "Taxonomy {$taxonomy} is not a content taxonomy";
|
return $results;
|
}
|
|
global $wpdb;
|
$table = $wpdb->prefix . BASE . 'content_' . $taxonomy;
|
|
// Check if table exists
|
if ($wpdb->get_var("SHOW TABLES LIKE '{$table}'") !== $table) {
|
$results['messages'][] = "Custom table {$table} does not exist";
|
return $results;
|
}
|
|
// Clear existing data
|
$wpdb->query("TRUNCATE TABLE {$table}");
|
$results['messages'][] = "Cleared existing data from {$table}";
|
|
// Get all terms for this taxonomy
|
$terms = get_terms([
|
'taxonomy' => jvbCheckBase($taxonomy),
|
'hide_empty'=> false,
|
'number' => 0, // Get all terms
|
'fields' => 'ids'
|
]);
|
|
if (is_wp_error($terms)) {
|
$results['messages'][] = "Error fetching terms: " . $terms->get_error_message();
|
return $results;
|
}
|
|
$results['total'] = count($terms);
|
$results['messages'][] = "Found {$results['total']} terms to process";
|
|
JVB()->queue()->queueOperation(
|
'sync_content_taxonomy_tables',
|
0,
|
[
|
'terms' => $terms,
|
'taxonomy' => $taxonomy
|
],
|
[
|
'chunk_key' => 'terms',
|
'chunk_size' => 10,
|
]
|
);
|
$results['messages'][] = 'Added to queue!';
|
|
return $results;
|
}
|
|
public function processOperation(WP_Error $result, object $operation, array $data):WP_Error|array
|
{
|
switch ($operation->type) {
|
case 'sync_content_taxonomy_tables':
|
return $this->processContentTaxonomyTableSync($operation, $data);
|
default:
|
return $result;
|
}
|
}
|
|
public function processContentTaxonomyTableSync(object $operation, array $data):array
|
{
|
$taxonomy = $data['taxonomy'];
|
$registrar = new TaxonomyRegistrar($taxonomy, JVB_TAXONOMY[$taxonomy]);
|
$results = ['errors' => 0, 'success' => 0];
|
foreach ($data['terms'] as $term) {
|
try {
|
$registrar->syncTermToCustomTable($term);
|
$results['success']++;
|
} catch (Exception $e) {
|
$results['errors']++;
|
$results['messages'][] = "Error processing term {$term->term_id} ({$term->name}): " . $e->getMessage();
|
}
|
}
|
|
$results['messages'][] = "Rebuild complete: {$results['success']} successful, {$results['errors']} errors";
|
|
return [
|
'success' => true,
|
'result' => $results
|
];
|
}
|
}
|
new ContentTaxonomy();
|