'manage_pcwp_events'); add_filter('parent_file', [$this, 'parentMenu']); add_filter('submenu_file', [$this, 'selectedMenu']); } public function menu(): void { add_submenu_page('edit.php?post_type=pcwp_event', __('ProEventsWP settings', 'proeventswp'), __('Settings', 'proeventswp'), 'manage_pcwp_events', 'pcwp-settings', [$this, 'render']); // Keep the old Pro URL registered in Free too, before WordPress checks page access. add_options_page(__('ProEventsWP AI', 'proeventswp'), __('ProEventsWP AI', 'proeventswp'), 'manage_options', 'pcwp-ai-settings', [$this, 'redirectLegacy']); } public static function url(string $tab = 'calendar'): string { return add_query_arg(['page' => 'pcwp-settings', 'tab' => self::validTab($tab)], admin_url('edit.php?post_type=pcwp_event')); } public static function validTab(string $tab): string { return in_array($tab, ['calendar', 'maps', 'license-ai'], true) ? $tab : 'calendar'; } private function tab(): string { $tab = $_GET['tab'] ?? 'calendar'; return self::validTab(is_string($tab) ? sanitize_key($tab) : 'calendar'); } /** @param array $request */ public static function legacyTab(array $request, string $method): ?string { // SDK actions, nonces and connection redirects must retain their original route. if ($method !== 'GET' || array_diff(array_keys($request), ['page', 'post_type', 'settings-updated']) !== []) { return null; } return match ($request['page'] ?? '') { 'pcwp-maps' => 'maps', 'pcwp-connection', 'pcwp-ai-settings' => 'license-ai', default => null, }; } public function redirectLegacy(): void { $tab = self::legacyTab($_GET, (string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')); if ($tab === null) { return; } $this->requireAccess($tab); $url = self::url($tab); if (isset($_GET['settings-updated'])) { $url = add_query_arg('settings-updated', 'true', $url); } wp_safe_redirect($url); exit; } public function hideLegacyMenus(): void { global $submenu; $parent = 'edit.php?post_type=pcwp_event'; foreach ($submenu[$parent] ?? [] as $item) { $slug = (string) $item[2]; if ($slug === 'pcwp-maps' || $slug === 'pcwp-connection' || str_starts_with($slug, 'pcwp-connection-')) { remove_submenu_page($parent, $slug); } } remove_submenu_page('options-general.php', 'pcwp-ai-settings'); } public function parentMenu(string $parent): string { return $this->isSettingsRoute() ? 'edit.php?post_type=pcwp_event' : $parent; } public function selectedMenu(?string $selected): ?string { return $this->isSettingsRoute() ? 'pcwp-settings' : $selected; } private function isSettingsRoute(): bool { $page = $_GET['page'] ?? ''; return is_string($page) && ($page === 'pcwp-settings' || $page === 'pcwp-maps' || $page === 'pcwp-connection' || str_starts_with($page, 'pcwp-connection-')); } public function assets(): void { if ($this->isSettingsRoute()) { wp_enqueue_style('pcwp-settings', PCWP_URL . 'assets/settings.css', [], PCWP_VERSION); } } private function requireAccess(string $tab): void { if (! current_user_can($tab === 'license-ai' ? 'manage_options' : 'manage_pcwp_events')) { wp_die(esc_html__('You do not have permission to access these settings.', 'proeventswp'), '', ['response' => 403]); } } public function checkAccess(): void { if (($_GET['page'] ?? '') === 'pcwp-settings') { $this->requireAccess($this->tab()); } } public function settings(): void { foreach (['pcwp_event_slug' => 'events', 'pcwp_venue_slug' => 'venues', 'pcwp_organizer_slug' => 'organizers', 'pcwp_series_slug' => 'event-series'] as $option => $default) { register_setting('pcwp_settings', $option, [ 'type' => 'string', 'default' => $default, 'sanitize_callback' => static function (mixed $value) use ($default): string { $slug = sanitize_title((string) $value); return $slug !== '' ? $slug : $default; }, ]); } register_setting('pcwp_settings', 'pcwp_default_view', [ 'type' => 'string', 'default' => 'list', 'sanitize_callback' => static fn (mixed $value): string => in_array($value, ['list', 'photo', 'month', 'day', 'week', 'map'], true) ? (string) $value : 'list', ]); add_settings_section('pcwp_urls', __('Public calendar', 'proeventswp'), static function (): void { echo '

' . esc_html__('Changing a slug requires saving WordPress permalinks before cutover.', 'proeventswp') . '

'; }, 'pcwp-settings'); $this->textField('pcwp_event_slug', __('Event archive slug', 'proeventswp')); $this->textField('pcwp_venue_slug', __('Venue archive slug', 'proeventswp')); $this->textField('pcwp_organizer_slug', __('Organizer archive slug', 'proeventswp')); $this->textField('pcwp_series_slug', __('Series archive slug', 'proeventswp')); add_settings_field('pcwp_default_view', __('Default view', 'proeventswp'), [$this, 'viewField'], 'pcwp-settings', 'pcwp_urls', ['label_for' => 'pcwp_default_view']); } public function render(): void { $tab = $this->tab(); $this->requireAccess($tab); $tabs = ['calendar' => __('Calendar', 'proeventswp'), 'maps' => __('Maps', 'proeventswp')]; if (current_user_can('manage_options')) { $tabs['license-ai'] = __('License & AI', 'proeventswp'); } ?>

'; foreach (['list' => __('List', 'proeventswp'), 'photo' => __('Photo', 'proeventswp'), 'month' => __('Month', 'proeventswp'), 'day' => __('Day', 'proeventswp'), 'week' => __('Week', 'proeventswp'), 'map' => __('Map', 'proeventswp')] as $value => $label) { echo ''; } echo ''; } private function textField(string $option, string $label): void { add_settings_field($option, $label, static function () use ($option): void { echo ''; }, 'pcwp-settings', 'pcwp_urls', ['label_for' => $option]); } }