Jake Vanderwerf
4 hours ago 56a9a1ccf764ff7a6af8f8a2292cb07443cb4aa7
inc/managers/CRUDManager.php
@@ -1,8 +1,9 @@
<?php
namespace JVBase\managers;
use JVBase\base\Site;
use JVBase\registrar\Registrar;
use JVBase\ui\CRUDSkeleton;
use JVBase\utility\Features;
if (!defined('ABSPATH')) {
   exit;
@@ -14,32 +15,27 @@
 */
class CRUD {
   protected CRUDSkeleton $skeleton;
   protected CacheManager $cache;
   protected array $config;
   protected Cache $cache;
   protected string $content;
   protected array $taxonomies = [];
   protected int $user_id;
   protected ?string $type = null;
   protected ?array $constant = null;
   protected Registrar $registrar;
   public function __construct(string $content) {
      if (array_key_exists($content, JVB_CONTENT)) {
         $this->type = 'post';
         $this->constant = JVB_CONTENT;
      } elseif (array_key_exists($content, JVB_TAXONOMY)) {
         $this->type = 'term';
         $this->constant = JVB_TAXONOMY;
      } elseif (array_key_exists($content, JVB_USER)) {
         $this->type = 'user';
         $this->constant = JVB_USER;
      } else {
      $this->registrar = Registrar::getInstance($content);
      if (!$this->registrar) {
         return;
      }
      $this->user_id = get_current_user_id();
      $this->config = $this->constant[$content];
      $this->content = $content;
      $this->cache = CacheManager::for($content);
      $this->cache = Cache::for('crud')->connect('post')->connect('taxonomy');
      if (JVB_TESTING) {
         $this->cache->flush();
      }
      // Create and configure skeleton
      $this->skeleton = new CRUDSkeleton();
      $this->configure();
@@ -51,64 +47,70 @@
   protected function configure(): void {
      // Basic info
      $this->skeleton
         ->content($this->content, $this->config['singular'], $this->config['plural'])
         ->content($this->content, $this->registrar->getSingular(), $this->registrar->getPlural())
         ->title(
            'Your ' . $this->config['plural'],
            $this->config['page_description'] ?? ''
            $this->registrar->config('dashboard')->getTitle(),
            $this->registrar->config('dashboard')->getDescription()?? ''
         );
      // Initialize meta
      $this->skeleton->initMeta($this->type, $this->content);
      $this->skeleton->addSearch();
      // Timeline if applicable
      if (Features::forContent($this->content)->has('is_timeline')) {
      if ($this->registrar && $this->registrar->hasFeature('is_timeline')) {
         $this->skeleton->setTimeline();
      }
      // Fields and sections
      $this->skeleton->setFields($this->config['fields']);
      $this->skeleton->setFields($this->registrar->getFields());
      $sections = array_key_exists('sections', $this->config) ? $this->config['sections'] : [];
      foreach ($sections as $id => $config) {
         $this->skeleton->addSection($id, $config);
      foreach ($this->registrar->getSections() as $config) {
         $this->skeleton->addSection($config['id'], $config);
      }
      // Taxonomies
      $this->initTaxonomies();
      // Statuses
      if (Features::forContent($this->content)->has('is_calendar')) {
      if ($this->registrar->hasFeature('is_calendar')) {
         $this->skeleton->setCalendar();
      }else {
         $this->skeleton->setDefaultStatus();
      }
      if ($this->registrar->getType() === 'post') {
         $this->skeleton->setDefaultStatus();
      } else {
         $this->skeleton->setStatuses([]);
      }
      // Views
      $this->skeleton
         ->addViews(['grid', 'list', 'table'])
         ->addViews()
         ->defaultView('grid');
      $this->skeleton->addItemActions();
      // Filters
      $this->skeleton->addDateFilter();
      $this->skeleton->addCustomDateRange($this->addDateRanges());
      if (!empty($this->taxonomies)) {
         $this->skeleton->addTaxonomyFilter(array_keys($this->taxonomies), 'user');
         $this->skeleton->addTaxonomyFilter($this->taxonomies, 'user');
      }
      // Capabilities
      $this->skeleton->addCapabilities(['view', 'edit', 'create', 'delete']);
      $plural = strtolower($this->config['plural'] ?? $this->content . 's');
      $canPublish = jvbUserIsVerified() && user_can($this->user_id, "publish_{$plural}");
      $plural = strtolower($this->registrar->getPlural() ?? $this->content . 's');
      $canPublish = $this->userIsVerified() && user_can($this->user_id, "publish_{$plural}");
      $this->skeleton->userCanPublish($canPublish);
      // Bulk actions
      $this->skeleton->addBulkActions(['edit', 'publish', 'draft', 'trash']);
      // Uploader
      $this->setupUploader();
      if ($this->registrar->getType() === 'post') {
         $this->setupUploader();
      }
      // Sticky fields
      $stuck = ['post_title', 'term_name'];
@@ -121,33 +123,39 @@
      add_filter('jvbAdditionalActions', [$this, 'createItem']);
   }
   protected function userIsVerified():bool {
      $membership = Site::membership();
      return !($membership && $membership->has('member_verified')) || current_user_can('skip_moderation');
   }
   /**
    * Setup uploader configuration
    */
   protected function setupUploader(): void {
      $isSingleImage = jvbCheck('single_image', $this->config);
      $isSingleImage = $this->registrar->hasFeature('single_image');
      $config = [
         'type' => 'upload',
         'subtype' => 'image',
         'mode' => $isSingleImage ? 'direct' : 'selection',
         'create_new' => true,
         'label' => $this->config['upload_title'] ?? 'Bulk Upload ' . $this->config['plural'],
         'label' => $this->registrar->getUploadTitle(),
         'content' => $this->content,
         'singular' => $this->config['singular'],
         'plural' => $this->config['plural'],
         'singular' => $this->registrar->getSingular(),
         'plural' => $this->registrar->getPlural(),
         'multiple' => true,
         'destination' => $isSingleImage ? 'post' : 'post_group'
      ];
      if (!$isSingleImage) {
         $config['upload_text'] = '<p>Drag images into groups. Each group becomes its own ' . $this->config['singular'] . '.</p>
         $config['upload_text'] = '<p>Drag images into groups. Each group becomes its own ' . $this->registrar->getSingular() . '.</p>
            <p>You can also select multiple images and click the "Add to Group" button.</p>
            <p>If a ' . $this->config['singular'] . ' has multiple images, you can select the ' . jvbDashIcon('star') . ' to set an image as the main one.</p>
            <p>Images left ungrouped will become individual ' . $this->config['plural'] . '</p>
            <p>If a ' . $this->registrar->getSingular() . ' has multiple images, you can select the ' . jvbDashIcon('star') . ' to set an image as the main one.</p>
            <p>Images left ungrouped will become individual ' . $this->registrar->getPlural() . '</p>
            <p>Once finished, click the \'Save Changes\' button to send to server for processing.</p>';
      } else {
         $config['description'] = 'Each image will become its own ' . $this->config['singular'] . '.';
         $config['description'] = 'Each image will become its own ' . $this->registrar->getSingular() . '.';
      }
      $this->skeleton->addUploader($config);
@@ -157,60 +165,7 @@
    * Initialize taxonomies from WordPress config
    */
   protected function initTaxonomies(): void {
      $this->taxonomies = array_filter(JVB_TAXONOMY, function ($config) {
         return in_array($this->content, $config['for_content']);
      });
   }
   /**
    * Get statuses - calendar or standard
    */
   protected function getStatuses(): array {
      return array_key_exists('is_calendar', $this->config) ?
         [
            'all' => [
               'icon' => 'calendar',
               'label' => 'Everything',
            ],
            'future' => [
               'label' => 'Upcoming',
               'icon' => 'clock-clockwise',
            ],
            'past' => [
               'label' => 'Past',
               'icon' => 'clock-counter-clockwise',
            ],
            'repeat' => [
               'label' => 'Recurring',
               'icon' => 'repeat',
            ],
            'draft' => [
               'icon' => 'eye-closed',
               'label' => 'Hidden',
            ],
            'trash' => [
               'label' => 'Scrapped',
               'icon' => 'trash',
            ],
         ] :
         [
            'all' => [
               'icon' => 'infinity',
               'label' => 'Everything',
            ],
            'publish' => [
               'icon' => 'eye',
               'label' => 'Live',
            ],
            'draft' => [
               'icon' => 'eye-closed',
               'label' => 'Hidden',
            ],
            'trash' => [
               'label' => 'Scrapped',
               'icon' => 'trash',
            ],
         ];
      $this->taxonomies = ($this->registrar->getType() === 'post') ? $this->registrar->registrar->taxonomies : [];
   }
   /**
@@ -218,9 +173,9 @@
    */
   public function createItem(array $actions): array {
      $actions[] = [
         'button' => '<button type="button" class="create-item row" title="Create New ' . $this->config['singular'] . '">'
         'button' => '<button type="button" class="create-item row" title="Create New ' . $this->registrar->getSingular() . '">'
            . jvbDashIcon('plus-square')
            . '<span class="screen-reader-text">Create New ' . $this->config['singular'] . '</span></button>',
            . '<span class="screen-reader-text">Create New ' . $this->registrar->getSingular() . '</span></button>',
         'content' => '', // Modal is rendered by skeleton
      ];