<?php
|
namespace JVBase\managers;
|
|
use JVBase\ui\CRUDSkeleton;
|
use JVBase\utility\Features;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* WordPress CRUD Manager
|
* Configures CRUDSkeleton for WordPress post types
|
*/
|
class CRUD {
|
protected CRUDSkeleton $skeleton;
|
protected CacheManager $cache;
|
protected array $config;
|
protected string $content;
|
protected array $taxonomies = [];
|
protected int $user_id;
|
protected ?string $type = null;
|
protected ?array $constant = null;
|
|
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_id = get_current_user_id();
|
$this->config = $this->constant[$content];
|
$this->content = $content;
|
$this->cache = CacheManager::for($content);
|
|
// 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'
|
];
|
|
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);
|
}
|
|
/**
|
* 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',
|
],
|
];
|
}
|
|
/**
|
* Add create button to dashboard actions
|
*/
|
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 addDateRanges():array
|
{
|
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
|
FROM $wpdb->posts
|
WHERE post_type = '{$postType}'
|
AND post_author = '{$this->user_id}'
|
ORDER BY post_date DESC
|
");
|
$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;
|
}
|
return $ranges;
|
}
|
);
|
}
|
|
/**
|
* Render the interface
|
*/
|
public function render(): void {
|
$this->skeleton->render();
|
}
|
|
/**
|
* Get the skeleton instance for further customization
|
*/
|
public function getSkeleton(): CRUDSkeleton {
|
return $this->skeleton;
|
}
|
}
|