From 0113d2e9c9ff34a6ffb10707cc76d34b67a0c367 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 19 Jan 2026 16:29:41 +0000
Subject: [PATCH] =Refactored window.getTemplate into a full templating class window.jvbTemplates. Refactored CRUD.js, UploadManager.js, FormController.js, PopulateForm.js with that in mind
---
inc/managers/CRUDManager.php | 1170 ++++++++++------------------------------------------------
1 files changed, 209 insertions(+), 961 deletions(-)
diff --git a/inc/managers/CRUDManager.php b/inc/managers/CRUDManager.php
index 6deb11e..b868510 100644
--- a/inc/managers/CRUDManager.php
+++ b/inc/managers/CRUDManager.php
@@ -1,472 +1,245 @@
<?php
namespace JVBase\managers;
-use JVBase\managers\UserTermsManager;
-use JVBase\meta\MetaManager;
-use WP_User;
+use JVBase\ui\CRUDSkeleton;
+use JVBase\utility\Features;
if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
+ exit;
}
+/**
+ * WordPress CRUD Manager
+ * Configures CRUDSkeleton for WordPress post types
+ */
class CRUD {
- protected WP_User $user;
- protected int $user_id;
+ protected CRUDSkeleton $skeleton;
+ protected CacheManager $cache;
protected array $config;
protected string $content;
- protected string $singular;
- protected string $plural;
- protected array $filters;
- protected array $bulkActions;
- protected MetaManager $meta;
- protected array $taxonomies;
- protected array $statuses;
- protected array $fields;
- protected array $sections;
- protected array $stuck;
+ protected array $taxonomies = [];
+ protected int $user_id;
+ protected ?string $type = null;
+ protected ?array $constant = null;
- protected bool $userCanPublish = false;
-
- public function __construct(string $content)
- {
- //If we haven't defined this content, bail early
- if (!array_key_exists($content, JVB_CONTENT)) {
+ 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 {
return;
}
- $this->user = wp_get_current_user();
- $this->user_id = $this->user->ID;
- $this->config = JVB_CONTENT[$content];
- $this->singular = $this->config['singular'];
- $this->plural = $this->config['plural'];
+
+ $this->user_id = get_current_user_id();
+ $this->config = $this->constant[$content];
$this->content = $content;
- $this->fields = jvbGetFields($this->content, 'post');
- $this->sections = jvbGetSections($this->content, 'post');
- $this->stuck = [
- 'post_title',
- 'term_name'
+ $this->cache = CacheManager::for($content);
+
+ $this->cache->clear();
+
+ // Create and configure skeleton
+ $this->skeleton = new CRUDSkeleton();
+ $this->configure();
+ }
+
+ /**
+ * Configure CRUDSkeleton from WordPress config
+ */
+ protected function configure(): void {
+ // Basic info
+ $this->skeleton
+ ->content($this->content, $this->config['singular'], $this->config['plural'])
+ ->title(
+ 'Your ' . $this->config['plural'],
+ $this->config['page_description'] ?? ''
+ );
+
+ // Initialize meta
+ $this->skeleton->initMeta($this->type, $this->content);
+
+ $this->skeleton->addSearch();
+
+ // Timeline if applicable
+ if (Features::forContent($this->content)->has('is_timeline')) {
+ $this->skeleton->setTimeline();
+ }
+
+ // Fields and sections
+ $this->skeleton->setFields($this->config['fields']);
+
+ $sections = array_key_exists('sections', $this->config) ? $this->config['sections'] : [];
+ foreach ($sections as $id => $config) {
+ $this->skeleton->addSection($id, $config);
+ }
+
+ // Taxonomies
+ $this->initTaxonomies();
+
+ // Statuses
+ if (Features::forContent($this->content)->has('is_calendar')) {
+ $this->skeleton->setCalendar();
+ }
+
+ $this->skeleton->setDefaultStatus();
+
+ // Views
+ $this->skeleton
+ ->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');
+ }
+
+ // 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}");
+ $this->skeleton->userCanPublish($canPublish);
+
+ // Bulk actions
+ $this->skeleton->addBulkActions(['edit', 'publish', 'draft', 'trash']);
+
+ // Uploader
+ $this->setupUploader();
+
+ // Sticky fields
+ $stuck = ['post_title', 'term_name'];
+ if ($this->skeleton->get('isTimeline')) {
+ $stuck[] = 'post_thumbnail';
+ }
+ $this->skeleton->stickFields($stuck);
+
+ // Hook for create button
+ add_filter('jvbAdditionalActions', [$this, 'createItem']);
+ }
+
+ /**
+ * Setup uploader configuration
+ */
+ protected function setupUploader(): void {
+ $isSingleImage = jvbCheck('single_image', $this->config);
+
+ $config = [
+ 'type' => 'upload',
+ 'subtype' => 'image',
+ 'mode' => $isSingleImage ? 'direct' : 'selection',
+ 'create_new' => true,
+ 'label' => $this->config['upload_title'] ?? 'Bulk Upload ' . $this->config['plural'],
+ 'content' => $this->content,
+ 'singular' => $this->config['singular'],
+ 'plural' => $this->config['plural'],
+ 'multiple' => true,
+ 'destination' => $isSingleImage ? 'post' : 'post_group'
];
- $this->init();
+ if (!$isSingleImage) {
+ $config['upload_text'] = '<p>Drag images into groups. Each group becomes its own ' . $this->config['singular'] . '.</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>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'] . '.';
+ }
+
+ $this->skeleton->addUploader($config);
}
- protected function init():void
- {
- $this->initStatuses();
- $this->initBulkActions();
- $this->initTaxonomies();
- $this->initFilters();
- $this->meta = new MetaManager(null, 'post', $this->content);
-
- $plural = strtolower($this->config['plural']??$this->content.'s');
- $this->userCanPublish = (jvbUserIsVerified()) ?
- user_can($this->user_id, "publish_{$plural}") : false;
-
- }
-
- protected function initTaxonomies():void
- {
+ /**
+ * 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']);
});
}
- protected function initStatuses():void
- {
- $this->statuses = (array_key_exists('is_calendar', $this->config)) ?
+ /**
+ * Get statuses - calendar or standard
+ */
+ protected function getStatuses(): array {
+ return array_key_exists('is_calendar', $this->config) ?
[
- 'all' => [
- 'icon' => 'calendar',
+ 'all' => [
+ 'icon' => 'calendar',
'label' => 'Everything',
],
- 'future'=> [
- 'label' => 'Upcoming',
- 'icon' => 'future',
+ 'future' => [
+ 'label' => 'Upcoming',
+ 'icon' => 'clock-clockwise',
],
- 'past' => [
- 'label' => 'Past',
- 'icon' => 'past',
+ 'past' => [
+ 'label' => 'Past',
+ 'icon' => 'clock-counter-clockwise',
],
- 'repeat'=> [
- 'label' => 'Recurring',
- 'icon' => 'repeat',
+ 'repeat' => [
+ 'label' => 'Recurring',
+ 'icon' => 'repeat',
],
- 'draft' => [
- 'icon' => 'hide',
- 'label' => 'Hidden',
+ 'draft' => [
+ 'icon' => 'eye-closed',
+ 'label' => 'Hidden',
],
- 'trash' => [
- 'label' => 'Scrapped',
- 'icon' => 'delete',
+ 'trash' => [
+ 'label' => 'Scrapped',
+ 'icon' => 'trash',
],
] :
[
- 'all' => [
- 'icon' => 'all',
+ 'all' => [
+ 'icon' => 'infinity',
'label' => 'Everything',
],
- 'publish'=> [
- 'icon' => 'publish',
- 'label' => 'Live',
+ 'publish' => [
+ 'icon' => 'eye',
+ 'label' => 'Live',
],
- 'draft' => [
- 'icon' => 'hide',
- 'label' => 'Hidden',
+ 'draft' => [
+ 'icon' => 'eye-closed',
+ 'label' => 'Hidden',
],
- 'trash' => [
- 'label' => 'Scrapped',
- 'icon' => 'delete',
+ 'trash' => [
+ 'label' => 'Scrapped',
+ 'icon' => 'trash',
],
];
}
- protected function initBulkActions():void
- {
- $this->bulkActions = [
- 'edit' => 'Edit',
- 'publish' => 'Show',
- 'draft' => 'Hide',
-// 'copy' => 'Duplicate',
- 'trash' => 'Scrap'
- ];
- }
-
- protected function initFilters():void
- {
- $this->filters = [
- 'status' => $this->statuses,
- 'date' => [
- 'label' => 'Date',
- 'icon' => 'calendar'
- ]
- ];
-
- foreach ($this->taxonomies as $taxonomy=> $config) {
- $this->filters['taxonomy'][$taxonomy] = [
- 'label' => $config['singular'],
- 'icon' => $taxonomy
- ];
- }
- }
-
- public function render():void
- {
- ob_start();
- ?>
- <div class="dashboard-page <?= esc_attr($this->content) ?>">
- <?php
- $this->renderHeader();
- $this->renderContent();
- $this->renderModals();
- $this->renderTemplates();
- ?>
- </div>
- <?php
- echo ob_get_clean();
-
- }
-
- protected function renderHeader():void
- {
- ?>
- <h1>Your <?= $this->config['plural'] ?></h1>
- <?php
- if (array_key_exists('page_description', $this->config)) {
- ?>
- <p class="page-description"><?=$this->config['page_description']?></p>
- <?php
- }
- $this->renderHeaderActions();
- }
-
- protected function renderHeaderActions():void
- {
- $uploadConfig = [
- 'type' => 'upload',
- 'subtype' => 'image',
- 'mode' => (jvbCheck('single_image', $this->config)) ? 'direct' : 'selection',
- 'create_new' => true,
- 'label' => (array_key_exists('image_title', $this->config)) ? $this->config['image_title'] : 'Upload More '.$this->config['plural'],
- 'content' => $this->content,
- 'singular' => $this->singular,
- 'plural' => $this->plural,
- 'multiple' => true,
- 'destination' => 'post'
- ];
- if (!jvbCheck('single_image', $this->config)) {
- $uploadConfig['destination'] = 'post_group';
- }
- $uploadConfig['destination'] = 'post_group';
- if (!jvbCheck('single_image', $this->config)) {
- $uploadConfig['group_title'] = 'Create '.$this->config['plural'];
- $uploadConfig['group_description'] = '<p>Drag images into groups. Each group becomes its own '.$this->singular.'.</p>
- <p>You can also select multiple images and click the "Add to Group" button.</p>
- <p>If a '.$this->singular.' has multiple images, you can select the '.jvbIcon('star').' to set an image as the main one.</p>
- <p>Images left ungrouped will become individual '.$this->plural.'</p>
- <p>Once finished, click the \'Save Changes\' button to send to server for processing.</p>';
- } else {
- $uploadConfig['description'] = 'Each image will become its own '.$this->singular.'.';
- }
- ?>
- <button type="button" class="create-item row" title="Create New <?= $this->singular?>"><?=jvbIcon('add') ?><span class="screen-reader-text">Create New <?= $this->singular?></span></button>
- <details open class="uploader">
- <summary class="row btw"><?= $this->config['upload_title'] ?? 'Bulk Upload '.$this->plural?></summary>
- <?php
- $this->meta->render(
- 'form',
- 'new_'.$this->content,
- $uploadConfig
- );
- ?>
- </details>
- <?php
- }
-
- protected function renderContent():void
- {
- ?>
- <section class="items-list <?=$this->content?> crud" data-content="<?= $this->content ?>">
- <?php
- $this->renderFilters();
- $this->renderBulkControls();
- ?>
- <div class="<?= $this->content ?> item-grid" role="grid"></div>
- <div class="scroll-sentinel" aria-hidden="true"></div>
- </section>
- <?php
- $state = apply_filters('jvbEmptyState', $this->renderEmptyState(), $this->content);
-
- echo '<template class="emptyState">'.$state.'</template>';
- ?>
- <?php
- }
-
- protected function renderEmptyState():string
- {
- ob_start();
- ?>
- <div class="empty-state">
- <h3><?=jvbIcon($this->content)?>Nothing here<?=jvbIcon($this->content)?></h3>
- <p>It doesn't look like you have any <?=$this->config['plural'] ?> yet.</p>
- <p><small><i>Add many by uploading images above.</i>, or click the "<?=jvbIcon('add')?>" button to add one at a time.</small></p>
- </div>
- <?php
- return ob_get_clean();
- }
-
- protected function renderFilters():void
- {
- ?>
- <div class="all-filters col start">
- <div class="search row start nowrap">
- <span class="label">Search:</span>
- <?= jvbSearch() ?>
- </div>
- <div class="controls col start">
- <?php
- $this->renderViewFilters();
- $this->renderStatusFilters();
- $this->renderOrderFilters();
- ?>
- </div>
- <div class="filters row start">
- <span class="label">Filters:</span>
- <?php
- $this->renderTaxonomyFilters();
- $this->renderDateFilters();
- ?>
- <button type="button" class="clear-filters row" hidden>
- <?= jvbIcon('close', ['title' => 'Clear']); ?>
- Clear All Filters
- </button>
- </div>
-
- <?= $this->renderColumnSelector(); ?>
- </div>
- <?php
- }
-
- protected function renderOrderFilters():void
- {
- ?>
- <div class="radio-options order row btw w-full">
- <?php
- $order = [
- 'orderby' => [
- 'date' => 'Order by date created',
- 'alphabetical' => 'Order alphabetically'
- ],
- 'order' => [
- 'asc' => 'In ascending order (Z-A, oldest to newest)',
- 'desc' => 'In descending order (A-Z, newest to oldest)'
- ]
- ];
-
- foreach ($order as $o => $option) {
- ?>
- <div class="row start">
- <span class="label"><?= ucfirst($o)?>:</span>
- <?php
- $i = 0;
- foreach ($option as $opt => $label) {
- ?>
- <input id="<?=$opt?>" class="btn" type="radio" name="<?=$o?>" data-filter="<?=$o?>" value="<?=$opt?>"<?=$i===0 ? ' checked':''?>>
-
- <label for="<?=$opt?>" title="<?=$label?>"><?=jvbIcon($opt)?></label>
- <?php
- $i++;
- }
- ?>
- </div>
- <?php
- }
- ?>
- </div>
- <?php
- }
- protected function renderStatusFilters():void
- {
- if (empty($this->statuses)) {
- return;
- }
- ?>
- <div class="radio-options status row">
- <span class="label">Status:</span>
- <?php
- $i = 1;
- foreach ($this->statuses as $status => $config) {
- $checked = ($i == 1) ? ' checked' : '';
- ?>
- <input type="radio" class="btn" data-filter="status" value="<?=$status?>" name="status" id="<?=$status?>"<?=$checked?>>
- <label for="<?=$status?>">
- <?= jvbIcon($config['icon']) ?>
- <span><?=$config['label']?><span class="count"></span></span>
- </label>
- <?php
- $i++;
- }
- ?>
- </div>
- <?php
- }
-
- protected function renderViewFilters():void
- {
- ?>
- <div class="radio-options view row">
- <span class="label">View:</span>
-
- <?php
- $views = [
- 'grid' => ['icon' => 'grid', 'label' => 'Grid View'],
- 'list' => ['icon' => 'list', 'label' => 'List View'],
- 'table' => ['icon' => 'table', 'label' => 'Table View']
- ];
-
- $first = true;
- foreach ($views as $view => $config):
- ?>
- <input type="radio"
- data-view="<?= esc_attr($view) ?>"
- value="<?= esc_attr($view) ?>"
- class="btn"
- name="view"
- id="view-<?= esc_attr($view) ?>"
- <?= $first ? 'checked' : '' ?>>
- <label for="view-<?= esc_attr($view) ?>"
- title="<?= esc_attr($config['label']) ?>">
- <?= jvbIcon($config['icon']) ?>
- <span class="screen-reader-text"><?= esc_html($config['label']) ?></span>
- </label>
- <?php
- $first = false;
- endforeach;
- ?>
- </div>
- <?php
- }
/**
- * Render column selector for table view
+ * Add create button to dashboard actions
*/
- protected function renderColumnSelector(): string {
- ob_start();
- ?>
- <details class="multi-select" title="Select columns" hidden>
- <summary class="row start nowrap">
- <?= jvbIcon('columns') ?>
- <span class="labels">Toggle Columns</span>
- </summary>
- <div class="column-list">
- <?php foreach ($this->fields as $fieldName => $config):
- if (array_key_exists('hidden', $config)){
- continue;
- }
- ?>
- <input type="checkbox"
- id="show-<?= esc_attr($fieldName) ?>"
- class="column-toggle ch"
- name="show-<?= esc_attr($fieldName) ?>"
- checked>
- <label for="show-<?= esc_attr($fieldName) ?>">
- <?= esc_html($config['label']) ?>
- </label>
- <?php endforeach; ?>
- </div>
- </details>
- <?php
- return ob_get_clean();
+ public function createItem(array $actions): array {
+ $actions[] = [
+ 'button' => '<button type="button" class="create-item row" title="Create New ' . $this->config['singular'] . '">'
+ . jvbDashIcon('plus-square')
+ . '<span class="screen-reader-text">Create New ' . $this->config['singular'] . '</span></button>',
+ 'content' => '', // Modal is rendered by skeleton
+ ];
+
+ return $actions;
}
- protected function renderTaxonomyFilters():void
+
+ protected function addDateRanges():array
{
- if (empty($this->taxonomies)) {
- return;
- }
- $out = '';
- foreach ($this->taxonomies as $taxonomy => $config) {
- $terms = $this->getCommonTerms($taxonomy);
- if (!empty($terms)) {
- $out .= sprintf(
- '<div class="row nowrap"><label for="filter-%s">%s<span class="screen-reader-text">Filter by %s</span></label>
- <select id="filter-%s" class="filter %s" name="%s" data-filter="taxonomies" data-taxonomy="%s">
- <option value="">by %s</option>',
- $taxonomy,
- jvbIcon($config['icon'], ['title' => $config['plural']]),
- esc_html($config['plural']),
- $taxonomy,
- $taxonomy,
- $taxonomy,
- $taxonomy,
- $config['plural']
- );
-
-
- foreach ($terms as $term) {
- $out .= sprintf(
- '<option value="%s">%s</option>',
- esc_attr($term['term_id']),
- esc_html($term['name'])
- );
- }
- $out .= '</select></div>';
- }
- }
- echo $out;
- }
- /**
- * Get common terms for taxonomy
- * @param string $taxonomy
- * @return array
- */
- protected function getCommonTerms(string $taxonomy):array {
- $manager = new UserTermsManager();
- return $manager->getUserTerms($this->user_id, $taxonomy);
- }
-
- protected function renderDateFilters():void
- {
- $postType = jvbCheckBase($this->content);
- // Get available months
- global $wpdb;
- $months = $wpdb->get_results("
+ return $this->cache->remember(
+ 'dateRanges',
+ function() {
+ $postType = jvbCheckBase($this->content);
+ // Get available months
+ global $wpdb;
+ $months = $wpdb->get_results("
SELECT DISTINCT
YEAR(post_date) as year,
MONTH(post_date) as month
@@ -475,553 +248,28 @@
AND post_author = '{$this->user_id}'
ORDER BY post_date DESC
");
-
- // Quick filters
- $out = '<div class="row nowrap">
- <label for="filter-date">'.
- jvbIcon('calendar',['title'=>'Date']).
- '<span class="screen-reader-text">by Date</span>
- </label>
- <select id="filter-date" class="date-filter" data-filter="date">
- <option value="">by Date</option>
- <option value="today">Today</option>
- <option value="week">Past Week</option>
- <option value="month">Past Month</option>
- <option value="year">Past Year</option>
- <option value="custom">Custom Range...</option>
- </select>
- </div>';
-
- $form = '<div class="custom-range row">
- <label for="date-start" class="col">
- From
- </label>
- <input type="date" id="date-start" class="date-start">
- <label for="date-end" class="col">
- To
- </label>
- <input type="date" id="date-end" class="date-end">
- </div>
- <div class="month-picker">
- <label>
- <span>Or select month</span>
- <select class="month-select">
- <option value="">  . . .  </option>';
-
-
- foreach ($months as $date) {
- $month_name = date('F Y', mktime(0, 0, 0, $date->month, 1, $date->year));
- $value = $date->year . '-' . str_pad($date->month, 2, '0', STR_PAD_LEFT);
- $form .= sprintf(
- '<option value="%s">%s</option>',
- esc_attr($value),
- esc_html($month_name)
- );
- }
-
- $form .= '</select>
- </label>
- </div>';
-
- // Custom date range
- $out .= jvbNewModal(
- 'date-range',
- 'Filter Results by Date:',
- $form
- );
-
- echo $out;
- }
-
- protected function renderBulkControls():void
- {
- if (empty($this->bulkActions)) {
- return;
- }
- ?>
- <div class="bulk-controls row nowrap btw">
- <div class="bulk-select">
- <input type="checkbox" id="select-all" class="select-all">
- <label for="select-all" class="row"><span>Select All</span><span class="selected-count" hidden></span></label>
- </div>
- <div class="bulk-actions row nowrap" hidden>
- <label for="bulk-action-select" class="screen-reader-text">
- Select what to do with this selection.
- </label>
- <select class="bulk-action-select" id="bulk-action-select">
-
- </select>
- </div>
- </div>
-
- <template class="notTrashOptions">
- <select class="wrap">
- <option value="">Bulk Actions...</option>
- <?php
- foreach ($this->bulkActions as $control => $label) {
- $disabled = ($control === 'publish' && !$this->userCanPublish) ? ' disabled' : '';
- ?>
- <option value="<?=$control?>"<?=$disabled?>><?=$label?></option>
- <?php
+ $ranges = [];
+ foreach ($months as $date) {
+ $month_name = date('F Y', mktime(0, 0, 0, $date->month, 1, $date->year));
+ $value = $date->year . '-' . str_pad($date->month, 2, '0', STR_PAD_LEFT);
+ $ranges[$value] = $month_name;
}
- foreach ($this->taxonomies as $taxonomy => $config) {
- ?>
- <option value="tax-<?=$taxonomy?>">Add to <?= $config['singular'] ?></option>
- <?php
- }
- ?>
- </select>
-
- </template>
- <template class="trashOptions">
- <select class="wrap">
- <option value="">Bulk Actions...</option>
- <option value="restore">Restore</option>
- <option value="delete">Permanently Delete</option>
- </select>
- </template>
- <?php
- }
-
- protected function renderModals():void
- {
- $this->renderCreateModal();
- $this->renderEditModal();
- $this->renderBulkEditModal();
- }
- protected function renderCreateModal():void
- {
- echo jvbNewModal(
- 'create',
- 'Creating <span class="count"></span> New '.$this->config['singular'],
- str_replace('edit-form"', 'create-form" data-noautosave', $this->editForm())
- );
- }
-
- protected function editForm():string
- {
- ob_start();
- ?>
- <form class="edit-form" data-save="content" data-form-id="edit-<?=$this->content?>">
- <input type="hidden" name="form-id" value="<?=uniqid('new-')?>" />
- <input type="hidden" name="content" value="<?=$this->content?>" />
- <div class="fields">
- <div class="field-group radio-options row">
- <span>Status:</span>
- <?php
- $this->getApplicableStatuses('edit');
- ?>
- </div>
- <?php if (!$this->userCanPublish) { ?>
- <p class="description">Your account needs to be verified before you can publish content.</p>
- <?php }
-
- if (!empty($this->sections)) {
- $tabs = [];
- foreach ($this->sections as $slug => $title) {
- $tabs[$slug] = [
- 'title' => $title,
- 'content' => '',
- 'description' => jvbSectionDescription($slug)??'',
- ];
- $icon = jvbSectionIcon($slug);
- if ($icon !== '') {
- $tabs[$slug]['icon'] = $icon;
- }
- }
- } else {
- $tabs = false;
- }
- $fields = $this->fields;
-
- $first = ['post_thumbnail', 'post_title', 'price'];
- foreach ($first as $f) {
- if (array_key_exists($f, $fields)) {
- if ($tabs) {
- $tabs['basic']['content'] .= $this->meta->render('form', $f, $fields[$f], false, true);
- } else {
- $this->meta->render('form', $f, $fields[$f]);
- }
-
- unset($fields[$f]);
- }
- }
- foreach ($fields as $n => $config) {
- if ($tabs) {
- $section = (array_key_exists('section', $config)) ? $config['section'] : 'basic';
- $tabs[$section]['content'] .= $this->meta->render('form', $n, $config, false, true);
- } else {
- $this->meta->render('form', $n, $config);
- }
-
- }
-
-
- if ($tabs) {
- jvbRenderTabs($tabs);
- }
- ?>
- </div>
- </form>
- <?php
- return ob_get_clean();
- }
-
- protected function getApplicableStatuses(string $prefix) {
- foreach ($this->statuses as $status => $config) {
- if ($status === 'all') {
- continue;
+ return $ranges;
}
- if (in_array($status, ['future', 'past'])) {
- if ($status === 'future') {
- $status = 'publish';
- $config = [
- 'icon' => 'publish',
- 'label' => 'Live',
- ];
- } else {
- continue;
- }
- }
- $disabled = ($status === 'publish' && !$this->userCanPublish) ? ' disabled' : '';
- ?>
- <input type ="radio"
- name="post_status"
- class="btn"
- value="<?= esc_attr($status)?>"
- id="<?=$prefix?>set-<?= esc_attr($status) ?>"
- <?= $disabled?>>
- <label for="<?=$prefix?>set-<?=esc_attr($status)?>">
- <?= jvbIcon($config['icon'], ['title' => $config['label']]) ?>
- <span><?= esc_html($config['label'])?></span>
- </label>
- <?php
- }
- }
- protected function renderEditModal():void
- {
- echo jvbNewModal(
- 'edit',
- 'Edit your '.$this->singular,
- $this->editForm()
);
}
- protected function renderBulkEditModal():void
- {
- if (empty($this->bulkActions)) return;
- ob_start();
- ?>
- <form class="bulk-edit-form" data-save="content" data-form-id="bulk-edit-<?=$this->content?>">
- <div class="selected"></div>
- <p class="description">You can unselect items by clicking the image here.</p>
- <p class="hint"><strong>IMPORTANT: </strong> Whatever changes you make here will be applied to all selected <?=$this->plural?>.</p>
- <div class="fields">
- <div class="field-group radio-options row">
- <?php
- $this->getApplicableStatuses('bulk-');
- ?>
- </div>
- <?php
- if (!empty($this->taxonomies)) {
- ?>
- <div class="taxonomies">
- <?php
- foreach ($this->taxonomies as $taxonomy => $config) {
- $this->meta->render(
- 'form',
- 'bulk-edit-'.$taxonomy,
- [
- 'type' => 'taxonomy',
- 'label' => $config['singular'],
- 'taxonomy' => $taxonomy,
- 'createNew' => jvbUserIsVerified(),
- 'multiple' => true,
- 'mode' => 'append'
- ]
- );
- }
- ?>
- </div>
- <?php
- }
- $fields = $this->fields;
- $fields = array_filter($fields, function ($field) {
- return array_key_exists('bulkEdit', $field);
- });
- foreach ($fields as $fieldName => $config) {
- $this->meta->render('form', $fieldName, $config);
- }
- ?>
- </div>
- </form>
- <template class="bulkItem">
- <label>
- <input type="checkbox">
- <img>
- </label>
- </template>
- <?php
- $form = ob_get_clean();
- echo jvbNewModal(
- 'bulkEdit',
- 'Bulk Edit <span class="selected"></span> '.$this->config['plural'],
- $form
- );
- }
-
- protected function renderTemplates():void
- {
- $this->renderListView();
- $this->renderGridView();
- $this->renderTableView();
- $this->renderTableRow();
- echo jvbGetEmptyStateTemplate();
- echo jvbGetGalleryPreviewTemplate();
-
-
- }
-
- protected function renderItemSelect():string
- {
- ob_start();
- ?>
- <div class="item-select">
- <input type="checkbox" class="select-item">
- <label class="select-item-label">
- <span class="screen-reader-text">Select this <?= $this->singular ?></span>
- </label>
- </div>
- <?php
- return ob_get_clean();
- }
-
- protected function renderImage():string
- {
- ob_start();
- ?>
- <img loading="lazy" alt="">
- <?php
- return ob_get_clean();
- }
-
- protected function renderItemActions():string
- {
- ob_start();
- ?>
- <div class="item-actions">
- <button type="button" class="action" data-action="edit" title="Edit <?= $this->singular ?>">
- <?=jvbIcon('edit')?>
- <span class="screen-reader-text">Edit <?= $this->singular ?></span>
- </button>
- <button type="button" class="action" data-action="trash" title="Scrap <?= $this->singular ?>">
- <?=jvbIcon('delete')?>
- <span class="screen-reader-text">Scrap <?= $this->singular ?></span>
- </button>
-<!-- <button type="button" class="action" data-action="toggle-status">-->
-<!-- <span class="screen-reader-text">Toggle --><?php //= $this->singular ?><!-- Visibility</span>-->
-<!-- </button>-->
- </div>
- <?php
- return ob_get_clean();
- }
-
- protected function renderItemFields(bool $form = false):string
- {
- ob_start();
- foreach ($this->fields as $name => $config) {
- $renderMode = $form ? 'form' : 'render';
-
- $field = $this->meta->render($renderMode, $name, $config, false, true);
-
- // Special handling for title in grid view
- if ($name === 'post_title' && !$form) {
- $field = str_replace('<p', '<h3', str_replace('</p>', '</h3>', $field));
- }
-
- echo $field;
- }
- return ob_get_clean();
- }
-
- protected function renderGridView():void
- {
- ?>
- <template class="gridView">
- <div class="item <?= $this->content ?>">
- <input type="checkbox" class="select-item" name="select-item">
- <label title="Select this <?= $this->singular?>" class="select-item-label">
- <?= $this->renderImage() ?>
- </label>
- <?= $this->renderItemActions(); ?>
- </div>
- </template>
- <?php
- }
-
- protected function renderListView():void
- {
- ?>
- <template class="listView">
- <div class="item <?=esc_attr($this->content)?> row nowrap">
- <?= $this->renderItemSelect()?>
- <?=$this->renderImage() ?>
- <div class="col start w-full">
- <?= $this->renderItemActions()?>
- <h3 data-field="post_title"></h3>
- <p data-attr="date"></p>
- <p data-field="price"></p>
- <div data-field="post_excerpt"></div>
- </div>
- </div>
- </template>
- <?php
- }
-
- protected function renderTableView():void
- {
- ?>
- <template class="contentTable">
- <form class="table"
- data-save="content"
- data-content="<?= esc_attr($this->content) ?>"
- data-form-id="content-table-<?= esc_attr($this->content) ?>">
-
- <?= $this->renderTableActions() ?>
-
- <table>
- <thead>
- <?= $this->renderTableHeader() ?>
- </thead>
- <tbody>
- <!-- Rows will be inserted here -->
- </tbody>
- <tfoot>
- <?= $this->renderTableHeader() ?>
- </tfoot>
- </table>
- </form>
- </template>
- <?php
- }
/**
- * Render table row template
+ * Render the interface
*/
- protected function renderTableRow(): void {
- ?>
- <template class="tableView">
- <tr class="item">
- <td class="select">
- <?= $this->renderItemSelect() ?>
- </td>
- <td class="status">
- <?= $this->renderStatusRadios() ?>
- </td>
- <?php
- $makeDetails = [
- 'group',
- 'repeater',
- 'checkbox',
- 'radio'
- ];
- foreach ($this->fields as $name => $config):
- if (array_key_exists('hidden', $config)){
- continue;
- }
- $makeThisDetailed = (in_array($config['type'], $makeDetails));
- ?>
- <td class="field show-<?= esc_attr($name) ?>" data-field="<?= esc_attr($name) ?>" data-field-type="<?=$config['type']?>"<?=(in_array($name, $this->stuck)) ? ' data-stuck':''?>>
- <?= $makeThisDetailed ? '<details><summary class="row btw">See Value</summary>' : '' ?>
- <?php $this->meta->render('form', $name, $config); ?>
- <?= $makeThisDetailed ? '</details>' : '' ?>
- </td>
- <?php endforeach; ?>
- </tr>
- </template>
- <?php
- }
- /**
- * Render status radio buttons
- */
- protected function renderStatusRadios(): string {
- ob_start();
- ?>
- <div class="radio-options status-options row">
- <?php foreach ($this->statuses as $status => $config):
- if ($status === 'all') continue;
-
- // Handle special cases
- if ($status === 'future') {
- $status = 'publish';
- $config = [
- 'icon' => 'publish',
- 'label' => 'Live'
- ];
- } elseif ($status === 'past') {
- continue;
- }
- ?>
- <input type="radio"
- name="post_status"
- id="status-<?= esc_attr($status) ?>"
- value="<?= esc_attr($status) ?>">
- <label for="status-<?= esc_attr($status) ?>">
- <?= jvbIcon($config['icon']) ?>
- <span class="screen-reader-text"><?= esc_html($config['label']) ?></span>
- </label>
- <?php endforeach; ?>
- </div>
- <?php
- return ob_get_clean();
- }
- /**
- * Render table header
- */
- protected function renderTableHeader(): string {
- ob_start();
-
- ?>
- <tr>
- <th scope="col" class="select-header">
- <input type="checkbox" id="select-all" name="select-all">
- <label for="select-all">All</label>
- </th>
- <th scope="col" class="status-header">Status</th>
- <?php foreach ($this->fields as $name => $config):
- if (array_key_exists('hidden', $config)){
- continue;
- }
- ?>
- <th scope="col" class="show-<?= esc_attr($name) ?>"<?= (in_array($name, $this->stuck)) ? ' data-stuck':''?>>
- <?= esc_html($config['label']) ?>
- </th>
- <?php endforeach; ?>
- </tr>
- <?php
- return ob_get_clean();
+ public function render(): void {
+ $this->skeleton->render();
}
/**
- * Render table action controls
+ * Get the skeleton instance for further customization
*/
- protected function renderTableActions(): string {
- ob_start();
- ?>
- <div class="table-actions row btw nowrap">
- <?= jvbRenderToggleTextField(
- 'vertical',
- 'TAB NAV:',
- '',
- jvbIcon('down'),
- jvbIcon('right')
- ) ?>
-
- <button type="button" class="add-row" title="Add new row">
- <?= jvbIcon('add') ?>
- <span>Add Row</span>
- </button>
- </div>
- <?php
- return ob_get_clean();
+ public function getSkeleton(): CRUDSkeleton {
+ return $this->skeleton;
}
}
--
Gitblit v1.10.0