slug = $slug; $this->config = $config; $this->post_type = BASE . $slug; $this->fieldRegistry = FieldRegistry::getInstance(); $this->fields = $this->fieldRegistry->getFields($slug, 'post'); $this->registerHooks(); } public function register(): void { $singular = $this->config['singular'] ?? ucfirst($this->slug); $plural = $this->config['plural'] ?? $singular . 's'; $loweredPlural = strtolower($plural); $capsMap = $this->config['capability_type']??[]; if (empty($capsMap)){ $capsMap = [ $this->slug, str_replace('-', '_',sanitize_title($loweredPlural)) ]; } $args = [ 'labels' => $this->buildLabels($singular, $plural), 'public' => $this->config['public'] ?? true, 'publicly_queryable' => $this->config['publicly_queryable'] ?? $this->config['public'] ?? true, 'show_ui' => $this->config['show_ui'] ?? true, // 'show_in_menu' => false, 'show_in_menu' => $this->config['show_in_menu'] ?? true, 'query_var' => $this->config['query_var'] ?? true, 'rewrite' => $this->config['rewrite'] ?? ['slug' => $this->slug, 'with_front' => false], 'capability_type' => $capsMap, 'capabilities' => [ 'edit_post' => "edit_{$this->slug}", 'read_post' => "read_{$this->slug}", 'delete_post' => "delete_{$this->slug}", 'edit_posts' => "edit_{$capsMap[1]}", 'edit_others_posts' => "edit_others_{$capsMap[1]}", 'publish_posts' => "publish_{$capsMap[1]}", 'read_private_posts' => "read_private_{$capsMap[1]}", 'create_posts' => "edit_{$capsMap[1]}", ], 'has_archive' => $this->config['has_archive'] ?? true, 'hierarchical' => $this->config['hierarchical'] ?? false, 'menu_position' => $this->config['menu_position'] ?? null, 'supports' => $this->config['supports'] ?? ['title', 'author', 'thumbnail', 'editor', 'revisions', 'custom-fields', 'excerpt', 'content'], 'show_in_rest' => $this->config['show_in_rest'] ?? true, ]; if ($this->config['is_calendar']??false) { $args['rewrite']['slug'] = $args['rewrite']['slug']??$this->slug.'/%eyear%/%emonth%/%eday%'; } if ($this->config['rewrite_taxonomy']??false && array_key_exists($this->config['rewrite_taxonomy'], JVB_TAXONOMY)) { $args['rewrite']['slug'] = "{$this->slug}/%{$this->config['rewrite_taxonomy']}%"; } if ($this->config['icon']??false) { $args['menu_icon'] = jvbCSSIcon($this->config['icon']); } if ($this->config['is_timeline']??false) { } register_post_type($this->post_type, $args); if (!empty($this->fields)) { $meta_registry = new MetaRegistry($this->fields, $this->slug, 'post'); $meta_registry->registerMetaFields(); } } private function buildLabels(string $singular, string $plural): array { return [ 'name' => $plural, 'singular_name' => $singular, 'menu_name' => $plural, 'name_admin_bar' => $singular, 'add_new' => "Add New", 'add_new_item' => "Add New {$singular}", 'new_item' => "New {$singular}", 'edit_item' => "Edit {$singular}", 'view_item' => "View {$singular}", 'all_items' => "All {$plural}", 'search_items' => "Search {$plural}", 'parent_item_colon' => "Parent {$plural}:", 'not_found' => "No {$plural} found.", 'not_found_in_trash' => "No {$plural} found in Trash.", ]; } public function registerHooks() { if ($this->config['hide_single'] ?? false) { add_filter('is_post_type_viewable', [$this, 'hideFromPublic']); if (jvbCheck('redirectToAuthor',$this->config)) { add_filter('post_type_link', [$this, 'redirectSingleToAuthor'], 15, 2); } else { add_filter('post_type_link', [$this, 'redirectSingleToArchive'], 15, 2); } } add_filter('jvbDashboardPage', [$this, 'renderDashPage'], 10, 3); if ($this->config['hide_children'] ?? false) { add_action('template_redirect', [$this, 'redirectChildToParent']); } if (array_key_exists('rewrite_taxonomy', $this->config) && array_key_exists($this->config['rewrite_taxonomy'], JVB_TAXONOMY)) { add_action('init', [$this, 'addRewriteRules'], 20); add_action('post_type_link', [$this, 'rewriteTaxonomySingle'], 15, 2); add_filter('post_type_archive_link', [$this, 'rewriteTaxonomyArchive'], 15, 2); } } public function hideFromPublic(bool $is_viewable): bool { if (!is_admin() && is_singular($this->post_type)) { return false; } return $is_viewable; } public function redirectSingleToAuthor(string $url, \WP_Post $post): string { if ($post->post_type !== $this->post_type) { return $url; } // Redirect to author page or archive $user_link = get_user_meta($post->post_author, BASE . 'link', true); if ($user_link) { $query_var = str_replace(BASE, '', $post->post_type); return add_query_arg($query_var, $post->ID, get_permalink($user_link)); } return get_post_type_archive_link($post->post_type); } public function redirectSingleToArchive(string $url, \WP_Post $post): string { if ($post->post_type !== $this->post_type) { return $url; } return get_post_type_archive_link($post->post_type); } public function addRewriteRules(): void { $type = $this->config['rewrite_taxonomy']; $taxonomy = jvbCheckBase($type); // Rule 1: Post type archive - /faq/ add_rewrite_rule( "{$this->slug}/?$", "index.php?post_type={$this->post_type}", 'top' ); // Rule 2: Single posts with taxonomy - /faq/section/post/ add_rewrite_rule( "{$this->slug}/([^/]+)/([^/]+)/?$", "index.php?post_type={$this->post_type}&name=\$matches[2]&{$taxonomy}=\$matches[1]", 'top' ); // Rule 3: Un-sectioned posts - /faq/post/ // Use 'bottom' priority so taxonomy rules match first add_rewrite_rule( "{$this->slug}/([^/]+)/?$", "index.php?post_type={$this->post_type}&name=\$matches[1]", 'bottom' ); } public function rewriteTaxonomySingle(string $url, \WP_Post $post): string { if ($post->post_type === $this->post_type) { $type = $this->config['rewrite_taxonomy']; $taxonomy = jvbCheckBase($type); $terms = wp_get_post_terms($post->ID, $taxonomy); if (!empty($terms) && !is_wp_error($terms)) { $path = TaxonomySelector::getTermPath($terms[0], true); $path = implode('/', array_map(function($term) { return sanitize_title($term); }, $path)); return str_replace("%{$type}%", $path, $url); } return str_replace("/%{$type}%", '', $url); } return $url; } public function rewriteTaxonomyArchive(string $url, string $post_type):string { if ($post_type === $this->post_type) { $url = get_home_url(null, "/{$this->slug}/"); } return $url; } /** * Redirect child timeline posts to their parent post */ public function redirectChildToParent(): void { if (!is_singular($this->post_type)) { return; } global $post; // If this post has a parent, redirect to parent if ($post->post_parent) { $parent_url = get_permalink($post->post_parent); // Add anchor or query param to indicate which child was accessed $redirect_url = add_query_arg('update', $post->ID, $parent_url); wp_redirect($redirect_url, 301); exit; } } public function renderDashPage(string $content, string $page, string $slug):string { if ($slug === $this->slug) { ob_start(); $crud = new CRUD($slug); $crud->render(); return ob_get_clean(); } return $content; } }