proUpsell()->registerHooks(); } private function proUpsell(): ProUpsell { return $this->proUpsell ??= new ProUpsell(); } public function enqueueAssets(string $hookSuffix): void { if ('woocommerce_page_' . self::PAGE !== $hookSuffix) { return; } wp_enqueue_style( 'tabby-admin', \Tabby\Plugin::instance()->url('assets/css/admin.css'), [], \Tabby\VERSION, ); wp_enqueue_script( 'tabby-repeater', \Tabby\Plugin::instance()->url('assets/js/repeater.js'), [], \Tabby\VERSION, ['in_footer' => true, 'strategy' => 'defer'], ); } public function addMenuPage(): void { add_submenu_page( 'woocommerce', __('Tabvera - Product Tabs', 'tabvera'), __('Tabvera Tabs', 'tabvera'), 'manage_woocommerce', self::PAGE, [$this, 'renderPage'], ); } public function registerSettings(): void { register_setting( self::PAGE, self::OPTION, [ 'type' => 'array', 'sanitize_callback' => [$this, 'sanitize'], ], ); add_filter( 'option_page_capability_' . self::PAGE, static fn (): string => 'manage_woocommerce', ); } public function renderPage(): void { if (! current_user_can('manage_woocommerce')) { return; } $repo = new TabRepository(); $settings = $repo->settings(); $globalTabs = $repo->globalTabs(); $richContent = (bool) ($settings['rich_content'] ?? false); ?>

proUpsell()->banner(); ?>

renderGlobalRow(0, '', '', true, $richContent); } else { foreach (array_values($globalTabs) as $index => $tab) { $this->renderGlobalRow((int) $index, $tab->title, $tab->content, $tab->enabled, $richContent); } } ?>

proUpsell()->cards(); ?>
*/ public function sanitize(mixed $raw): array { if (! is_array($raw)) { $raw = []; } /** @var array $defaults */ $defaults = require TABBY_DIR . 'config/defaults.php'; $globalTabs = []; if (isset($raw['global_tabs']) && is_array($raw['global_tabs'])) { $position = 0; foreach ($raw['global_tabs'] as $entry) { if (! is_array($entry)) { continue; } $title = isset($entry['title']) ? sanitize_text_field((string) $entry['title']) : ''; if ('' === $title) { continue; } $globalTabs[] = [ 'id' => $this->uniqueSlug($title, $position, $globalTabs), 'title' => $title, 'content' => isset($entry['content']) ? wp_kses_post((string) $entry['content']) : '', 'enabled' => ! empty($entry['enabled']), ]; ++$position; } } return array_merge($defaults, [ 'enabled' => ! empty($raw['enabled']), 'rich_content' => ! empty($raw['rich_content']), 'global_tabs' => $globalTabs, ]); } /** * Build a slug for a global tab, ensuring it does not collide with earlier * tabs in the same save. * * @param array> $existing */ private function uniqueSlug(string $title, int $position, array $existing): string { $base = sanitize_key($title); if ('' === $base) { $base = 'tab_' . $position; } $used = []; foreach ($existing as $tab) { if (isset($tab['id'])) { $used[] = (string) $tab['id']; } } $slug = $base; $n = 1; while (in_array($slug, $used, true)) { $slug = $base . '_' . $n; ++$n; } return $slug; } }