Jake Vanderwerf
2026-09-14 7935d26cd16a4f8f69a59a44e27a7263b60e54bf
inc/blocks/SimpleCalendarBlock.php
@@ -1,6 +1,8 @@
<?php
namespace JVBase\blocks;
use DateTime;
use JVBase\helpers\Events;
use JVBase\managers\Cache;
use JVBase\meta\Meta;
use JVBase\registrar\Registrar;
@@ -30,7 +32,8 @@
    public function __construct()
    {
        $this->cache = Cache::for('menu', DAY_IN_SECONDS)->connect('post');
        $this->cache = Cache::for('calendar', 'post',  DAY_IN_SECONDS);
      $this->cache->flush();
        add_action('init', [ $this, 'registerBlock' ]);
    }
@@ -73,6 +76,14 @@
      } else {
         $this->params = [];
      }
      foreach (['eyear', 'emonth', 'eday'] as $key) {
         $val = get_query_var($key);
         if (!empty($val)) {
            $this->params[$key] = $val;
         }
      }
   }
    public function render(array $attributes, string $content, WP_Block $block)
@@ -82,10 +93,9 @@
         return '';
      }
      $key = $this->cache->generateKey($this->params);
      $this->cache->flush();
      return $this->cache->remember(
         $key,
         $this->params,
         function() {
            ob_start();
            $this->renderBlock();
@@ -96,19 +106,20 @@
    protected function renderBlock():void
    {
      echo '<section class="simple-calendar">';
        $this->renderHeader();
        $this->renderCalendar();
        $this->renderOnThisPage();
      echo '</section>';
    }
    protected function renderHeader():void
    {
      $title = 'Events';
      if(!in_array('all', $this->params)) {
         $title = '<small>'.$title.'</small>';
      }
      $title = $this->determineTitle();
        ?>
        <header id="top">
            <h1><?=$title?></h1>
         <?php $this->buildNav(); ?>
@@ -119,6 +130,50 @@
      </section>
        <?php
    }
      protected function determineTitle():string
      {
         if (is_tax(BASE.'event_category')) {
            $title = sprintf(
               '%s Events',
               get_queried_object()->name,
            );
         } else {
            $title = 'Events';
         }
         if(!in_array('all', $this->params)) {
            $title = '<small>'.$title.'</small>';
         }
         if (is_tax(BASE.'city')) {
            $title .= sprintf(
               ' in %s',
               get_queried_object()->name,
            );
         }
         if ($this->has('emonth')) {
            $title .= sprintf(
               ' %s %s',
               is_tax() ? 'happening on' : 'in',
               Events::getMonthName((int)$this->has('emonth')),
            );
         }
         if ($this->has('eday')) {
            $title .= sprintf(
               ' %s,',
               (int)$this->has('eday')
            );
         }
         if ($this->has('eyear')) {
            $title.= sprintf(
               ' %s%s',
               !$this->has('emonth') ? 'in ' : '',
               $this->has('eyear')
            );
         }
         return $title;
      }
      protected function buildNav():void
      {
         $content = $taxonomies = '';
@@ -134,7 +189,8 @@
            }
            if (!empty($out)) {
               $content = sprintf(
            '<ul class="term-list content"><li class="label">Content:</li>%s</ul>',
            '<details class="content row start"><summary>%sBy Content</summary><ul class="term-list content">%s</ul></details>',
                  jvbIcon('calendar-alt'),
                  implode('', $out)
               );
            }
@@ -159,24 +215,122 @@
                  }
               }
               if (!empty($out)) {
                  $inst = Registrar::getInstance($slug);
                  $taxonomies .= sprintf(
                     '<ul class="term-list %s"><li class="label">%s</li>%s</ul>',
                     '<details class="%s row start"><summary>%sBy %s</summary><ul class="term-list row start %s">%s</ul></details>',
                     $inst->getSlug(),
                     jvbIcon($inst->getIcon()??'folder'),
                     $inst->getSingular(),
                     $slug,
                     $title,
                     implode('', $out)
                  );
               }
            }
         }
         if (!empty($content) || !empty($taxonomies)) {
         $dates = $this->getDateNavigation();
         if (!is_post_type_archive($this->firstContent()) || $this->isDatedArchive()) {
            echo sprintf(
               '<nav class="types">%s%s</nav>',
               '<a href="%s" class="btn chip">All Events</a>',
               get_post_type_archive_link($this->firstContent())
            );
         }
         if (!empty($content) || !empty($taxonomies) || !empty($dates)) {
            echo sprintf(
               '<nav class="calendar types">%s%s%s</nav>',
               $content,
               $taxonomies
               $taxonomies,
               $dates
            );
         }
      }
         protected function isDatedArchive():bool
         {
            foreach (['eyear', 'emonth', 'eday'] as $key) {
               if (array_key_exists($key, $this->params)) {
                  return true;
               }
            }
            return false;
         }
         protected function getDateNavigation():string
         {
            $type = get_post_type();
            if (empty($type)) {
               return '';
            }
            return $this->cache->remember(
               $type.'dateNav',
               function() use ($type) {
                  $tmp = new WP_Query([
                     'post_type' => $type,
                     'post_status' => 'publish',
                     'posts_per_page' => -1,
                     'fields' => 'ids',
                  ]);
                  $dates = [];
                  foreach ($tmp->posts as $id) {
                     $meta = Meta::forPost($id);
                     $start = $meta->get('date_start');
                     if (!empty($start)) {
                        $date = new DateTime($start);
                        $year = $date->format('Y');
                        $month = $date->format('m');
                        $day = $date->format('d');
                        if (!array_key_exists($year, $dates)) {
                           $dates[$year] = [];
                        }
                        if (!array_key_exists($month, $dates[$year])) {
                           $dates[$year][$month] = [];
                        }
                        if (!in_array($day, $dates[$year][$month])) {
                           $dates[$year][$month][] = $day;
                        }
                     }
                  }
                  if (empty($dates)) {
                     return '';
                  }
                  $base = get_post_type_archive_link($type);
                  $out = sprintf(
                  '<details class="dates row start"><summary>%sBy Date</summary>',
                     jvbIcon('calendar')
                  );
                  foreach ($dates as $year => $months) {
                     $out .= sprintf(
                        '<details><summary><a href="%s/%d" title="See all in %d">%d</a></summary><ul>',
                        $base,
                        $year,
                        $year,
                        $year
                     );
                     foreach ($months as $month => $days) {
                        $m = intval($month);
                        $monthName = Events::getMonthName($m);
                        $out .= sprintf(
                           '<li><a href="%s/%d/%s" title="View all posts in %s, %d">%s</a></li>',
                           $base,
                           $year,
                           $month,
                           $monthName,
                           $year,
                           $monthName
                        );
                     }
                     $out .= '</ul></details>';
                  }
                  $out .= '</details>';
                  return $out;
               }
            );
         }
    protected function renderCalendar():void
    {
@@ -202,7 +356,7 @@
      }
      if (is_tax()) {
         $tax = get_queried_object();
         jvbDump($tax);
         jvbDump($tax, 'taxonomy');
      }
      return [];
   }
@@ -214,32 +368,12 @@
         return $this->formatSingular();
      }
      $page = $past
         ? $_GET['jpast']??1
         : $_GET['jpage']??1;
      $items = new WP_Query([
         'post_type' =>$this->getCurrentPostTypes(),
         'post_status'  => 'publish',
         'orderby'      => 'meta_value',
         'meta_key'     => BASE.'date_start',
         'posts_per_page'=> 30,
         'fields'    => 'ids',
         'paged'        => $page,
         'meta_type'    => 'DATETIME',
         'meta_query'   => [
            [
               'key' => BASE.'past',
               'value'  => '1',
               'compare' => $past ? '=' : '!='
            ]
         ]
      ]);
      $items = new WP_Query($this->buildArgs($past));
      $events = implode('', array_map([$this, 'formatItem'], $items->posts));
      $events = empty($events) ? '<p class="empty">Nothing here yet</p>' : sprintf(
         '<ul class="item-list">%s</ul>',
         '<ul class="item-list col">%s</ul>',
         $events
      );
      $total = $items->max_num_pages;
@@ -249,34 +383,110 @@
         $events .= paginate_links([
            'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
            'format' => '?'.$format.'=%#%',
            'current' => $page,
            'current' => $items->query_vars['paged'],
            'total' => $total
         ]);
      }
      wp_reset_postdata();
      return $events;
   }
   protected function buildArgs(bool $past):array
   {
      $page = $past
         ? min($_GET['jpast']??1, 1)
         : min($_GET['jpage']??1, 1);
      $postTypes = $this->getCurrentPostTypes();
      $args = [
         'post_type' => $postTypes,
         'post_status'  => 'publish',
         'orderby'      => 'meta_value',
         'meta_key'     => BASE.'date_start',
         'order'        => 'ASC',
         'meta_type'    => 'DATETIME',
         'posts_per_page'=> 30,
         'fields'    => 'ids',
         'paged'        => $page,
         'meta_query'   => [
            [
               'key' => BASE.'past',
               'value'  => '1',
               'compare' => $past ? 'EXISTS' : 'NOT EXISTS'
            ]
         ]
      ];
      if ($this->has('eyear')) {
         $args['meta_query'][] = [
            'key' => BASE.'year',
            'value'  => intval($this->has('eyear')),
         ];
      }
      if ($this->has('emonth')) {
         $args['meta_query'][] = [
            'key' => BASE.'month',
            'value'  => str_pad($this->has('emonth'), 2, "0", STR_PAD_LEFT),
         ];
      }
      if ($this->has('eday')) {
         $args['meta_query'][] = [
            'key' => BASE.'day',
            'value'  => str_pad($this->has('eday'), 2, "0", STR_PAD_LEFT),
         ];
      }
      if (is_tax()) {
         $obj = get_queried_object();
         $args['tax_query']= [
            [
               'taxonomy'  => $obj->taxonomy,
               'terms'     => $obj->term_id,
            ]
         ];
      }
      return $args;
   }
   protected function formatItem(int $ID):string
   {
      $meta = Meta::forPost($ID);
      $type = jvbNoBase(get_post_type($ID));
      if (empty($meta->get('date_start'))) {
         return '';
      }
      $date = strtotime($meta->get('date_start'));
      $img = empty($meta->get('post_thumbnail')) ? '' : jvbFormatImage($meta->get('post_thumbnail'), 'tiny', 'medium');
      $fields = $meta->getAll();
      return sprintf(
   '<li class="item %s">
            %s
            <h3>
               <a href="%s" title="Learn More">%s</a>
               <small><time datetime="%s">%s</time></small>
            </h3>
            %s
            <div class="row start header">
               %s
               <h3>
                  <a href="%s" title="Learn More">%s</a>
               </h3>
            </div>
            <div class="meta">
               <p>%s</p>
               %s
               %s
            </div>
            %s
         </li>',
         $type,
         empty($meta->get('post_thumbnail')) ? '' : jvbFormatImage($meta->get('post_thumbnail'), 'tiny', 'medium'),
         Events::tags($fields),
         $img,
         Events::calendarDay($fields['date_start']),
         get_the_permalink($ID),
         $meta->get('post_title'),
         date('c', $meta->get('date_start')),
         date('F j, Y', $meta->get('date_start')),
         Events::formatDay($meta->get('date_start'), $meta->get('time_start'), $meta->get('time_end')),
         Events::location($fields),
         Events::terms($fields, false),
         empty($meta->get('post_excerpt')) ? '' : wpautop($meta->get('post_excerpt'))
      );
   }
@@ -294,4 +504,17 @@
        }
        echo jvbOnThisPage(array_keys($this->details));
    }
   protected function has(string $key):false|string
   {
      return array_key_exists($key, $this->params) && absint($this->params[$key]) > 0 ?
         $this->params[$key] :
         false;
   }
   protected function firstContent():string
   {
      return array_keys($this->content)[0]??'';
   }
}