<?php
|
namespace JVBase\managers;
|
|
if (!defined('ABSPATH')) {
|
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 = $this->getDirectories();
|
if (empty($this->directories)) {
|
return;
|
}
|
$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'], 99999, 3);
|
}
|
|
public function registerDirectories()
|
{
|
$plural = 'Directories';
|
$singular = 'Directory';
|
register_post_type(BASE.'directory', array(
|
'labels' => [
|
'name' => $plural,
|
'singular_name' => $singular,
|
'menu_name' => $plural,
|
'add_new' => "Add New {$singular}",
|
'add_new_item' => "Add New {$singular}",
|
'edit_item' => "Edit {$singular}",
|
'new_item' => "New {$singular}",
|
'view_item' => "View {$singular}",
|
'search_items' => "Search {$plural}",
|
'not_found' => "No {$plural} found",
|
'not_found_in_trash' => "No {$plural} found in Trash"
|
],
|
'menu_icon' => jvbCSSIcon('sort-ascending'),
|
'public' => true,
|
'publicly_queryable' => true,
|
'show_in_menu' => true,
|
'show_in_admin_bar' => false,
|
'has_archive' => true,
|
'rewrite' => array(
|
'slug' => 'directory',
|
'with_front' => false
|
),
|
'capability_type' => 'post',
|
'supports' => array('title', 'editor', 'content', 'excerpt', 'custom-fields')
|
));
|
}
|
|
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($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 (Features::forSite()->has('has_map')) {
|
$ID = wp_insert_post([
|
'post_type' => BASE.'directory',
|
'post_title' => 'Map',
|
'post_status'=> 'publish',
|
'slug' => 'map',
|
]);
|
if (!is_wp_error($ID)) {
|
add_post_meta($ID, self::$type, 'map');
|
$created['map'] = (int)$ID;
|
$directories['map'] = [
|
'slug' => 'map',
|
'title' => 'Map',
|
'ID' => $ID,
|
'url' => get_home_url(null, '/directory/map'),
|
'page' => 'Map',
|
'type' => 'term',
|
];
|
}
|
}
|
|
if (!empty($created)) {
|
update_option(BASE.'directory_ids', $created);
|
}
|
if (!empty($directories)) {
|
update_option(BASE.'directory_list', $directories);
|
}
|
}
|
|
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);
|
return match ($type) {
|
'content' => JVB_CONTENT[$slug],
|
'taxonomy' => JVB_TAXONOMY[$slug],
|
'user' => JVB_USER[$slug],
|
default => [],
|
};
|
}
|
|
public function letters():array
|
{
|
return [
|
'a',
|
'b',
|
'c',
|
'd',
|
'e',
|
'f',
|
'g',
|
'h',
|
'i',
|
'j',
|
'k',
|
'l',
|
'm',
|
'n',
|
'o',
|
'p',
|
'q',
|
'r',
|
's',
|
't',
|
'u',
|
'v',
|
'w',
|
'x',
|
'y',
|
'z'
|
];
|
}
|
|
public function alphabetizeMe(
|
array $list,
|
string $name = '',
|
string $url = '',
|
string $ID = '',
|
$extra = false
|
):array {
|
if ($name == '') {
|
$name = get_the_title();
|
}
|
if ($url == '') {
|
$url = get_the_permalink();
|
}
|
if ($ID == '') {
|
$ID = get_the_ID();
|
}
|
|
$first = strtolower(mb_substr($name, 0, 1));
|
$list[$first][] = array(
|
'name' => $name,
|
'url' => $url,
|
'id' => $ID,
|
'extra' => $extra,
|
);
|
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->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($config['icon']).$aClose.
|
'<h2>'.$aOpen.$directory['title'].$aClose.'</h2>';
|
if (!empty($directory['description'])) {
|
$cache .= '<div class="description">';
|
foreach ($directory['description'] as $description) {
|
$cache .= '<p>'.$description.'</p>';
|
}
|
$cache .= '</div>';
|
}
|
$cache .= '</div>';
|
}
|
$cache .= '</section>';
|
return $cache;
|
}
|
);
|
}
|
|
protected function renderIndex(string $current = '', bool $open = false):string
|
{
|
$cache = $this->cache->remember(
|
'index',
|
function() {
|
$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'].'" class="'.$actualSlug.'">'.
|
$icon.$directory['title'].'
|
</a>
|
</li>';
|
}
|
$cache .= '</ul></nav>';
|
return $cache;
|
}
|
);
|
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)
|
.'</details>';
|
}
|
return $cache;
|
}
|
|
private function renderDirectory():string
|
{
|
|
$slug = get_post_meta(get_the_ID(), self::$slug, true);
|
if ($slug === '') {
|
return '';
|
}
|
$this->directories();
|
return $this->cache->remember(
|
$slug,
|
function() use ($slug) {
|
$config = $this->getConfigFromType($slug);
|
$out = '<h1>'.$config['directory'].'</h1>';
|
$out .= '<div class="description">';
|
|
foreach ($config[$slug]['description']??[] as $p) {
|
$out .= '<p>'.$p.'</p>';
|
}
|
$out .= '</div>';
|
$out .= $this->renderIndex($slug);
|
|
$type = $this->directories[$slug];
|
$list = [];
|
switch ($type) {
|
case 'content':
|
$get = new WP_Query([
|
'post_type' => jvbCheckBase($slug),
|
'posts_per_page' => -1,
|
'orderby' => 'title',
|
'order' => 'ASC'
|
]);
|
|
$hasExtra = Features::forContent($slug)->has('directory_extra');
|
if ($get->have_posts()) {
|
while ( $get->have_posts() ) {
|
$get->the_post();
|
$extra = [];
|
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,
|
];
|
|
}
|
}
|
}
|
$list = $this->alphabetizeMe(
|
$list,
|
get_the_title(),
|
get_the_permalink(),
|
get_the_ID(),
|
$extra
|
);
|
}
|
}
|
wp_reset_postdata();
|
break;
|
case 'taxonomy':
|
$get = get_terms([
|
'taxonomy' => jvbCheckBase($slug),
|
'hide_empty' => true,
|
'orderby' => 'name',
|
'order' => 'ASC',
|
]);
|
|
if ($get && !is_wp_error($get)) {
|
$extra = [];
|
foreach ($get as $term) {
|
|
$list = $this->alphabetizeMe(
|
$list,
|
$term->name,
|
get_term_link( $term->term_id, jvbCheckBase( $slug ) ),
|
$term->term_id,
|
$extra
|
);
|
}
|
}
|
break;
|
case 'user':
|
$get = get_users([
|
'role' => jvbCheckBase($slug),
|
'orderby' => 'display_name',
|
'order' => 'ASC',
|
]);
|
|
break;
|
}
|
|
$out .= '<section class="directory-list '.$slug.'">';
|
if (empty($list)) {
|
$out .= '<h2>Nothing here.</h2><p>We don\'t have anything here yet.</p>';
|
} else {
|
$out .= $this->renderLettersIndex($list);
|
$out .= $this->renderLettersList($list, $slug);
|
}
|
$out .= '</section>';
|
|
$out .= $this->renderIndex($slug, true);
|
return $out;
|
}
|
);
|
}
|
|
private function renderGroupedDirectory():string
|
{
|
$slug = get_post_meta(get_the_ID(), self::$slug, true);
|
if ($slug === '') {
|
return '';
|
}
|
return $this->cache->remember(
|
$slug.'_group',
|
function() use ($slug){
|
$out = '<h1>'.$this->directories[$slug]['title'].'</h1>';
|
$out .= '<div class="description">';
|
|
foreach ($this->directories[$slug]['description']??[] as $p) {
|
$out .= '<p>'.$p.'</p>';
|
}
|
$out .= '</div>';
|
$out .= $this->renderIndex($slug);
|
|
|
$taxonomy = str_replace('-grouped', '', $slug);
|
$tax = jvbCheckBase($taxonomy);
|
$list = $this->renderListChunk($tax, 0);
|
if ($list !== '') {
|
$out .= '<section class="directory-list '.$taxonomy.' grouped">'.$list.'</section>';
|
} else {
|
$out .= '<h2>Nothing here.</h2><p>We don\'t have anything here yet.</p>';
|
}
|
|
$out .= $this->renderIndex($slug, true);
|
return $out;
|
}
|
);
|
}
|
|
protected function renderListChunk(string $taxonomy, int $parent):string
|
{
|
$get = get_terms([
|
'taxonomy' => jvbCheckBase(str_replace('-grouped', '', $taxonomy)),
|
'hide_empty' => true,
|
'orderby' => 'name',
|
'order' => 'ASC',
|
'parent' => $parent
|
]);
|
|
if (!$get || is_wp_error($get)) {
|
return '';
|
}
|
$out = '<ul class="grouped-directory">';
|
foreach ($get as $term) {
|
$children =$this->renderListChunk($taxonomy, $term->term_id);
|
$out .= '<li>';
|
if ($children !== '') {
|
$out .= '<details class="term"><summary class="row btw"><a href="'.get_term_link($term->term_id, $term->taxonomy).'" title="See more '.$term->name.'">'.$term->name.'</a></summary>';
|
$out .= $children;
|
$out .= '</details>';
|
} else {
|
$out .= '<a href="'.get_term_link($term->term_id, $term->taxonomy).'" title="See more '.$term->name.'">'.$term->name.'</a>';
|
}
|
$out .= '</li>';
|
}
|
$out .= '</ul>';
|
return $out;
|
}
|
|
public function renderLettersIndex(array $list):string
|
{
|
$out = '<nav class="letters on-this-page"><ul>';
|
foreach ($this->letters() as $l) {
|
$aOpen = $aClose = $class = '';
|
if (array_key_exists($l, $list)) {
|
$aOpen = '<a href="#starts-with-'.$l.'">';
|
$aClose = '</a>';
|
$class = ' class="has"';
|
}
|
$out .= '<li'.$class.'>'.$aOpen.strtoupper($l).$aClose.'</li>';
|
}
|
|
$out .= '</ul></nav>';
|
|
return $out;
|
}
|
|
public function renderLettersList(array $list, string $type):string
|
{
|
|
$umami = JVB()->connect('umami');
|
|
$out = '<ul class="list-none">';
|
foreach ($list as $letter => $items) {
|
$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'])) {
|
$extra = '<span>';
|
foreach ($item['extra'] as $ext) {
|
$umamiType = ($ext['type'] === BASE.'shop') ? 'click_shop' : 'click_taxonomy';
|
$extra .= '<a href="'.$ext['url'].'"'.$umami->trackContentClick($item['id'],$umamiType, ['source_type' => 'directory']).'>'.$ext['name'].'</a>';
|
}
|
$extra .= '</span>';
|
}
|
$out .= '<li class="row btw">
|
<a href="'.$item['url'].'" title="More about '.$item['name'].'">
|
'.$item['name'].'</a>'.$extra.
|
'</li>';
|
|
}
|
$out .= '</ul></li>';
|
}
|
$out .= '</ul>';
|
return $out;
|
}
|
|
public function renderBlock(string $content, array $block, WP_Block $instance)
|
{
|
if (!is_post_type_archive(BASE.'directory') && !is_singular(BASE.'directory')) {
|
return $content;
|
}
|
if ($block['blockName'] !== 'core/post-content' && ($block['blockName'] !== 'core/group')) {
|
return $content;
|
}
|
|
// For archive page
|
if (is_post_type_archive(BASE.'directory') && $block['blockName'] === 'core/group') {
|
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>';
|
case 'yup':
|
return '<main>' . $this->renderGroupedDirectory() . '</main>';
|
}
|
}
|
|
return $content;
|
}
|
}
|