<?php
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
use JVBase\managers\Cache;
|
use JVBase\managers\RoleManager;
|
use JVBase\meta\Form;
|
use JVBase\registrar\Registrar;
|
|
/**
|
* For whatever reason, after much testing, it seems that
|
* good ol' WP resets post_parent if you call wp_update_post
|
* without explicitly setting the post_parent
|
* This is a wrapper that grabs old data and merges it with
|
* what we're trying to update - reducing repetition
|
* @param array $postArr as in wp_update_post
|
* @param array $allowOverride an array of keys that are allowed to be overridden
|
* @return int|WP_Error
|
*/
|
function jvb_update_post(array $postArr, array $allowOverride = []) {
|
if (empty($postArr['ID'])) {
|
return new WP_Error('missing_id', 'Post ID is required');
|
}
|
|
$old = get_post($postArr['ID'], ARRAY_A);
|
if (!$old) {
|
return new WP_Error('invalid_id', 'Post not found');
|
}
|
/**
|
* WARNING: You won't want to override fields like:
|
* guid
|
* filter
|
* ancestors
|
* post_category
|
* tags_input
|
* to_ping
|
* pinged
|
**/
|
$preserveFields = [
|
'post_parent', 'menu_order',
|
'post_status', 'post_password', 'comment_status', 'ping_status',
|
'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt',
|
'post_name', 'post_title', 'post_excerpt', 'post_content',
|
'post_author'
|
];
|
// Remove fields we explicitly want to override
|
$preserveFields = array_diff($preserveFields, $allowOverride);
|
|
// Keep only preserved fields from old post
|
$old = array_intersect_key($old, array_flip($preserveFields));
|
|
// Merge old → new (new wins)
|
$merged = array_merge($old, $postArr);
|
$merged['ID'] = (int)$postArr['ID'];
|
|
return wp_update_post($merged, true);
|
}
|
|
/**
|
* @deprecated use CRUDManager.php or CRUDSkeleton.php
|
* Outputs the blocks of a CRUD management in backend
|
* Mainly used in news.php so far
|
* @param string $content
|
* @param array|null $statusFilters
|
* @param array|null $bulkEdit
|
*
|
* @return string
|
*/
|
function jvbCrudManagement(
|
string $content,
|
array|null $statusFilters = null,
|
array|null $bulkEdit = null
|
):string {
|
$statusFilters = ($statusFilters) ?: [
|
[
|
'id' => 'all',
|
'icon' => 'all',
|
'label' => 'Everything',
|
],
|
[
|
'id' => 'publish',
|
'icon' => 'show',
|
'label' => 'Live',
|
],
|
[
|
'id' => 'draft',
|
'icon' => 'hide',
|
'label' => 'Hidden',
|
],
|
[
|
'id' => 'trash',
|
'icon' => 'delete',
|
'label' => 'Scrapped',
|
]
|
];
|
$permission = JVB()->roles()->getContentPlural($content);
|
$canPublish = current_user_can("publish_{$permission}") && current_user_can('skip_moderation');
|
$out = '<div class="filters">';
|
if (!empty($statusFilters)) {
|
$out .= '<div class="status">';
|
foreach ($statusFilters as $filter) {
|
$disabled = ($filter['id'] === 'publish' && !$canPublish) ? ' disabled' : '';
|
$status = esc_attr($filter['id']);
|
$title = esc_html($filter['label']);
|
$out .= sprintf(
|
'<input type="radio"
|
name="status"
|
value="%s"
|
id="set-%s"%s>
|
<label for="set-%s">
|
%s
|
<span>%s</span>
|
</label>',
|
$status,
|
$status,
|
$disabled,
|
$status,
|
jvbIcon($filter['icon']),
|
$title
|
);
|
}
|
if (!$canPublish) {
|
$out .= '<p class="description">Your account needs to be verified before you can publish '.$content.'.</p>';
|
}
|
$out .= '</div>';
|
}
|
|
$bulkActions = jvbGetBulkActions($content);
|
|
if (!empty($bulkActions)) {
|
$out .= '<div class="bulk-controls">
|
<div class="bulk-select">
|
<input type="checkbox" id="select-all" class="select-all">
|
<label for="select-all">Select All<span class="selected-count"></span></label>
|
</div>
|
<div class="bulk-actions" hidden>
|
<select class="bulk-action-select">
|
<option value="">Bulk Actions...</option>';
|
foreach ($bulkActions as $status => $control) {
|
$disabled = ($control['disabled']) ? ' disabled' : '';
|
$out .= sprintf(
|
'<option value="%s"%s>%s</option>',
|
esc_attr($status),
|
$disabled,
|
esc_html($control['label'])
|
);
|
}
|
$out .= sprintf(
|
'</select>
|
<button type="button" class="apply-bulk">Apply</button>
|
<button type="button" class="cancel-bulk">
|
%s
|
Clear
|
</button>
|
</div>
|
</div>',
|
jvbIcon('x', ['title'=>'Cancel'])
|
);
|
}
|
$out .= '</div>';
|
|
return $out;
|
}
|
/**
|
* Outputs available actions
|
* mainly used by news.php
|
* @param string $content
|
*
|
* @return array|array[]
|
*/
|
function jvbGetBulkActions(string $content):array
|
{
|
$bulkActions = [
|
'publish' => [
|
'icon' => 'show',
|
'label' => 'Show',
|
'disabled' => true,
|
],
|
'edit' => [
|
'icon' => 'edit',
|
'label' => 'Edit',
|
'disabled' => true,
|
],
|
'draft' => [
|
'icon' => 'hide',
|
'label' => 'Hide',
|
'disabled' => true,
|
],
|
'trash' => [
|
'icon' => 'delete',
|
'label' => 'Scrap',
|
'disabled' => true,
|
]
|
];
|
$permission = JVB()->roles()->getContentPlural($content);
|
if (current_user_can('skip_moderation') && current_user_can("publish_{$permission}")) {
|
$bulkActions['publish']['disabled'] = false;
|
}
|
if (current_user_can("edit_{$permission}")) {
|
$bulkActions['edit']['disabled'] = false;
|
$bulkActions['draft']['disabled'] = false;
|
}
|
if (current_user_can("delete_{$permission}")) {
|
$bulkActions['trash']['disabled'] = false;
|
}
|
|
return $bulkActions;
|
}
|
|
/**
|
* Outputs the date filters for a content type
|
* @param string $content
|
*
|
* @return string
|
*/
|
function jvbRenderDateFilter(string $content):string
|
{
|
$cache = Cache::for('date_filter')->connect('post', true);
|
$check = $cache->get($content);
|
if ($check) {
|
return $check;
|
}
|
|
$postType = (str_starts_with($content, BASE)) ? $content : BASE. $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}'
|
ORDER BY post_date DESC
|
");
|
|
// Quick filters
|
$out = '<div>
|
<label for="filter-date">'.
|
jvbIcon('calendar', ['title'=>'Date']).
|
'<span class="screen-reader-text">Filter by Date</span>
|
</label>
|
<select id="filter-date" class="date-filter" name="date-filter">
|
<option value="">[ 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>';
|
|
// Custom date range
|
$out .= '<dialog class="date-range" >
|
<div class="wrap">
|
<div class="custom-range row">
|
<label class="col">
|
<span>From</span>
|
<input type="date" class="date-start">
|
</label>
|
<label class="col">
|
<span>To</span>
|
<input type="date" class="date-end">
|
</label>
|
</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);
|
$out .= sprintf(
|
'<option value="%s">%s</option>',
|
esc_attr($value),
|
esc_html($month_name)
|
);
|
}
|
|
$out .= '</select>
|
</label>
|
</div>
|
</div>
|
</dialog>';
|
|
$cache->set($content, $out);
|
|
return $out;
|
}
|
|
|
/**
|
* Renders sections based on what was set in the Content Registry
|
* @param object $handler
|
* @param int $ID
|
* @param string $contentType
|
* @param string $postType
|
* @param bool $prefix
|
*
|
* @return void
|
*/
|
function jvbRenderSections(
|
int $ID,
|
string $contentType = 'post',
|
string $postType = '',
|
bool $prefix = false
|
):void {
|
$registrar = Registrar::getInstance($postType);
|
if (!$registrar || empty($registrar->getSections())) {
|
return;
|
}
|
|
echo '<div class="container">';
|
$nav = '<nav class="tabs row left" role="tablist">';
|
$i = 1;
|
foreach ($registrar->getSections() as $slug => $section) {
|
$nav .= '<button type="button" class="tab';
|
|
$ariaActive = 'false';
|
if ($i === 1) {
|
$nav .= ' active';
|
$ariaActive = 'true';
|
}
|
$tabName = ($prefix) ? $ID.'-'.$slug : $slug;
|
$nav .= '" data-tab="'.$tabName.'" role="tab" aria-selected="'.$ariaActive.'">
|
<h2>'.jvbIcon($section['icon']).$section['label'].'</h2></button>';
|
$i++;
|
}
|
$nav .= '</nav>';
|
echo $nav;
|
|
$fields = Registrar::getFieldsFor($postType);
|
?>
|
<form class="jvb-form" id="bio" data-form-id="bio-<?=$ID?>" data-save="bio"
|
data-object-id="<?=$ID?>" data-content-type="<?=$postType?>">
|
<?php
|
$i = 0;
|
foreach ($registrar->getSections() as $slug => $section) {
|
$tabName = ($prefix) ? $ID.'-'.$slug : $slug;
|
|
$class = ($i == 0) ? ' active' : '';
|
?>
|
<section id="<?= $slug ?>" class="tab-content<?=$class?>" data-tab="<?=$tabName?>" role="tabpanel">
|
<?php if (!empty($section['title']) && !$prefix) : ?>
|
<h2><?= esc_html($section['title']); ?></h2>
|
<?php endif; ?>
|
|
<?php if (!empty($section['description'])) : ?>
|
<p class="section-description">
|
<?= wp_kses_post($section['description']); ?>
|
</p>
|
<?php endif; ?>
|
|
|
<?php
|
$sectionFields = array_filter($fields, function ($f) use ($slug) {
|
return array_key_exists('section', $f) && $f['section'] == $slug;
|
});
|
foreach ($sectionFields as $field => $config) : ?>
|
<?php
|
if ($config['type'] == 'callback') {
|
$callback = $config['callback'];
|
echo $callback($ID);
|
} else {
|
if (array_key_exists('role', $config)) {
|
$user = get_userdata($ID);
|
if (in_array($config['role'], $user->roles)) {
|
echo Form::render($field, null, $config);
|
}
|
} else {
|
echo Form::render($field, null, $config);
|
}
|
}
|
?>
|
<?php endforeach; ?>
|
</section>
|
<?php
|
$i++;
|
}
|
?>
|
</form>
|
</div>
|
<?php
|
}
|