Jake Vanderwerf
2026-01-19 0113d2e9c9ff34a6ffb10707cc76d34b67a0c367
inc/managers/DirectoryManager.php
@@ -5,28 +5,36 @@
   exit;
}
use JVBase\utility\Features;
use WP_Block;
use WP_Query;
class DirectoryManager
{
    protected array $directories;
    protected array $directoryPageIDs;
   protected array $directoryList;
    protected static string $type = BASE.'for_type';
    protected static string $slug = BASE.'for_type_slug';
    protected CacheManager $cache;
    public function __construct()
    {
        $this->directories = jvbGlobalDirectoryInfo();
        if (empty(jvbGlobalDirectories())) {
        $this->directories = $this->getDirectories();
        if (empty($this->directories)) {
            return;
        }
        $this->cache = new CacheManager('directory', WEEK_IN_SECONDS);
        $this->cache = CacheManager::for('directory', WEEK_IN_SECONDS);
      foreach(['content','taxonomy','user'] as $key) {
         if (array_key_exists($key, $this->directories)) {
            $this->cache->connectTo($key);
         }
      }
        add_action('init', [$this, 'registerDirectories']);
        jvb_register_do_once('directories_registered', [$this, 'activate']);
        add_action('render_block', [$this, 'renderBlock'], 99, 3);
        add_action('render_block', [$this, 'renderBlock'], 99999, 3);
    }
    public function registerDirectories()
@@ -47,7 +55,7 @@
                'not_found' => "No {$plural} found",
                'not_found_in_trash' => "No {$plural} found in Trash"
            ],
            'menu_icon'             => jvbCSSIcon('asc'),
            'menu_icon'             => jvbCSSIcon('sort-ascending'),
            'public'                => true,
            'publicly_queryable'    => true,
            'show_in_menu'          => true,
@@ -62,81 +70,121 @@
        ));
    }
   public function getDirectories():array
   {
      $directories = get_option(BASE.'directories');
      if (!$directories) {
         $directories = [];
         //content
         if(Features::anyContentHas('show_directory')) {
            foreach (JVB_CONTENT as $key => $config) {
               if (Features::forContent($key)->has('show_directory')) {
                  $directories[$key] = 'content';
               }
            }
         }
         if(Features::anyTaxonomyHas('show_directory')) {
            foreach (JVB_TAXONOMY as $key=>$config) {
               if (Features::forTaxonomy($key)->has('show_directory')) {
                  $directories[$key] = 'taxonomy';
               }
            }
         }
         if (Features::anyUserHas('show_directory')) {
            foreach(JVB_USER as $key=>$config) {
               if (Features::forUser($key)->has('show_directory')) {
                  $directories[$key] = 'user';
               }
            }
         }
         update_option(BASE.'directories', $directories);
      }
      return $directories;
   }
   protected function getConfigFromType(string $type):array
   {
      if (!array_key_exists($type, $this->directories)) {
         return [];
      }
      return match ($this->directories[$type]) {
         'content' => JVB_CONTENT[$type],
         'taxonomy' => JVB_TAXONOMY[$type],
         'user' => JVB_USER[$type],
         default => [],
      };
   }
    public function activate()
    {
        $created = [];
        $directories = [];
        foreach (jvbGlobalDirectories() as $directory => $type) {
            switch ($type) {
                case 'content':
                    $config = JVB_CONTENT;
                    break;
                case 'tax':
                    $config = JVB_TAXONOMY;
                    break;
                case 'user':
                    $config = JVB_USER;
                    break;
            }
            $title = $config[$directory]['directory']??$config[$directory]['plural'];
            $excerpt = implode(' ', $config[$directory]['description']??[]);
            $ID = wp_insert_post([
                'post_type' => BASE.'directory',
                'post_title'=> $title,
                'post_status'=> 'publish',
                'post_excerpt' => $excerpt,
                'slug'      => sanitize_title($title)
            ]);
            if (!is_wp_error($ID)) {
                add_post_meta($ID, self::$type, $type);
                add_post_meta($ID, self::$slug, $directory);
                $created[$directory] = (int)$ID;
                $slug = sanitize_title($title);
                $directories[$directory] = [
                    'slug'  => $slug,
                    'title' => $title,
                    'ID'    => $ID,
                    'url'   => get_home_url(2, '/directory/'.$slug),
                    'page'  => $title,
                    'description'    =>$config[$directory]['description']??[],
                    'type'  => $type,
                    'extra'    => $config[$directory]['directory_extra'] ??[],
                ];
            }
      foreach($this->directories as $directory => $type) {
         $config = $this->getConfigFromType($directory);
         $title = $config['directory']??$config['plural'];
         $excerpt = implode(' ', $config['description']??[]);
         $ID = wp_insert_post([
            'post_type' => BASE.'directory',
            'post_title'   => $title,
            'post_status'  => 'publish',
            'post_excerpt' => $excerpt,
            'slug'         => sanitize_title($title)
         ]);
         if (!is_wp_error($ID)) {
            add_post_meta($ID, self::$type, $type);
            add_post_meta($ID, self::$slug, $directory);
            $created[$directory] = (int)$ID;
            $slug = sanitize_title($title);
            $directories[$directory] = [
               'slug'  => $slug,
               'title' => $title,
               'ID'    => $ID,
               'url'   => get_home_url(null, '/directory/'.$slug),
               'page'  => $title,
               'description'    =>$config[$directory]['description']??[],
               'type'  => $type,
               'extra'    => $config[$directory]['directory_extra'] ??[],
            ];
         }
         $isGrouped = match ($type) {
            'content' => Features::forContent($directory)->has('isGrouped'),
            'taxonomy' => Features::forTaxonomy($directory)->has('isGrouped'),
            'user' => Features::forUser($directory)->has('isGrouped'),
            default => false,
         };
         if ($isGrouped) {
            $title = $title.', but Grouped';
            $slug = sanitize_title($title).'-grouped';
            $excerpt = $config['groupedDescription']??'Too many options? This is grouped by type.';
            $ID = wp_insert_post([
               'post_type' => BASE.'directory',
               'post_title'   => $title,
               'post_status'  => 'publish',
               'post_excerpt' => $excerpt,
               'slug'         => $slug,
            ]);
            if (!is_wp_error($ID)) {
               add_post_meta($ID, self::$type, $type);
               add_post_meta($ID, self::$slug, $directory.'-grouped');
               add_post_meta($ID, BASE.'grouped_directory', 'yup');
               $created[$directory.'-grouped'] = (int)$ID;
               $directories[$directory.'-grouped'] = [
                  'slug'  => $slug,
                  'title' => $title,
                  'ID'    => $ID,
                  'url'   => get_home_url(null, '/directory/'.$slug),
                  'page'  => $title,
                  'description'    =>$config[$directory]['description']??[],
                  'type'  => $type,
                  'extra'    => $config[$directory]['directory_extra'] ??[],
               ];
            }
         }
      }
            if (jvbCheck('isGrouped', $config[$directory])) {
                $title = $title. ', but Grouped';
                $excerpt = 'Too many options in the list? This is grouped by type, nested deep, man.';
            $slug = sanitize_title(str_replace(', but', '', $title));
                $ID = wp_insert_post([
                    'post_type' => BASE.'directory',
                    'post_title'=> $title,
                    'post_status'=> 'publish',
                    'post_excerpt' => $excerpt,
                    'slug'      => $slug
                ]);
                if (!is_wp_error($ID)) {
                    add_post_meta($ID, self::$type, $type);
                    add_post_meta($ID, self::$slug, $directory.'-grouped');
                    add_post_meta($ID, BASE.'grouped_directory', 'yup');
                    $created[$directory.'-grouped'] = (int)$ID;
                    $directories[$directory.'-grouped'] = [
                        'slug'  => $slug,
                        'title' => $title,
                        'ID'    => $ID,
                        'url'   => get_home_url(2, '/directory/'.$slug),
                        'page'  => $title,
                        'description'    =>$config[$directory]['description']??[],
                        'type'  => $type,
                        'extra'    => $config[$directory]['directory_extra'] ??[],
                    ];
                }
            }
        }
        if (jvbCheck('has_map', JVB_SITE)) {
        if (Features::forSite()->has('has_map')) {
            $ID = wp_insert_post([
                'post_type'     => BASE.'directory',
                'post_title'    => 'Map',
@@ -150,7 +198,7 @@
                    'slug'  => 'map',
                    'title' => 'Map',
                    'ID'    => $ID,
                    'url'   => get_home_url(2, '/directory/map'),
                    'url'   => get_home_url(null, '/directory/map'),
                    'page'  => 'Map',
                    'type'  => 'term',
                ];
@@ -165,20 +213,32 @@
        }
    }
   public function getDirectoryPageIDs():array
   {
      if (empty($this->directoryPageIDs)) {
         $this->directoryPageIDs = get_option(BASE.'directory_ids', []);
      }
      return $this->directoryPageIDs;
   }
   public function getDirectoryList():array
   {
      if (empty($this->directoryList)) {
         $this->directoryList = get_option(BASE.'directory_list', []);
      }
      return $this->directoryList;
   }
    public static function getConfig(int $ID):array
    {
        $type = get_post_meta($ID, self::$type, true);
        $slug = get_post_meta($ID, self::$slug, true);
        switch ($type) {
            case 'content':
                return JVB_CONTENT[$slug];
            case 'taxonomy':
                return JVB_TAXONOMY[$slug];
            case 'user':
                return JVB_USER[$slug];
        }
        return [];
    }
      return match ($type) {
         'content' => JVB_CONTENT[$slug],
         'taxonomy' => JVB_TAXONOMY[$slug],
         'user' => JVB_USER[$slug],
         default => [],
      };
   }
    public function letters():array
    {
@@ -212,7 +272,7 @@
        ];
    }
    protected function alphabetizeMe(
    public function alphabetizeMe(
        array $list,
        string $name = '',
        string $url = '',
@@ -239,19 +299,35 @@
        return $list;
    }
   public function directories(string $search = 'all'):array
   {
      $directories = $this->getDirectories();
      if ($search === 'all') {
         return $directories;
      }
      return $directories[$search]??[];
   }
   public function isDirectory():bool
   {
      return (is_post_type_archive(BASE.'directory') || is_singular(BASE.'directory'));
   }
    private function renderArchive(): string
    {
      $this->getDirectoryList();
      return $this->cache->remember(
         'archive',
         function() {
            $cache = '<h1>Directory of Directories</h1>
                <p>You like lists? We\'ve got \'em!</p>
                <section class="directories item-grid">';
            foreach ($this->directories as $slug => $directory) {
            foreach ($this->directoryList as $slug => $directory) {
               $config = $this->getConfigFromType($slug);
               $aOpen = '<a href="'.$directory['url'].'" title="See our list of '.$directory['title'].'">';
               $aClose = '</a>';
               $cache .= '<div class="directory col start">
                '.$aOpen.jvbIcon($slug).$aClose.
                '.$aOpen.jvbIcon($config['icon']).$aClose.
                  '<h2>'.$aOpen.$directory['title'].$aClose.'</h2>';
               if (!empty($directory['description'])) {
                  $cache .= '<div class="description">';
@@ -273,11 +349,14 @@
      $cache = $this->cache->remember(
         'index',
         function() {
            $cache = '<nav class="directory"><ul>';
            foreach (jvbDirectories() as $slug => $directory) {
            $cache = '<nav class="directory condensed"><ul>';
            foreach ($this->getDirectoryList() as $slug => $directory) {
               $actualSlug = str_replace('-grouped', '', $slug);
               $config = $this->getConfigFromType($actualSlug);
               $icon = jvbIcon($config['icon']??'');
               $cache .= '<li id="'.$slug.'">
                    <a href="'.$directory['url'].'">'.
                  jvbIcon(str_replace('-grouped', '', $slug)).$directory['title'].'
                    <a href="'.$directory['url'].'" class="'.$actualSlug.'">'.
                  $icon.$directory['title'].'
                    </a>
                </li>';
            }
@@ -285,7 +364,7 @@
            return $cache;
         }
      );
        if ($current !== '' && array_key_exists($current, jvbDirectories())) {
        if ($current !== '' && array_key_exists($current, $this->directories())) {
            $open = ($open) ? ' open' : '';
            $cache = '<details'.$open.'><summary class="row btw">Other Directories:</summary>'.
                     str_replace('id="'.$current.'"', 'id="'.$current.'" class="current"', $cache)
@@ -301,21 +380,23 @@
        if ($slug === '') {
            return '';
        }
      $this->directories();
      return $this->cache->remember(
         $slug,
         function() use ($slug) {
            $out = '<h1>'.$this->directories[$slug]['title'].'</h1>';
            $config = $this->getConfigFromType($slug);
            $out = '<h1>'.$config['directory'].'</h1>';
            $out .= '<div class="description">';
            foreach ($this->directories[$slug]['description']??[] as $p) {
            foreach ($config[$slug]['description']??[] as $p) {
               $out .= '<p>'.$p.'</p>';
            }
            $out .= '</div>';
            $out .= $this->renderIndex($slug);
            $data = $this->directories[$slug];
            $type = $this->directories[$slug];
            $list = [];
            switch ($data['type']) {
            switch ($type) {
               case 'content':
                  $get = new WP_Query([
                     'post_type'            => jvbCheckBase($slug),
@@ -324,37 +405,40 @@
                     'order'                => 'ASC'
                  ]);
                  $hasExtra = Features::forContent($slug)->has('directory_extra');
                  if ($get->have_posts()) {
                     while ( $get->have_posts() ) {
                        $get->the_post();
                        $extra = [];
                        foreach ( $data['extra'] as $item ) {
                           $item = jvbCheckBase( $item );
                        if ($hasExtra) {
                           foreach ($config['directory_extra'] as $item ) {
                              $item = jvbCheckBase( $item );
                           $terms = get_the_terms( get_the_ID(), jvbCheckBase( $item ) );
                           if ( $terms && ! is_wp_error( $terms ) ) {
                              $term    = $terms[0];
                              $extra[] = [
                                 'name' => (get_term_meta( $term->term_id, BASE . 'singular', true ) !== '') ? get_term_meta( $term->term_id, BASE . 'singular', true ) : $term->name,
                                 'url'  => get_term_link( $term->term_id, $item ),
                                 'id'   => $term->term_id,
                                 'type' => $item,
                              ];
                              $terms = get_the_terms( get_the_ID(), jvbCheckBase( $item ) );
                              if ( $terms && ! is_wp_error( $terms ) ) {
                                 $term    = $terms[0];
                                 $extra[] = [
                                    'name' => (get_term_meta( $term->term_id, BASE . 'singular', true ) !== '') ? get_term_meta( $term->term_id, BASE . 'singular', true ) : $term->name,
                                    'url'  => get_term_link( $term->term_id, $item ),
                                    'id'   => $term->term_id,
                                    'type' => $item,
                                 ];
                              }
                           }
                           $list = $this->alphabetizeMe(
                              $list,
                              get_the_title(),
                              get_the_permalink(),
                              get_the_ID(),
                              $extra
                           );
                        }
                        $list = $this->alphabetizeMe(
                           $list,
                           get_the_title(),
                           get_the_permalink(),
                           get_the_ID(),
                           $extra
                        );
                     }
                  }
                  wp_reset_postdata();
                  break;
               case 'tax':
               case 'taxonomy':
                  $get = get_terms([
                     'taxonomy'            => jvbCheckBase($slug),
                     'hide_empty'        => true,
@@ -363,7 +447,7 @@
                  ]);
                  if ($get && !is_wp_error($get)) {
                     $extra = false;
                     $extra = [];
                     foreach ($get as $term) {
                        $list = $this->alphabetizeMe(
@@ -384,9 +468,6 @@
                  ]);
                  break;
               default:
                  $list = [];
                  break;
            }
            $out .= '<section class="directory-list '.$slug.'">';
@@ -412,7 +493,7 @@
        }
      return $this->cache->remember(
         $slug.'_group',
         function() {
         function() use ($slug){
            $out = '<h1>'.$this->directories[$slug]['title'].'</h1>';
            $out .= '<div class="description">';
@@ -493,7 +574,7 @@
        $out = '<ul class="list-none">';
        foreach ($list as $letter => $items) {
            $out .= '<li id="starts-with-'.$letter.'" class="row a-start btw"><h3>'.strtoupper($letter).'</h3><ul>';
            $out .= '<li id="starts-with-'.$letter.'" class="row a-start btw nowrap"><h3>'.strtoupper($letter).'</h3><ul>';
            foreach ($items as $item) {
                $extra = '';
                if (!empty($item['extra'])) {
@@ -525,15 +606,13 @@
            return $content;
        }
        // For archive page
        if (is_post_type_archive(BASE.'directory') && $block['blockName'] === 'core/group') {
            return ($block['attrs']['tagName']??false === 'main') ? '<main>'.$this->renderArchive().'</main>' : $content;
            return ($block['attrs']['tagName']??'' === 'main') ? '<main>'.$this->renderArchive().'</main>' : $content;
        }
        // For single directory posts
        if ($block['blockName'] === 'core/group') {
            switch (get_post_meta(get_the_ID(), BASE.'grouped_directory', true)) {
                case '':
                    return '<main>' . $this->renderDirectory() . '</main>';
@@ -545,15 +624,3 @@
        return $content;
    }
}
new DirectoryManager();
function jvbDirectoryConfig():array
{
    $ID = get_the_ID();
    if ($ID) {
        return DirectoryManager::getConfig($ID);
    }
    return [];
}