From ac444cba221832c012c0435fdc8339fe9f37febb Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 11 May 2026 18:35:04 +0000
Subject: [PATCH] =Some changes to the CRUD.js editing, timeline post configuration
---
inc/rest/routes/ContentRoutes.php | 293 ++++++++++++++++++++++++++++++++++++++++-----------------
1 files changed, 204 insertions(+), 89 deletions(-)
diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index 119b3a1..dd25f8e 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -5,18 +5,17 @@
use JVBase\managers\queue\executors\ContentExecutor;
use JVBase\managers\queue\TypeConfig;
use JVBase\meta\Meta;
+use JVBase\registrar\Registrar;
use JVBase\rest\PermissionHandler;
use JVBase\rest\Response;
use JVBase\rest\Rest;
-use JVBase\managers\Cache;
use JVBase\rest\Route;
-use JVBase\utility\Features;
use WP_Post;
use WP_Query;
-use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
-use Exception;
+use WP_Term;
+use WP_Term_Query;
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
@@ -43,6 +42,7 @@
$this->cache->flush();
}
$this->cache->connect('post', true);
+ $this->cache->connect('term', true);
add_action('init', [$this, 'registerContentExecutors'], 5);
}
@@ -76,20 +76,39 @@
Route::for('content')
->get([$this, 'getContent'])
->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
+ ->args([
+ 'content' => 'string|required',
+ 'status' => 'string|default:all',
+ 'page' => 'integer|default:1|min:1',
+ 'per_page' => 'integer|default:30|min:1|max:100',
+ 'orderby' => 'string|enum:date,alphabetical|default:date',
+ 'order' => 'string|enum:asc,desc|default:desc',
+ 'search' => 'string',
+ 'date-filter' => 'string',
+ 'dateFrom' => 'string',
+ 'dateTo' => 'string',
+ ])
->rateLimit(20)
->post([$this, 'postContent'])
->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
- ->rateLimit(30);
+ ->rateLimit(30)
+ ->args([
+ 'user' => 'int|required',
+ 'posts' => 'required',
+ 'content' => 'string',
+ ])
+ ->register();
}
protected function initTimelineFields(string $content): void
{
$content = jvbNoBase($content);
- if (!Features::forContent($content)->has('is_timeline')) {
+
+ $config = Registrar::getInstance($content);
+ if (!$config || !$config->hasFeature('is_timeline')) {
return;
}
- $config = Features::getConfig($content);
- $this->fields = $config['fields'];
+ $this->fields = $config->getFields();
$this->timelineSharedFields = $this->getTimelineSharedFields($content);
array_unshift($this->timelineSharedFields, 'post_thumbnail');
@@ -102,11 +121,12 @@
public function getTimelineUniqueFields(string $content): array
{
$content = jvbNoBase($content);
- if (!Features::forContent($content)->has('is_timeline')) {
+ $registrar = Registrar::getInstance($content);
+ if (!$registrar || !$registrar->hasFeature('is_timeline')) {
return [];
}
- $config = Features::getConfig($content);
- $allFields = $config['fields'];
+
+ $allFields = $registrar->getFields();
return array_keys(array_filter($allFields, function ($field) {
if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
@@ -119,17 +139,15 @@
public function getTimelineSharedFields(string $content): array
{
$content = jvbNoBase($content);
- if (!Features::forContent($content)->has('is_timeline')) {
+ $registrar = Registrar::getInstance($content);
+ if (!$registrar || !$registrar->hasFeature('is_timeline')) {
return [];
}
- $config = Features::getConfig($content);
- if (!$config || empty($config)) {
- return [];
- }
- $allFields = $config['fields'] ?? [];
+
+ $allFields = $registrar->getFields()??[];
return array_keys(array_filter($allFields, function ($field) {
- if (!array_key_exists('for_all', $field) || $field['for_all'] === false) {
+ if (!array_key_exists('for_all', $field) || is_null($field['for_all']) || $field['for_all'] === false) {
return true;
}
return false;
@@ -151,22 +169,19 @@
return Response::success(['message'=>'No posts found in request']);
}
+
$count = count($data['posts']);
$operationId = $data['id'];
unset($data['user']);
unset($data['id']);
error_log('[CONTENT]:'.print_r($data, true));
-
$queue = JVB()->queue();
$queue->queueOperation(
'content_update',
$user_id,
$data,
[
- 'count' => $count,
- 'chunk_key' => 'posts',
- 'chunk_size' => 10,
'operation_id' => $operationId
]
);
@@ -184,6 +199,24 @@
public function getContent(WP_REST_Request $request): WP_REST_Response
{
$params = $request->get_params();
+ error_log('getContent::params '.print_r($params, true));
+
+ $registrar = Registrar::getInstance($params['content']);
+ switch ($registrar->getType()) {
+ case 'term':
+ return $this->getTerms($request, $params, $registrar);
+ case 'user':
+ //TODO maybe do something?
+ break;
+ case 'post':
+ return $this->getPosts($request, $params, $registrar);
+ }
+
+ return $this->error('Something went wrong, this does not appear to have a proper content type');
+ }
+
+ public function getPosts(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response
+ {
$user_id = $params['user'];
$post_status = $params['status'];
@@ -194,7 +227,6 @@
}
$post_type = str_replace('-', '_', jvbCheckBase($params['content']));
-
// Build query args
$args = [
'post_type' => $post_type,
@@ -205,13 +237,15 @@
'author' => $user_id,
'post_status' => $post_status
];
+
+
//Only top level posts for timeline types
- if (Features::forContent($post_type)->has('is_timeline')) {
+ if ($registrar?->hasFeature('is_timeline')) {
$args['post_parent'] = 0;
}
//Calendar filters
- if (Features::forContent($post_type)->has('is_calendar')) {
+ if ($registrar?->hasFeature('is_calendar')) {
$args = $this->applyCalendarFilters($args, $params);
}
$taxonomies = array_filter($params, function ($param) {
@@ -238,6 +272,70 @@
$args['s'] = sanitize_text_field($params['search']);
}
+ $key = $this->cache->generateKey($args);
+ $cached = $this->checkCache($key, $request);
+ if ($cached) {
+ return $cached;
+ }
+
+ $this->post_type = jvbCheckBase($params['content'] ?? $params['type']);
+
+ if (array_key_exists('s', $args)) {
+ $args = $this->applySearchFilters($args, $params);
+ }
+
+ // Run query
+ $query = new WP_Query($args);
+
+ $registrar = Registrar::getInstance($this->post_type);
+ $this->fields = $registrar->getFields()??[];
+ $this->taxonomies = $this->getTaxonomies($this->post_type);
+
+ $posts = array_map([$this, 'preparePost'], $query->posts);
+
+ $data = [
+ 'items' => $posts,
+ 'total' => $query->found_posts,
+ 'total_pages' => $query->max_num_pages,
+ 'has_more' => $args['paged']??1 < $query->max_num_pages,
+ ];
+
+
+ $this->cache->set($key, $data);
+
+ $response = Response::success($data);
+ return $this->addCacheHeaders($response);
+ }
+ public function getTerms(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response
+ {
+ // Build query args
+ $args = [
+ 'taxonomy' => jvbCheckBase($params['content']),
+ 'number' => $params['per_page'] ?? 30,
+ 'orderby' => 'name',
+ 'order' => 'DESC',
+ 'hide_empty' => false,
+ ];
+ $paged = $params['page']??1;
+ $args['page'] = $paged;
+ if ($paged > 1) {
+ $args['offset'] = ($paged-1) * $args['number'];
+ }
+
+ //TODO
+// if (array_key_exists('taxonomies', $params)) {
+// $args = $this->applyTaxonomyFilters($args, $params);
+// }
+// if (array_key_exists('date-filter', $params) || array_key_exists('dateFrom', $params)) {
+// $args = $this->applyDateFilters($args, $params);
+// }
+ if (array_key_exists('orderby', $params) || array_key_exists('order', $params)) {
+ $args = $this->applyOrderFilters($args, $params);
+ }
+
+ if (array_key_exists('search', $params)) {
+ $args['s'] = sanitize_text_field($params['search']);
+ }
$key = $this->cache->generateKey($args);
// Check HTTP cache headers with the specific content type
@@ -251,7 +349,6 @@
$response = Response::success($cache);
return $this->addCacheHeaders($response);
}
-
$this->post_type = jvbCheckBase($params['content'] ?? $params['type']);
// Only expand search to taxonomies if we're actually going to query
if (array_key_exists('s', $args)) {
@@ -259,20 +356,33 @@
}
// Run query
- $query = new WP_Query($args);
+ $query = new WP_Term_Query($args);
-
- $this->fields = jvbGetFields(str_replace('-', '_', $this->post_type));
- $this->taxonomies = $this->getTaxonomies($this->post_type);
- $posts = array_map([$this, 'prepareItem'], $query->posts);
-
+ $terms = $query->get_terms();
$data = [
- 'items' => $posts,
- 'total' => $query->found_posts,
- 'total_pages' => $query->max_num_pages,
- 'has_more' => $args['paged']??1 < $query->max_num_pages,
+ 'total' => 0,
+ 'total_pages' => 0,
+ 'has_more' => false
];
+ if (!is_wp_error($terms) && !empty($terms))
+ {
+ $total = get_terms([
+ 'taxonomy' => $args['taxonomy'],
+ 'hide_empty' => false,
+ 'fields' => 'count'
+ ]);
+ $data['total'] = $total;
+ $data['total_pages'] = max($total/$args['number'], 1);
+ $data['has_more'] = ($args['page'] * $args['number']) < $total;
+ } else {
+ $terms = [];
+ }
+
+ $this->fields = $registrar->getFields()??[];
+
+ $this->taxonomies = [];
+ $data['items'] =array_map([$this, 'prepareTerm'], $terms);
$this->cache->set($key, $data);
@@ -369,16 +479,19 @@
*/
protected function getTaxonomies(string $content): array
{
- $taxonomy_for = jvbGlobalTaxonomyFor();
- $out = [];
- foreach ($taxonomy_for as $tax => $postTypes) {
- if (in_array($content, $postTypes)) {
- $out[BASE . $tax] = [
- 'label' => JVB_CONTENT[$content]['plural'],
- 'icon' => $tax,
- ];
- }
+ $registrar = Registrar::getInstance($content);
+ if (!$registrar || $registrar->getType()!== 'post') {
+ return [];
}
+ $out = [];
+ foreach ($registrar->registrar->taxonomies as $tax) {
+ $taxReg = Registrar::getInstance($tax);
+ $out[jvbCheckBase($tax)] = [
+ 'label' => $taxReg->getPlural(),
+ 'icon' => $taxReg->getIcon()??jvbDefaultIcon()
+ ];
+ }
+
return $out;
}
@@ -403,13 +516,15 @@
*
* @return array
*/
- protected function prepareItem(WP_Post $post, bool $skip = false, bool $fields = true): array
+ protected function preparePost(WP_Post $post, bool $skip = false, bool $fields = true): array
{
- if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) {
+ $registrar = Registrar::getInstance($post->post_type);
+ if (!$skip && $registrar && $registrar->hasFeature('is_timeline')) {
$this->initTimelineFields($post->post_type);
return $this->formatTimeline($post);
}
$this->meta = Meta::forPost($post->ID);
+ $fields = ($fields) ? $this->meta->getAll() : [];
$data = [
'id' => $post->ID,
'title' => $post->post_title,
@@ -418,66 +533,66 @@
'modified' => $post->post_modified,
'thumbnail' => get_the_post_thumbnail_url($post->ID),
'alt' => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true),
- 'icon' => $this->post_type,
+ 'icon' => $registrar->getIcon(),
'taxonomies' => [],
- 'fields' => ($fields) ? $this->meta->getAll() : [],
+ 'fields' => $fields,
'images' => [],
];
- // Add taxonomy terms
- foreach ($this->taxonomies as $taxonomy => $options) {
- $tax = str_replace(BASE, '', $taxonomy);
- $terms = wp_get_object_terms(
- $post->ID,
- $taxonomy,
- ['fields' => 'id=>name']
- );
- $data['taxonomies'][$tax] = [
- 'terms' => (is_wp_error($terms)) ? [] : $terms,
- 'name' => $options['label'],
- 'icon' => $tax
- ];
- }
-
- $images = $this->extractImages();
-
-
+ $images = $this->extractImages($fields, $this->meta);
if (!empty($images)) {
$data['images'] = $images;
}
+
+ $taxonomies = $this->extractTerms($fields, $this->meta);
+ if (!empty($taxonomies)) {
+ $data['taxonomies'] = $taxonomies;
+ }
return $data;
}
- protected function extractImages(array $fields = []): array
+ /**
+ * @param WP_Term $post the post object
+ *
+ * @return array
+ */
+ protected function prepareTerm(WP_Term $post, bool $fields = true): array
{
- //Extract images
- $images = [];
- $get = [];
- $fields = (empty($fields)) ? $this->fields : $fields;
- foreach ($fields as $field => $config) {
- if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') {
- $get[] = $field;
- }
+ $registrar = Registrar::getInstance($post->taxonomy);
+
+ $this->meta = Meta::forTerm($post->term_id);
+ $fields = ($fields) ? $this->meta->getAll() : [];
+ $data = [
+ 'id' => $post->term_id,
+ 'title' => $post->name,
+ 'date' => $fields['created_date']??'',
+ 'modified' => $fields['modified_date']??'',
+ 'thumbnail' => '',
+ 'icon' => $registrar->getIcon(),
+ 'taxonomies' => [],
+ 'fields' => $fields,
+ 'images' => [],
+ ];
+
+ $images = $this->extractImages($fields, $this->meta);
+ if (!empty($images)) {
+ $data['images'] = $images;
}
- if (!empty($get)) {
- $allImages = $this->meta->getAll($get);
- foreach ($allImages as $k => $imgs) {
- $temp = explode(',', $imgs);
- foreach ($temp as $img) {
- if (is_numeric($img) && !array_key_exists($img, $images)) {
- $images[$img] = jvbImageData((int)$img);
- }
- }
- }
+ $taxonomies = $this->extractTerms($fields, $this->meta);
+ if (!empty($taxonomies)) {
+ $data['taxonomies'] = $taxonomies;
}
- return $images;
+
+ error_log('Term data: '.print_r($data, true));
+ return $data;
}
+
public function formatTimeline(WP_Post $post): array
{
- $item = $this->prepareItem($post, true, false);
+ $item = $this->preparePost($post, true, false);
//Step 1: Get the fields that apply to all posts
$mainMeta = Meta::forPost($post->ID);
$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
@@ -496,7 +611,7 @@
$images[$f['post_thumbnail']] = jvbImageData((int)$f['post_thumbnail']);
}
- $item['fields']['timeline'] = $subFields;
+ $item['fields']['timeline_gallery'] = $subFields;
$item['images'] = $item['images'] + $images;
$item['number'] = $mainMeta->get('number');
--
Gitblit v1.10.0