$vars * @return list */ public static function register_query_var(array $vars): array { $vars[] = self::QUERY_VAR_PROVIDER; $vars[] = self::QUERY_VAR_LOCATION; $vars[] = self::QUERY_VAR_SITEMAP; return $vars; } public static function register_rewrites(): void { self::register_entity_rewrite((int) get_option('powr_detail_page_id', 0), self::QUERY_VAR_PROVIDER); self::register_entity_rewrite((int) get_option('powr_location_listing_page_id', 0), self::QUERY_VAR_LOCATION); add_rewrite_rule('^pd-providers-sitemap\.xml$', 'index.php?' . self::QUERY_VAR_SITEMAP . '=1', 'top'); } /** * Flush once after a plugin update that added rewrite rules (activation * already flushes on new installs). */ public static function maybe_flush_rules(): void { if (get_option('powr_dir_rules_version') === self::RULES_VERSION) { return; } flush_rewrite_rules(false); update_option('powr_dir_rules_version', self::RULES_VERSION, false); } private static function register_entity_rewrite(int $page_id, string $query_var): void { if ($page_id <= 0) { return; } $uri = get_page_uri($page_id); if (! is_string($uri) || $uri === '') { return; } $pattern = '^' . preg_quote($uri, '/') . '/([^/]+)/?$'; add_rewrite_rule( $pattern, 'index.php?page_id=' . $page_id . '&' . $query_var . '=$matches[1]', 'top' ); } public static function flush_rules(): void { self::register_rewrites(); flush_rewrite_rules(false); } /** * Build /detail-page/{slug}/ from a detail page permalink. */ public static function build_profile_url(string $detail_url, string $slug): string { if ($detail_url === '' || $slug === '') { return '#'; } $path = untrailingslashit($detail_url) . '/' . rawurlencode($slug); return user_trailingslashit($path); } public static function build_location_url(string $detail_url, string $slug): string { return self::build_profile_url($detail_url, $slug); } /** * 301 ?provider=slug / ?location=slug → pretty path URLs. */ public static function redirect_legacy_query_urls(): void { self::redirect_legacy_for_entity( (int) get_option('powr_detail_page_id', 0), 'provider', self::QUERY_VAR_PROVIDER ); self::redirect_legacy_for_entity( (int) get_option('powr_location_listing_page_id', 0), 'location', self::QUERY_VAR_LOCATION ); } /** * When small-practice mode is on and there is exactly one provider, * 301 /providers/ → /providers/{slug}/. */ public static function redirect_single_provider(): void { if (! (int) get_option('powr_small_practice_mode', 0)) { return; } $detail_page_id = (int) get_option('powr_detail_page_id', 0); if ($detail_page_id <= 0 || ! is_page($detail_page_id)) { return; } // Already on /providers/{slug}/ — do not loop. if (sanitize_title((string) get_query_var(self::QUERY_VAR_PROVIDER)) !== '') { return; } $client = new Powr_Dir_Api_Client(); if (! $client->is_configured()) { return; } $result = $client->get_providers(); if (is_wp_error($result) || count($result['data']) !== 1) { return; } $slug = (string) ($result['data'][0]['slug'] ?? ''); if ($slug === '') { return; } $target = self::build_profile_url((string) get_permalink($detail_page_id), $slug); if ($target === '#' || $target === '') { return; } wp_safe_redirect($target, 301); exit; } /** * When small-practice mode is on and there is exactly one location, * 301 /locations/ → /locations/{slug}/. */ public static function redirect_single_location(): void { if (! (int) get_option('powr_small_practice_mode', 0)) { return; } $page_id = (int) get_option('powr_location_listing_page_id', 0); if ($page_id <= 0 || ! is_page($page_id)) { return; } // Already on /locations/{slug}/ — do not loop. if (sanitize_title((string) get_query_var(self::QUERY_VAR_LOCATION)) !== '') { return; } $client = new Powr_Dir_Api_Client(); if (! $client->is_configured()) { return; } $locations = $client->get_locations(); if (is_wp_error($locations) || count($locations) !== 1) { return; } $slug = (string) ($locations[0]['slug'] ?? ''); if ($slug === '') { return; } $target = self::build_location_url((string) get_permalink($page_id), $slug); if ($target === '#' || $target === '') { return; } wp_safe_redirect($target, 301); exit; } /** * 301 old provider slugs to the current slug, or emit a real 404 when gone. */ public static function redirect_missing_provider_slug(): void { self::handle_missing_entity_slug( (int) get_option('powr_detail_page_id', 0), self::QUERY_VAR_PROVIDER, static fn (Powr_Dir_Api_Client $client, string $slug) => $client->get_provider($slug), static fn (string $detail_url, string $slug) => self::build_profile_url($detail_url, $slug), ); } /** * 301 old location slugs to the current slug, or emit a real 404 when gone. */ public static function redirect_missing_location_slug(): void { self::handle_missing_entity_slug( (int) get_option('powr_location_listing_page_id', 0), self::QUERY_VAR_LOCATION, static fn (Powr_Dir_Api_Client $client, string $slug) => $client->get_location($slug), static fn (string $detail_url, string $slug) => self::build_location_url($detail_url, $slug), ); } /** * @param callable(Powr_Dir_Api_Client, string): (array|WP_Error) $fetch_entity * @param callable(string, string): string $build_target_url */ private static function handle_missing_entity_slug( int $detail_page_id, string $query_var, callable $fetch_entity, callable $build_target_url ): void { if ($detail_page_id <= 0 || ! is_page($detail_page_id)) { return; } $slug = sanitize_title((string) get_query_var($query_var)); if ($slug === '') { return; } $client = new Powr_Dir_Api_Client(); if (! $client->is_configured()) { return; } $result = $fetch_entity($client, $slug); if (! is_wp_error($result)) { return; } $redirect_slug = self::extract_redirect_slug($result); if ($redirect_slug !== '') { $detail_url = (string) get_permalink($detail_page_id); $target = $build_target_url($detail_url, $redirect_slug); if ($target !== '#' && $target !== '') { wp_safe_redirect($target, 301); exit; } } global $wp_query; if ($wp_query instanceof WP_Query) { $wp_query->set_404(); } status_header(404); nocache_headers(); } private static function extract_redirect_slug(WP_Error $error): string { $data = $error->get_error_data(); if (! is_array($data)) { return ''; } $body = $data['body'] ?? null; if (! is_array($body)) { return ''; } return sanitize_title((string) ($body['redirect_slug'] ?? '')); } private static function redirect_legacy_for_entity(int $detail_page_id, string $query_key, string $query_var): void { if ($detail_page_id <= 0 || ! is_page($detail_page_id)) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only query param, not a form submission. if (! isset($_GET[$query_key])) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only query param, not a form submission. $legacy = sanitize_title(wp_unslash((string) $_GET[$query_key])); if ($legacy === '') { return; } // Already on a pretty URL — do not loop. $pretty = sanitize_title((string) get_query_var($query_var)); if ($pretty !== '') { return; } $detail_url = (string) get_permalink($detail_page_id); $target = self::build_profile_url($detail_url, $legacy); if ($target === '#' || $target === '') { return; } wp_safe_redirect($target, 301); exit; } }