[['name','label','taxonomies'=>['category',…]], …], * 'taxonomies'=> [['name','label','postTypes'=>[…],'terms'=>[['slug','name','count'],…],'truncated'=>bool], …], * ] */ public static function all(): array { $post_types = []; $tax_names = []; /* * Filter by show_ui rather than public. * * Content types that exist "purely as data" (FAQs, downloads) are precisely the ones that * most need looping — they have no front-end address of their own, so public is false, * while a design will certainly contain a "list every FAQ" section. Listing only public types * means those types do not appear in the query panel's dropdown at all and can only be reached * by editing the scene JSON directly. That is how the ebike site's support page came about. * * show_ui means precisely "somebody manages this content in the admin", which is the right * test for what should be listed. */ $candidates = get_post_types(['show_ui' => true], 'objects'); foreach ($candidates as $pt) { // Attachments are not "content" and listing them only misleads — they have no archive and // should not be looped if ($pt->name === 'attachment') { continue; } // WordPress's own housekeeping types (nav menus, blocks, templates…): built in and not // public if (!empty($pt->_builtin) && !$pt->public) { continue; } /* * A type that is neither public nor exposed in REST is not "content" but a definition * (a form definition, a settings container). Looping them is meaningless, and listing them * only fills the dropdown with names nobody recognises. */ if (!$pt->public && !$pt->show_in_rest) { continue; } $taxes = []; foreach (get_object_taxonomies($pt->name, 'objects') as $tax) { // post_format is public, but it is a rendering hint about "what this post looks like", // not a content classification. Listing it under "filter by term" only makes people think // they forgot to configure something. // Same test as content types: if somebody manages it in the admin (show_ui), it should be // listed if ((!$tax->public && !$tax->show_ui) || $tax->name === 'post_format') { continue; } $taxes[] = $tax->name; $tax_names[$tax->name] = $tax; } /* * The list of posts in this type, for "bind to one specific post". * * Template settings used to require typing a page ID — the user had to dig through the * Pages list and copy the number out of a URL. The same class of problem as typing * `product_cat` in the query panel: the data layer was finished with no way in. * * There is a cap: a content-heavy site cannot fit in a dropdown. The overflow falls back to * typing, and the truncation is stated explicitly, or the user concludes "my post is * gone". */ $posts = get_posts([ 'post_type' => $pt->name, 'numberposts' => self::POST_LIMIT + 1, 'post_status' => 'publish', 'orderby' => 'title', 'order' => 'ASC', ]); $post_truncated = count($posts) > self::POST_LIMIT; $posts = array_slice($posts, 0, self::POST_LIMIT); $post_types[] = [ 'name' => $pt->name, 'label' => $pt->labels->name ?? $pt->name, 'taxonomies' => $taxes, // Whether it has a front-end address of its own — decides "can a card link through to a // detail page" 'public' => (bool) $pt->public, 'hasArchive' => (bool) $pt->has_archive, 'posts' => array_map(static function ($p) { return ['id' => $p->ID, 'title' => get_the_title($p) ?: ('#' . $p->ID)]; }, $posts), 'postsTruncated' => $post_truncated, ]; } $taxonomies = []; foreach ($tax_names as $name => $tax) { $terms = get_terms([ 'taxonomy' => $name, 'hide_empty' => false, 'number' => self::TERM_LIMIT + 1, 'orderby' => 'name', ]); if (is_wp_error($terms)) { $terms = []; } // One extra is fetched purely to detect truncation — the user has to be told when it happens, // or they conclude "my term is gone" $truncated = count($terms) > self::TERM_LIMIT; $terms = array_slice($terms, 0, self::TERM_LIMIT); $taxonomies[] = [ 'name' => $name, 'label' => $tax->labels->name ?? $name, 'postTypes' => array_values((array) $tax->object_type), 'terms' => array_map(static function ($t) { return ['slug' => $t->slug, 'name' => $t->name, 'count' => (int) $t->count]; }, $terms), 'truncated' => $truncated, ]; } return apply_filters('aumframe/site_data', [ 'postTypes' => $post_types, 'taxonomies' => $taxonomies, ]); } }