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 | 1176 ++++++++++++++++++++++++++-------------------------------
1 files changed, 537 insertions(+), 639 deletions(-)
diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index 6916093..dd25f8e 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -1,722 +1,620 @@
<?php
+
namespace JVBase\rest\routes;
-use JVBase\JVB;
-use JVBase\rest\RestRouteManager;
-use JVBase\managers\CacheManager;
-use JVBase\meta\MetaManager;
+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\rest\Route;
+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
+ exit; // Exit if accessed directly
}
-class ContentRoutes extends RestRouteManager
+class ContentRoutes extends Rest
{
- protected array $fields = [];
- protected array $taxonomies = [];
- protected MetaManager $meta;
- protected string $post_type = '';
- protected string $user_id = '';
+ protected array $fields = [];
+ protected array $taxonomies = [];
+ protected string $post_type = '';
+ protected string $user_id = '';
- //TODO: Ensure we are handling the bulk operations for all processes
- //TODO: be sure to clear cache ($this->>cache->invalidateGroup($this->>cache_name)) on content update/create
- //TODO: Also invalidate feed caches on updates!!
+ //For Timeline-specific posts
+ protected array $timelineSharedFields = [];
+ protected array $timelineUniqueFields = [];
+ protected static ?string $action = 'dash-';
+ protected Meta $meta;
- public function __construct()
- {
- $this->cache_name = 'user_content_'.get_current_user_id();
- parent::__construct();
- $this->action = 'dash-';
- $this->operation_type = 'content_update';
- add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
- }
+ public function __construct()
+ {
+ $this->cacheName = 'user_content_' . get_current_user_id();
+ parent::__construct();
+ if (JVB_TESTING) {
+ $this->cache->flush();
+ }
+ $this->cache->connect('post', true);
+ $this->cache->connect('term', true);
+ add_action('init', [$this, 'registerContentExecutors'], 5);
+ }
- /**
- * Registers content routes
- * @return void
- */
- public function registerRoutes():void
- {
- // Base content endpoint
- register_rest_route($this->namespace, "/content", [
- [
- 'methods' => 'GET',
- 'callback' => [$this, 'handleContentRequest'],
- 'permission_callback' => [$this, 'checkPermission'],
- ],
- [
- 'methods' => 'POST',
- 'callback' => [$this, 'handleContentUpdate'],
- 'permission_callback' => [$this, 'checkPermission']
- ]
- ]);
+ /**
+ * Register content operation types with the queue's TypeRegistry
+ */
+ public function registerContentExecutors(): void
+ {
+ $registry = JVB()->queue()->registry();
+ $executor = new ContentExecutor();
- //TODO: consolidate create/batch in with create? I don't think we are ever creating a single item
- register_rest_route($this->namespace, "/create", [
- [
- 'methods' => 'POST',
- 'callback' => [$this, 'handleContentCreate'],
- 'permission_callback' => [$this, 'checkPermission']
- ]
- ]);
- register_rest_route($this->namespace, "/create/batch", [
- [
- 'methods' => 'POST',
- 'callback' => [$this, 'handleBatchCreation'],
- 'permission_callback' => [$this, 'checkPermission']
- ]
- ]);
- }
+ // Content updates - chunked at 10 posts
+ $registry->register('content_update', new TypeConfig(
+ executor: $executor,
+ chunkKey: 'posts',
+ chunkSize: 10
+ ));
- /**
- * Handle content update/creation
- * @param WP_REST_Request $request
- *
- * @return WP_REST_Response
- */
- public function handleContentUpdate(WP_REST_Request$request):WP_REST_Response
- {
- $data = $request->get_params();
- error_log('Received data: '.print_r($data, true));
- $user_id = $data['user'];
+ // Batch creation (from uploads) TODO: I believe this is all handled by UploadExecutor
+// $registry->register('batch_creation', new TypeConfig(
+// executor: $executor
+// ));
+ }
+
+ /**
+ * Registers content routes
+ * @return void
+ */
+ public function registerRoutes(): void
+ {
+ 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)
+ ->args([
+ 'user' => 'int|required',
+ 'posts' => 'required',
+ 'content' => 'string',
+ ])
+ ->register();
+ }
+
+ protected function initTimelineFields(string $content): void
+ {
+ $content = jvbNoBase($content);
+
+ $config = Registrar::getInstance($content);
+ if (!$config || !$config->hasFeature('is_timeline')) {
+ return;
+ }
+ $this->fields = $config->getFields();
+
+ $this->timelineSharedFields = $this->getTimelineSharedFields($content);
+ array_unshift($this->timelineSharedFields, 'post_thumbnail');
+ array_unshift($this->timelineSharedFields, 'post_title');
+ array_unshift($this->timelineSharedFields, 'post_status');
+
+ $this->timelineUniqueFields = $this->getTimelineUniqueFields($content);
+ }
+
+ public function getTimelineUniqueFields(string $content): array
+ {
+ $content = jvbNoBase($content);
+ $registrar = Registrar::getInstance($content);
+ if (!$registrar || !$registrar->hasFeature('is_timeline')) {
+ return [];
+ }
+
+ $allFields = $registrar->getFields();
+
+ return array_keys(array_filter($allFields, function ($field) {
+ if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
+ return true;
+ }
+ return false;
+ }));
+ }
+
+ public function getTimelineSharedFields(string $content): array
+ {
+ $content = jvbNoBase($content);
+ $registrar = Registrar::getInstance($content);
+ if (!$registrar || !$registrar->hasFeature('is_timeline')) {
+ return [];
+ }
+
+ $allFields = $registrar->getFields()??[];
+
+ return array_keys(array_filter($allFields, function ($field) {
+ if (!array_key_exists('for_all', $field) || is_null($field['for_all']) || $field['for_all'] === false) {
+ return true;
+ }
+ return false;
+ }));
+ }
+
+ /**
+ * Handle content update/creation
+ * @param WP_REST_Request $request
+ *
+ * @return WP_REST_Response
+ */
+ public function postContent(WP_REST_Request $request): WP_REST_Response
+ {
+ $data = $request->get_params();
+ $user_id = $data['user'];
+
+ if (!array_key_exists('posts', $data) || !is_array($data['posts'])) {
+ return Response::success(['message'=>'No posts found in request']);
+ }
- if (!isset($data['posts']) || !is_array($data['posts'])) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' =>'Invalid request format'
- ]);
- }
+ $count = count($data['posts']);
+ $operationId = $data['id'];
+ unset($data['user']);
+ unset($data['id']);
- $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,
+ [
+ 'operation_id' => $operationId
+ ]
+ );
- $queue = JVB()->queue();
- $queue->queueOperation(
- 'content_update',
- $user_id,
- $data,
- [
- 'count' => $count,
- 'chunk_key' => 'posts',
- 'chunk_size' => 10,
- 'operation_id' => $operationId
- ]
- );
- return new WP_REST_Response([
- 'success' => true,
- 'message' => 'Queued for processing',
- 'operation' => $operationId
- ]);
- }
-
- /**
- * Handle content creation
- * @param WP_REST_Request $request
- *
- * @return WP_REST_Response
- */
- public function handleContentCreate(WP_REST_Request$request):WP_REST_Response
- {
- $data = $request->get_json_params();
- $user_id = $data['user'];
-
- if (!isset($data['posts']) || !is_array($data['posts'])) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => 'Invalid request format'
- ]);
- }
-
- $count = count($data['posts']);
- $operationId = $data['id'];
- unset($data['user']);
- unset($data['id']);
- JVB()->queue()->queueOperation(
- 'batch_creation',
- $user_id,
- $data,
- [
- 'count' => $count,
- 'operation_id' => $operationId,
- ]
- );
-
- return new WP_REST_Response([
- 'success' => true,
- 'message' => 'Queued for processing',
- 'operation' => $operationId
- ]);
- }
+ return Response::queued($operationId);
+ }
- /**
- * Handle request
- * @param WP_REST_Request $request
- *
- * @return WP_REST_Response
- */
- public function handleContentRequest(WP_REST_Request $request):WP_REST_Response
- {
- $params = $request->get_params();
- error_log('handleContentRequest params: '.print_r($params, true));
+ /**
+ * Handle request
+ * @param WP_REST_Request $request
+ *
+ * @return WP_REST_Response
+ */
+ public function getContent(WP_REST_Request $request): WP_REST_Response
+ {
+ $params = $request->get_params();
+ error_log('getContent::params '.print_r($params, true));
- error_log('Fetching content. Params: '.print_r($params, true));
- $user_id = $params['user'];
- if (!$this->userCheck($user_id)) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => 'User does not match up. Are you a bot?',
- ]);
- }
+ $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);
+ }
- $post_status = $params['status'];
- if ($post_status === 'all') {
- $post_status = ['publish', 'draft'];
- } else {
- $post_status = explode(',', $post_status);
- }
- $post_type = str_replace('-', '_',jvbCheckBase($params['content']));
+ return $this->error('Something went wrong, this does not appear to have a proper content type');
+ }
- $config = (array_key_exists($params['content'], JVB_CONTENT) && !empty(JVB_CONTENT[$params['content']])) ? JVB_CONTENT[$params['content']] : [];
+ public function getPosts(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response
+ {
+ $user_id = $params['user'];
+
+ $post_status = $params['status'];
+ if ($post_status === 'all') {
+ $post_status = ['publish', 'draft'];
+ } else {
+ $post_status = explode(',', $post_status);
+ }
+ $post_type = str_replace('-', '_', jvbCheckBase($params['content']));
+
+ // Build query args
+ $args = [
+ 'post_type' => $post_type,
+ 'posts_per_page' => $params['per_page'] ?? 30,
+ 'paged' => $params['page'],
+ 'orderby' => 'date',
+ 'order' => 'DESC',
+ 'author' => $user_id,
+ 'post_status' => $post_status
+ ];
-
- // Build query args
- $args = [
- 'post_type' => $post_type,
- 'posts_per_page' => $params['per_page']??30,
- 'paged' => $params['page'],
- 'orderby' => 'date',
- 'order' => 'DESC',
- 'author' => $user_id,
- 'post_status' => $post_status
- ];
+ //Only top level posts for timeline types
+ if ($registrar?->hasFeature('is_timeline')) {
+ $args['post_parent'] = 0;
+ }
//Calendar filters
- if (jvbCheck('is_calendar', $config)) {
+ if ($registrar?->hasFeature('is_calendar')) {
$args = $this->applyCalendarFilters($args, $params);
}
+ $taxonomies = array_filter($params, function ($param) {
+ return str_starts_with($param, 'tax_');
+ }, ARRAY_FILTER_USE_KEY);
+ if (!empty($taxonomies)) {
+ $params['taxonomies'] = [];
+ foreach ($taxonomies as $taxonomy => $terms) {
+ $taxonomy = str_replace('tax_', '', $taxonomy);
+ $params['taxonomies'][$taxonomy] = $terms;
+ }
+ }
if (array_key_exists('taxonomies', $params)) {
$args = $this->applyTaxonomyFilters($args, $params);
}
- if (array_key_exists('date', $params) && !empty($params['date'])) {
+ 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 (!empty($params['search'])) {
+ if (array_key_exists('search', $params)) {
$args['s'] = sanitize_text_field($params['search']);
}
- error_log('Content Routes final args: '.print_r($args, true));
-
- $key = $this->cache->generateKey($args);
- $lastModified = $this->cache->getTimestamp($key);
- if ($lastModified !== false) {
- $headerCheck = $this->ifModifiedSince($lastModified, $args, $request);
- if (!is_null($headerCheck)) {
- return $headerCheck;
- }
- } else {
- // No timestamp yet, but we can still set ETag
- $etag = '"' . md5(serialize($args)) . '"';
- header('ETag: ' . $etag);
- header('Cache-Control: private, max-age=30');
+ $key = $this->cache->generateKey($args);
+ $cached = $this->checkCache($key, $request);
+ if ($cached) {
+ return $cached;
}
+ $this->post_type = jvbCheckBase($params['content'] ?? $params['type']);
- $cache = $this->cache->get($key);
- $cache = false;
- if ($cache) {
- return new WP_REST_Response($cache);
- }
+ if (array_key_exists('s', $args)) {
+ $args = $this->applySearchFilters($args, $params);
+ }
- // Run query
- $query = new WP_Query($args);
+ // Run query
+ $query = new WP_Query($args);
- $this->post_type = $params['content']??$params['type'];
+ $registrar = Registrar::getInstance($this->post_type);
+ $this->fields = $registrar->getFields()??[];
+ $this->taxonomies = $this->getTaxonomies($this->post_type);
- $this->fields = jvbGetFields(str_replace('-','_',$this->post_type));
- $this->taxonomies = $this->getTaxonomies($this->post_type);
- $posts = array_map([$this, 'prepareItem'], $query->posts);
+ $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,
+ ];
- $data = [
- 'items' => $posts,
- 'total' => $query->found_posts,
- 'total_pages' => $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'];
+ }
- $this->cache->set($key, $data);
+ //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);
+ }
- return new WP_REST_Response($data);
- }
+ if (array_key_exists('search', $params)) {
+ $args['s'] = sanitize_text_field($params['search']);
+ }
- /**
- * Gets allowed taxonomies for a particular content
- * @param string $content
- *
- * @return array
- */
- 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,
- ];
- }
- }
- return $out;
- }
+ $key = $this->cache->generateKey($args);
+ // Check HTTP cache headers with the specific content type
+ $cache_check = $this->checkHeaders($request, $key);
+ if ($cache_check) {
+ return $cache_check;
+ }
- /**
- * Processes operation from queue
- * @param object $operation
- * @param array $data
- *
- * @return array
- */
- protected function processBatches(object $operation, array $data):array
- {
- $this->user_id = $operation->user_id;
- $posts = $data['posts'];
+ $cache = $this->cache->get($key);
+ if ($cache) {
+ $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)) {
+ $args = $this->applySearchFilters($args, $params);
+ }
- if (empty($posts)) {
- return [
- 'success' => false,
- 'message' => 'No posts to update'
- ];
- }
+ // Run query
+ $query = new WP_Term_Query($args);
- $results = [];
+ $terms = $query->get_terms();
+ $data = [
+ 'total' => 0,
+ 'total_pages' => 0,
+ 'has_more' => false
+ ];
- foreach ($posts as $ID => $post_data) {
- if (str_starts_with($ID, 'new')) {
+ 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 = [];
+ }
- error_log('New post detected. Creating... with: '.print_r([
- 'post_author' => $this->user_id,
- 'post_type' => jvbCheckBase($post_data['content']),
- 'post_title' => $post_data['post_title']??'',
- 'post_status' => $post_data['status']??'draft',
- ], true));
- error_log('Recieved Data: '.print_r($post_data, true));
- $ID = wp_insert_post([
- 'post_author' => $this->user_id,
- 'post_type' => jvbCheckBase($post_data['content']),
- 'post_title' => $post_data['post_title']??'',
- 'post_status' => $post_data['status']??'draft',
+ $this->fields = $registrar->getFields()??[];
+
+ $this->taxonomies = [];
+ $data['items'] =array_map([$this, 'prepareTerm'], $terms);
+
+ $this->cache->set($key, $data);
+
+ $response = Response::success($data);
+ return $this->addCacheHeaders($response);
+ }
+
+ protected function applySearchFilters(array $args, array $params): array
+ {
+ $search_term = sanitize_text_field($params['search']);
+
+ // Search term is already in $args['s'] from earlier
+
+ // Get all taxonomies registered to this post type
+ $taxonomies = get_object_taxonomies($this->post_type, 'names');
+
+ if (empty($taxonomies)) {
+ return $args;
+ }
+
+ // Cache the taxonomy term lookup per search term + post type
+ $term_cache_key = 'search_terms_' . md5($search_term . $this->post_type);
+ $matching_term_ids = $this->cache->get($term_cache_key);
+
+ if ($matching_term_ids === false) {
+ $matching_term_ids = [];
+
+ foreach ($taxonomies as $taxonomy) {
+ $terms = get_terms([
+ 'taxonomy' => $taxonomy,
+ 'search' => $search_term,
+ 'hide_empty' => false,
+ 'fields' => 'ids'
]);
- if (!$ID || is_wp_error($ID)) {
- $results[$ID] = [
- 'success' => false,
- 'message' => 'Couldn\'t Create Post'
- ];
- continue;
- }
- $fields = jvbGetFields($post_data['content']);
- $allowedFields = array_filter($post_data, function($key) use ($fields) {
- return array_key_exists($key, $fields);
- }, ARRAY_FILTER_USE_KEY);
- $meta = new MetaManager($ID, 'post');
- $success = $meta->setAll($allowedFields);
- $results[$ID] = [
- 'success' => $success
+ if (!is_wp_error($terms) && !empty($terms)) {
+ $matching_term_ids = array_merge($matching_term_ids, $terms);
+ }
+ }
+
+ // Cache term IDs for 1 hour
+ $this->cache->set($term_cache_key, $matching_term_ids, 3600);
+ }
+
+ if (empty($matching_term_ids)) {
+ return $args;
+ }
+
+ // Build tax_query for matching terms
+ $term_queries = [];
+
+ foreach ($taxonomies as $taxonomy) {
+ $taxonomy_term_ids = array_filter($matching_term_ids, function ($term_id) use ($taxonomy) {
+ $term = get_term($term_id);
+ return !is_wp_error($term) && $term->taxonomy === $taxonomy;
+ });
+
+ if (!empty($taxonomy_term_ids)) {
+ $term_queries[] = [
+ 'taxonomy' => $taxonomy,
+ 'field' => 'term_id',
+ 'terms' => array_values($taxonomy_term_ids),
+ 'operator' => 'IN'
+ ];
+ }
+ }
+
+ if (!empty($term_queries)) {
+ if (isset($args['tax_query'])) {
+ $args['tax_query'] = [
+ 'relation' => 'OR',
+ $args['tax_query'],
+ [
+ 'relation' => 'OR',
+ ...$term_queries
+ ]
];
} else {
- if (!$this->verifyOwnership($ID)) {
- $results[$ID] = [
- 'success' => false,
- 'message' => 'No permission to modify this post'
- ];
- continue;
- }
- error_log('Saving post data: '.print_r($post_data, true));
-
- if (array_key_exists('post_status', $post_data)) {
- switch ($post_data['post_status']) {
- case 'publish':
- unset($post_data['post_status']);
- if (user_can($this->user_id, 'manage_options') || user_can($this->user_id, 'skip_moderation')) {
- $result = wp_update_post(['ID' => $ID, 'post_status' => 'publish']);
- }
- break;
- case 'draft':
- $result = wp_update_post([
- 'ID' => $ID,
- 'post_status' => 'draft'
- ]);
- break;
- case 'trash':
- $result = wp_trash_post($ID);
- break;
- case 'delete':
- $result = wp_delete_post($ID, true);
- return ['success' => (bool)$result];
- }
- }
- error_log('Updating data: '.print_r($post_data, true));
- $fields = jvbGetFields($post_data['content']);
- $allowedFields = array_filter($post_data, function($key) use ($fields) {
- return array_key_exists($key, $fields);
- }, ARRAY_FILTER_USE_KEY);
-
- error_log('Allowed Fields: '.print_r($allowedFields, true));
- $meta = new MetaManager($ID, 'post');
- $success = $meta->setAll($allowedFields);
- error_log('Should be set?');
- $results[$ID] = [
- 'success' => $success
+ $args['tax_query'] = [
+ 'relation' => 'OR',
+ ...$term_queries
];
-
}
+ }
- CacheManager::invalidateGroup($post_data['content']);
- if (jvbSiteUsesFeedBlock()) {
- CacheManager::invalidateGroup($post_data['feed']);
- }
- }
+ return $args;
+ }
+
+ /**
+ * Gets allowed taxonomies for a particular content
+ * @param string $content
+ *
+ * @return array
+ */
+ protected function getTaxonomies(string $content): array
+ {
+ $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;
+ }
- CacheManager::invalidateGroup('user_content');
- if (jvbSiteHasNotifications()) {
- $this->notifications = JVB()->notification();
- $this->notifications->addNotification(
- $this->user_id,
- 'content_update_complete',
- null,
- 'Content updates completed!'
- );
+
+ /**
+ * Generates a post title, based on content type
+ * @param string $content the post type
+ *
+ * @return string
+ */
+ protected function generatePostTitle(string $content): string
+ {
+ $username = get_user_meta($this->user_id, 'first_name', true);
+ $link = get_user_meta($this->user_id, BASE . 'link', true);
+ $city = jvbArtistCity($link);
+ return ucfirst($content) . ' by ' . $city . ' artist ' . $username;
+ }
+
+ /**
+ * @param WP_Post $post the post object
+ *
+ * @return array
+ */
+ protected function preparePost(WP_Post $post, bool $skip = false, bool $fields = true): array
+ {
+ $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,
+ 'status' => $post->post_status,
+ 'date' => $post->post_date,
+ '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' => $registrar->getIcon(),
+ 'taxonomies' => [],
+ 'fields' => $fields,
+ 'images' => [],
+ ];
+
+ $images = $this->extractImages($fields, $this->meta);
+ if (!empty($images)) {
+ $data['images'] = $images;
}
- return [
- 'success' => true,
- 'result' => $results
- ];
- }
+ $taxonomies = $this->extractTerms($fields, $this->meta);
+ if (!empty($taxonomies)) {
+ $data['taxonomies'] = $taxonomies;
+ }
+ return $data;
+ }
- /**
- * Handle batch content creation from uploads
- * @param WP_REST_Request $request
- *
- * @return WP_REST_Response
- */
- public function handleBatchCreation(WP_REST_Request $request):WP_REST_Response
- {
- //Operation has two parts
- //First, queue image processing
- //Then queue post creation from the stored IDs, depending on mode
- //if direct, each image becomes a new post
- //if selection, each group becomes its own post,
- // and ungrouped items each become their own post
- if (!isset($_FILES['files'])) {
- return new WP_REST_Response([
- 'success' => false,
- 'message' => 'No files uploaded...',
- ]);
- }
+ /**
+ * @param WP_Term $post the post object
+ *
+ * @return array
+ */
+ protected function prepareTerm(WP_Term $post, bool $fields = true): array
+ {
+ $registrar = Registrar::getInstance($post->taxonomy);
- $data = $request->get_params();
+ $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;
+ }
+
+ $taxonomies = $this->extractTerms($fields, $this->meta);
+ if (!empty($taxonomies)) {
+ $data['taxonomies'] = $taxonomies;
+ }
+
+ error_log('Term data: '.print_r($data, true));
+ return $data;
+ }
- $user_id = $data['user'];
- if (!$this->userCheck($user_id)) {
- return new WP_REST_Response([
- 'success' => 'false',
- 'message' => 'Invalid user match... are you a bot?'
- ]);
- }
- $operation_id = $data['id'];
- $response = new WP_REST_Response([
- 'success' => true,
- 'message' => 'Successfully sent to server. Added to queue.',
- 'operation_id' => $operation_id,
- 'status' => 'pending'
- ]);
- $this->queue = JVB()->queue();
- JVB()->routes('uploads')->handleUploadRequest($request, false);
- $this->queue->queueOperation(
- 'batch_creation',
- $user_id,
- [
- 'content' => $request->get_param('content'),
- 'mode' => $request->get_param('mode') ?: 'direct',
- 'files_data'=> $request->get_param('files_data')
- ],
- [
- 'operation_id' => $operation_id,
- 'priority' => 'high',
- 'notification' => true,
- 'depends_on' => $operation_id.'_upload'
- ]
- );
+ public function formatTimeline(WP_Post $post): array
+ {
+ $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);
- return $response;
- }
+ //Step 2: Get the fields for each individual posts
+ $children = get_children(['post_parent' => $post->ID, 'orderby' => 'date', 'order' => 'ASC', 'post_status' => ['publish', 'draft'], 'fields' => 'ids']);
+ array_unshift($children, $post->ID);
- /**
- * Generates a post title, based on content type
- * @param string $content the post type
- *
- * @return string
- */
- protected function generatePostTitle(string $content):string
- {
- $username = get_user_meta($this->user_id, 'first_name', true);
- $link = get_user_meta($this->user_id, BASE.'link', true);
- $city = jvbArtistCity($link);
- return ucfirst($content).' by '.$city.' artist '.$username;
- }
-
- /**
- * @param object $post the wordpress post object
- *
- * @return array
- */
- protected function prepareItem(object $post):array
- {
- $this->meta = new MetaManager($post->ID, 'post');
- $data = [
- 'id' => $post->ID,
- 'status' => $post->post_status,
- 'date' => $post->post_date,
- '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,
- 'taxonomies'=> [],
- 'fields' => $this->meta->getAll(),
- '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
- ];
- }
-
-
- //Extract images
+ $subFields = [];
$images = [];
- $get = [];
- foreach ($this->fields as $field => $config) {
- if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') {
- $get[] = $field;
- }
- }
+ foreach ($children as $child) {
+ $meta = Meta::forPost($child);
+ $f = $meta->getAll($this->timelineUniqueFields);
+ $f = ['id' => $child] + $f;
+ $subFields[] = $f;
- 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);
- }
- }
- }
+ $images[$f['post_thumbnail']] = jvbImageData((int)$f['post_thumbnail']);
}
+ $item['fields']['timeline_gallery'] = $subFields;
+ $item['images'] = $item['images'] + $images;
+ $item['number'] = $mainMeta->get('number');
- if (!empty($images)) {
- $data['images'] = $images;
- }
-
- return $data;
- }
-
- /**
- * Builds the taxonomy query
- * @param array $taxonomies
- *
- * @return array|string[]
- */
- protected function buildTaxQuery(array $taxonomies):array
- {
- $tax_query = [];
- error_log('Taxonomies in query: '.print_r($taxonomies, true));
-
- foreach ($taxonomies as $taxonomy => $terms) {
- if (!empty($terms)) {
- $tax_query[] = [
- 'taxonomy' => jvbCheckBase($taxonomy),
- 'field' => 'term_id',
- 'terms' => array_map('absint', (array)$terms)
- ];
- }
- }
-
-
- return count($tax_query) > 1
- ? array_merge(['relation' => 'AND'], $tax_query)
- : $tax_query;
- }
-
- /**
- * Builds the date query
- * @param array $date_params
- *
- * @return array
- */
- protected function buildDateQuery(array $date_params):array
- {
- $query = [];
-
- if (!empty($date_params['after'])) {
- $query['after'] = sanitize_text_field($date_params['after']);
- }
-
- if (!empty($date_params['before'])) {
- $query['before'] = sanitize_text_field($date_params['before']);
- }
-
- if (isset($date_params['inclusive'])) {
- $query['inclusive'] = (bool)$date_params['inclusive'];
- }
-
- return empty($query) ? [] : [$query];
- }
-
- /**
- * @param int $post_id
- *
- * @return bool
- */
- protected function verifyOwnership(int $post_id):bool
- {
- $post = get_post($post_id);
- return $post && $post->post_author == $this->user_id;
- }
-
- /**
- * Processes operation from Operation Queue
- * @param WP_Error|array $result
- * @param object $operation
- * @param array $data
- *
- * @return array|WP_Error
- */
- public function processOperation(WP_Error|array $result, object $operation, array $data):array|WP_Error
- {
- if ($operation->type === 'batch_creation') {
- $JVB = JVB();
- $queue = $JVB->queue();
-
- $images = $queue->getOperationValue($operation->id.'_upload', 'result')??false;
-
- $this->user_id = $operation->user_id;
- $this->post_type = BASE.$data['content'];
- try {
- $results = [];
- if ($images) {
- if ($data['mode'] == 'selection') {
- $total = count($images);
- foreach ($images as $group => $files) {
- $settings = json_decode($data['files_data'][$group]);
-
- switch ($settings->type) {
- case 'group':
- $featuredIndex = $settings->metadata->featuredFile??0;
- $title = $settings->metadata->title??$this->generatePostTitle($data['content']);
- $new = wp_insert_post([
- 'post_type' => BASE.$data['content'],
- 'post_title' => $title,
- 'post_status' => 'draft',
- 'post_author' => $operation->user_id
- ]);
- if ($new && !is_wp_error($new)) {
- set_post_thumbnail($new, $files[$featuredIndex]['attachment_id']);
- unset($files[$featuredIndex]);
- if (!empty($files)) {
- $meta = new MetaManager($new, 'post');
- $IDs = array_column($files, 'attachment_id');
- $meta->updateValue('gallery', implode(',', $IDs));
- }
- $results[] = $new;
-// $queue->updateOperationProgress($operation->id, $group + 1, $total);
- }
- break;
- default:
- foreach ($files as $img) {
- $new = wp_insert_post([
- 'post_type' => BASE. $data['content'],
- 'post_title' => $this->generatePostTitle($data['content']),
- 'post_status' => 'draft',
- 'post_author' => $operation->user_id
- ]);
-
- if ($new && !is_wp_error($new)) {
- set_post_thumbnail($new, $img['attachment_id']);
- $results[] = $new;
-// $queue->updateOperationProgress($operation->id, $group + 1, $total);
- }
- }
- break;
- }
- }
- } else {
- $total = count($images);
- foreach ($images as $key => $img) {
- $new = wp_insert_post([
- 'post_type' => BASE.$data['content'],
- 'post_title' => $this->generatePostTitle($data['content']),
- 'post_status' => 'draft',
- 'post_author' => $operation->user_id
- ]);
- if ($new && !is_wp_error($new)) {
- set_post_thumbnail($new, $img['attachment_id']);
- }
- $results[] = $new;
-// $queue->updateOperationProgress($operation->id, $key + 1, $total);
- }
- }
-
- //Clear cache
- CacheManager::invalidateGroup($data['content']);
- CacheManager::invalidateGroup('feed');
- CacheManager::invalidateGroup('user_content');
- }
-
- return [
- 'success' => true,
- 'result' => $results
- ];
- } catch (Exception $e) {
- $JVB->error()->log(
- '[ContentRoutes]:processOperation',
- $e->getMessage()
- );
- }
-
- return $results;
- } elseif ($operation->type == 'content_update') {
- $result = $this->processBatches($operation, $data);
- }
-
- return $result;
- }
+ return $item;
+ }
}
--
Gitblit v1.10.0