<?php
|
namespace JVBase\managers\SEO;
|
|
use JVBase\managers\AdminPages;
|
use JVBase\meta\Form;
|
use JVBase\registrar\Registrar;
|
use JVBase\ui\Tabs;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Admin interface for SEO configuration
|
*
|
* Provides UI for configuring meta tags and schema for content types.
|
* Now includes live schema preview functionality.
|
*
|
*/
|
class SEOAdminPage
|
{
|
private ConfigManager $config;
|
// private SchemaBuilder $registry;
|
|
public function __construct()
|
{
|
// $this->registry = SchemaBuilder::getInstance();
|
|
|
// Add to JVB dashboard
|
add_filter('jvbDashboardPage', [$this, 'addDashboardSection'], 20, 2);
|
add_action('admin_enqueue_scripts', [$this, 'enqueueScripts']);
|
}
|
|
public function enqueueScripts():void
|
{
|
global $_GET;
|
if (array_key_exists('page', $_GET) && $_GET['page'] === BASE.'seo') {
|
wp_enqueue_script('jvb-form');
|
wp_enqueue_script('jvb-schema');
|
}
|
}
|
|
public static function addSubpage():void
|
{
|
$subpage = [
|
'page_title' => 'SEO Settings',
|
'menu_title' => 'SEO',
|
'capability' => 'manage_options',
|
'menu_slug' => BASE . 'seo',
|
'callback' => [self::class, 'renderAdminPageStatic']
|
];
|
AdminPages::addSubPage(BASE.'seo', $subpage);
|
}
|
|
public static function renderAdminPageStatic():void
|
{
|
JVB()->seoAdmin()->renderAdminPage();
|
}
|
|
/**
|
* Add section to JVB dashboard
|
*/
|
public function addDashboardSection(string $content, string $page): string
|
{
|
if ($page !== 'SEO') {
|
return $content;
|
}
|
ob_start();
|
$this->renderAdminPage();
|
return ob_get_clean();
|
}
|
|
/**
|
* Render admin page
|
*/
|
public function renderAdminPage(bool $outputScripts = true): void
|
{
|
?>
|
<div class="wrap jvb-seo-admin">
|
<h1><?= jvbDashIcon('magnifying-glass'); ?> SEO Configuration</h1>
|
|
<?php
|
$tabs = new Tabs();
|
|
$tabs->addTab('main')
|
->title('Website & Business')
|
->icon('storefront')
|
->content($this->renderMain());
|
|
$tabs->addTab('content')
|
->title('Content')
|
->icon('note')
|
->content($this->renderConfig('content'));
|
|
$tabs->addTab('taxonomies')
|
->title('Taxonomies')
|
->icon('tag')
|
->content($this->renderConfig('taxonomy'));
|
|
echo $tabs->render();
|
?>
|
</div>
|
|
<?php
|
$this->renderTemplates();
|
if ($outputScripts) {
|
$this->renderStyles();
|
}
|
}
|
|
protected function renderForm(string $key, ?string $type = null, string $configType = 'schema'):string
|
{
|
if (!in_array($configType, ['meta', 'schema', 'archive'])) {
|
return '';
|
}
|
|
$this->config = ConfigManager::for($key);
|
// Setup archive if needed
|
if ($configType === 'archive') {
|
$this->config->setupArchive();
|
$config = $this->config->archive();
|
} elseif ($configType === 'schema') {
|
$config = $this->config->schema();
|
} else { // meta
|
$config = $this->config->meta();
|
}
|
|
if (!$type) {
|
$type = (array_key_exists('type', $config)) ? $config['type'] : 'WebPage';
|
}
|
$fields = ($configType === 'meta') ? $this->registry->getMetaFields() : $this->registry->getFieldsForType($type);
|
$registry = $this->registry->getTypeDefinition($type);
|
ob_start();
|
?>
|
<form data-save="seo" data-content="<?=$key?>">
|
<input type="hidden" name="context" value="<?=$key?>">
|
<fieldset>
|
<legend><?= $this->registry->getTypeDefinition($type)['label']??ucwords($key) ?></legend>
|
<?php
|
$exclude = ['creator'];
|
foreach ($fields as $index => $fieldName) {
|
if (in_array($fieldName, $exclude) ) {
|
continue;
|
}
|
if ($index === 0 && $fieldName !== 'type') {
|
echo '<div class="seo-'.$type.'">';
|
}
|
$fieldConfig = $this->registry->getFieldDefinition($fieldName);
|
if (!$fieldConfig) {
|
continue;
|
}
|
echo Form::render($fieldName, $config[$fieldName]??'', $fieldConfig);
|
if ($index === 0 && $fieldName === 'type') {
|
echo '<div class="seo-'.$type.'">';
|
}
|
}
|
?>
|
</div>
|
</fieldset>
|
<div class="row nowrap">
|
<button type="button" data-action="reset" style="width:max-content"><?= jvbDashIcon('arrow-counter-clockwise')?> Reset</button>
|
<button type="submit"><?=jvbDashIcon('floppy-disk') ?> Save <?=$registry['label']??ucwords($key)?></button>
|
</div>
|
</form>
|
<?php
|
return ob_get_clean();
|
}
|
|
protected function renderMain():string
|
{
|
$business = ConfigManager::for('organization');
|
$savedBusiness = $business->schema()['type'] ?? 'Organization';
|
|
$tabs = new Tabs();
|
|
$tabs->addTab('website')
|
->title('WebSite Schema')
|
->icon('globe-simple')
|
->description('This is the main definition for your website')
|
->content($this->renderForm('website', 'WebSite'));
|
|
$tabs->addTab('organization')
|
->title('Organization Schema')
|
->icon('storefront')
|
->description('Define your organization or local business here.')
|
->content($this->renderForm('organization', $savedBusiness));
|
|
return $tabs->render();
|
}
|
|
protected function renderConfig(string $type):string
|
{
|
$types = ['meta', 'schema'];
|
|
if ($type == 'content') {
|
$types[] = 'archive';
|
}
|
|
$registrar = Registrar::getInstance($type);
|
if (!$registrar){
|
return '';
|
}
|
$config = $registrar->getConfig('seo');
|
|
$mainTabs = new Tabs();
|
|
foreach ($config as $c => $opt) {
|
$subTabs = new Tabs();
|
|
foreach ($types as $t) {
|
$tab = $subTabs->addTab($c.'_'.$t);
|
|
switch ($t) {
|
case 'meta':
|
$tab->title('Meta')
|
->icon('folders')
|
->description('The title and description are used when a link is shared to social media and a preview shows, or in the search engine result for this page.')
|
->content($this->renderForm($c, null, $t));
|
break;
|
|
case 'schema':
|
$tab->title('Schema')
|
->icon('robot')
|
->description('Defining the schema helps search engines understand what the content of this page is about.')
|
->content($this->renderForm($c, null, $t));
|
break;
|
|
case 'archive':
|
$tab->title('Archive')
|
->icon('hard-drives')
|
->description('The archive is similar to the per-post schema for this content, but is generally a CollectionPage of some sort.')
|
->content($this->renderForm($c, null, $t));
|
break;
|
}
|
}
|
|
$mainTabs->addTab($c)
|
->title($opt['plural'])
|
->icon($opt['icon'])
|
->content($subTabs->render());
|
}
|
|
return $mainTabs->render();
|
}
|
|
/**
|
* Render admin styles
|
*/
|
private function renderStyles(): void
|
{
|
jvbInlineStyles('forms');
|
}
|
|
protected function renderTemplates():void
|
{
|
$types = array_keys($this->registry->schemaTypes);
|
foreach ($types as $type) {
|
?>
|
<template class="seo-<?=$type?>">
|
<div class="seo-<?=$type?>">
|
<?php
|
$fields = $this->registry->getFieldsForType($type);
|
foreach ($fields as $fieldName) {
|
$config = $this->registry->getFieldDefinition($fieldName);
|
if (!$config) {
|
continue;
|
}
|
echo Form::render($fieldName, '', $config);
|
}
|
?>
|
</div>
|
</template>
|
<?php
|
}
|
}
|
}
|