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 { ?>

SEO Configuration

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(); ?>
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(); ?>
registry->getTypeDefinition($type)['label']??ucwords($key) ?> $fieldName) { if (in_array($fieldName, $exclude) ) { continue; } if ($index === 0 && $fieldName !== 'type') { echo '
'; } $fieldConfig = $this->registry->getFieldDefinition($fieldName); if (!$fieldConfig) { continue; } echo Form::render($fieldName, $config[$fieldName]??'', $fieldConfig); if ($index === 0 && $fieldName === 'type') { echo '
'; } } ?>
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) { ?>