<?php
|
namespace JVBase\managers;
|
|
use JVBase\base\Site;
|
use JVBase\registrar\Registrar;
|
use JVBase\ui\CRUDSkeleton;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* WordPress CRUD Manager
|
* Configures CRUDSkeleton for WordPress post types
|
*/
|
class CRUD {
|
protected CRUDSkeleton $skeleton;
|
protected Cache $cache;
|
protected string $content;
|
protected array $taxonomies = [];
|
protected int $user_id;
|
protected Registrar $registrar;
|
|
public function __construct(string $content) {
|
$this->registrar = Registrar::getInstance($content);
|
|
if (!$this->registrar) {
|
return;
|
}
|
|
$this->user_id = get_current_user_id();
|
$this->content = $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();
|
}
|
|
/**
|
* Configure CRUDSkeleton from WordPress config
|
*/
|
protected function configure(): void {
|
// Basic info
|
$this->skeleton
|
->content($this->content, $this->registrar->getSingular(), $this->registrar->getPlural())
|
->title(
|
$this->registrar->config('dashboard')->getTitle(),
|
$this->registrar->config('dashboard')->getDescription()?? ''
|
);
|
|
// Initialize meta
|
$this->skeleton->addSearch();
|
|
// Timeline if applicable
|
if ($this->registrar && $this->registrar->hasFeature('is_timeline')) {
|
$this->skeleton->setTimeline();
|
}
|
|
// Fields and sections
|
$this->skeleton->setFields($this->registrar->getFields());
|
|
foreach ($this->registrar->getSections() as $config) {
|
$this->skeleton->addSection($config['id'], $config);
|
}
|
|
// Taxonomies
|
$this->initTaxonomies();
|
|
// Statuses
|
if ($this->registrar->hasFeature('is_calendar')) {
|
$this->skeleton->setCalendar();
|
}
|
|
if ($this->registrar->getType() === 'post') {
|
$this->skeleton->setDefaultStatus();
|
} else {
|
$this->skeleton->setStatuses([]);
|
}
|
|
|
// 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($this->taxonomies, 'user');
|
}
|
|
// Capabilities
|
$this->skeleton->addCapabilities(['view', 'edit', 'create', 'delete']);
|
|
$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
|
if ($this->registrar->getType() === 'post') {
|
$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']);
|
}
|
|
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 = $this->registrar->hasFeature('single_image');
|
|
$config = [
|
'type' => 'upload',
|
'subtype' => 'image',
|
'mode' => $isSingleImage ? 'direct' : 'selection',
|
'create_new' => true,
|
'label' => $this->registrar->getUploadTitle(),
|
'content' => $this->content,
|
'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->registrar->getSingular() . '.</p>
|
<p>You can also select multiple images and click the "Add to Group" button.</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->registrar->getSingular() . '.';
|
}
|
|
$this->skeleton->addUploader($config);
|
}
|
|
/**
|
* Initialize taxonomies from WordPress config
|
*/
|
protected function initTaxonomies(): void {
|
$this->taxonomies = ($this->registrar->getType() === 'post') ? $this->registrar->registrar->taxonomies : [];
|
}
|
|
/**
|
* 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->registrar->getSingular() . '">'
|
. jvbDashIcon('plus-square')
|
. '<span class="screen-reader-text">Create New ' . $this->registrar->getSingular() . '</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;
|
}
|
}
|