From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan

---
 inc/rest/routes/ContentRoutes.php |  777 +++--------------------------------------------------------
 1 files changed, 41 insertions(+), 736 deletions(-)

diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index 8ff8c78..119b3a1 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -2,12 +2,14 @@
 
 namespace JVBase\rest\routes;
 
-use JVBase\JVB;
 use JVBase\managers\queue\executors\ContentExecutor;
 use JVBase\managers\queue\TypeConfig;
-use JVBase\rest\RestRouteManager;
-use JVBase\managers\CacheManager;
-use JVBase\meta\MetaManager;
+use JVBase\meta\Meta;
+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;
@@ -20,25 +22,27 @@
 	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 = '';
 
 	//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();
+		$this->cacheName = 'user_content_' . get_current_user_id();
 		parent::__construct();
-		$this->cache->clear();
-		$this->action = 'dash-';
-		$this->operation_type = 'content_update';
+		if (JVB_TESTING) {
+			$this->cache->flush();
+		}
+		$this->cache->connect('post', true);
 		add_action('init', [$this, 'registerContentExecutors'], 5);
 	}
 
@@ -57,10 +61,10 @@
 			chunkSize: 10
 		));
 
-		// Batch creation (from uploads)
-		$registry->register('batch_creation', new TypeConfig(
-			executor: $executor
-		));
+		// Batch creation (from uploads) TODO: I believe this is all handled by UploadExecutor
+//		$registry->register('batch_creation', new TypeConfig(
+//			executor: $executor
+//		));
 	}
 
 	/**
@@ -69,35 +73,13 @@
 	 */
 	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']
-			]
-		]);
-
-		//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']
-			]
-		]);
+		Route::for('content')
+			->get([$this, 'getContent'])
+			->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
+			->rateLimit(20)
+			->post([$this, 'postContent'])
+			->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
+			->rateLimit(30);
 	}
 
 	protected function initTimelineFields(string $content): void
@@ -160,24 +142,13 @@
 	 *
 	 * @return WP_REST_Response
 	 */
-	public function handleContentUpdate(WP_REST_Request $request): WP_REST_Response
+	public function postContent(WP_REST_Request $request): WP_REST_Response
 	{
 		$data = $request->get_params();
-		error_log('Received data: ' . print_r($data, true));
 		$user_id = $data['user'];
 
-		if (!$this->userCheck($user_id)) {
-			return new WP_REST_Response([
-				'success' => true,
-				'message' => 'You for real?'
-			]);
-		}
-
 		if (!array_key_exists('posts', $data) || !is_array($data['posts'])) {
-			return new WP_REST_Response([
-				'success' => true,
-				'message' => 'No posts found'
-			]);
+			return Response::success(['message'=>'No posts found in request']);
 		}
 
 		$count = count($data['posts']);
@@ -185,6 +156,8 @@
 		unset($data['user']);
 		unset($data['id']);
 
+		error_log('[CONTENT]:'.print_r($data, true));
+
 		$queue = JVB()->queue();
 		$queue->queueOperation(
 			'content_update',
@@ -197,50 +170,8 @@
 				'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);
 	}
 
 
@@ -250,16 +181,10 @@
 	 *
 	 * @return WP_REST_Response
 	 */
-	public function handleContentRequest(WP_REST_Request $request): WP_REST_Response
+	public function getContent(WP_REST_Request $request): WP_REST_Response
 	{
 		$params = $request->get_params();
 		$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?',
-			]);
-		}
 
 		$post_status = $params['status'];
 		if ($post_status === 'all') {
@@ -313,20 +238,17 @@
 			$args['s'] = sanitize_text_field($params['search']);
 		}
 
+
 		$key = $this->cache->generateKey($args);
 		// Check HTTP cache headers with the specific content type
-		$content_type = $params['content'] ?? $params['type'];
-		$cache_check = $this->checkHeaders($request, $content_type, [
-			'filter_hash' => $key,
-		]);
+		$cache_check = $this->checkHeaders($request, $key);
 		if ($cache_check) {
 			return $cache_check;
 		}
 
-
 		$cache = $this->cache->get($key);
 		if ($cache) {
-			$response = new WP_REST_Response($cache);
+			$response = Response::success($cache);
 			return $this->addCacheHeaders($response);
 		}
 
@@ -344,17 +266,17 @@
 		$this->taxonomies = $this->getTaxonomies($this->post_type);
 		$posts = array_map([$this, 'prepareItem'], $query->posts);
 
-
 		$data = [
 			'items' => $posts,
 			'total' => $query->found_posts,
-			'total_pages' => $query->max_num_pages
+			'total_pages' => $query->max_num_pages,
+			'has_more'	=> $args['paged']??1 < $query->max_num_pages,
 		];
 
 
 		$this->cache->set($key, $data);
 
-		$response = new WP_REST_Response($data);
+		$response = Response::success($data);
 		return $this->addCacheHeaders($response);
 	}
 
@@ -460,454 +382,7 @@
 		return $out;
 	}
 
-	/**
-	 * 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'];
 
-		if (empty($posts)) {
-			return [
-				'success' => false,
-				'message' => 'No posts to update'
-			];
-		}
-
-		$results = [];
-
-		foreach ($posts as $ID => $post_data) {
-			if (Features::forContent($post_data['content'])->has('is_timeline') && array_key_exists('timeline', $post_data)) {
-				// Handle timeline posts - ensure we have a valid integer ID
-				$parent_id = (int)$ID;
-
-				// Skip if ID is invalid (0, 'null', etc would become 0)
-				if ($parent_id === 0) {
-					error_log('Invalid timeline parent ID: ' . $ID);
-					$results[$ID] = [
-						'success' => false,
-						'message' => 'Invalid parent post ID for timeline'
-					];
-					continue;
-				}
-
-				$results[$ID] = $this->processTimelinePost($parent_id, $post_data);
-				continue;
-			}
-			if (str_starts_with($ID, 'new')) {
-
-				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',
-				]);
-				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
-				];
-			} 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);
-				$results[$ID] = [
-					'success' => $success
-				];
-
-			}
-
-			CacheManager::for($post_data['content'])->clear();
-			if (jvbSiteUsesFeedBlock()) {
-				CacheManager::for('feed')->clear();
-			}
-		}
-
-		if (jvbSiteHasNotifications()) {
-			$this->notifications = JVB()->notification();
-			$this->notifications->addNotification(
-				$this->user_id,
-				'content_update_complete',
-				null,
-				'Content updates completed!'
-			);
-		}
-
-
-		return [
-			'success' => true,
-			'result' => $results
-		];
-	}
-
-	/**
-	 * Extracts the postdata for timeline post child posts from the pseudo-repeater element
-	 * @param int $parent_id
-	 * @param array $post_data
-	 * @return array|true[]
-	 */
-	protected function processTimelinePost(int $parent_id, array $post_data): array
-	{
-		if (!$this->verifyOwnership($parent_id)) {
-			return ['success' => false, 'message' => 'No permission'];
-		}
-
-		$ignore = ['content', 'user'];
-		$this->fields = jvbGetFields($post_data['content']);
-		$this->initTimelineFields($post_data['content']);
-
-		// Get parent post details
-		$parent_post = get_post($parent_id);
-		$parent_title = $parent_post->post_title;
-		$parent_is_published = ($parent_post->post_status === 'publish');
-
-		// Extract shared data from top level (excluding post_thumbnail which is unique per post)
-		$sharedData = array_filter($post_data, function ($key) use ($ignore) {
-			return in_array($key, $this->timelineSharedFields)
-				&& !in_array($key, $ignore)
-				&& $key !== 'post_thumbnail';
-		}, ARRAY_FILTER_USE_KEY);
-
-		// If no shared post_title at top level, extract from first timeline entry
-		if (!isset($sharedData['post_title']) && isset($post_data['timeline'][0]['post_title'])) {
-			$sharedData['post_title'] = $post_data['timeline'][0]['post_title'];
-		}
-		$clearParent = false;
-		if (array_key_exists('timeline', $post_data) && is_array($post_data['timeline'])) {
-			// Remove post_title and post_thumbnail from shared taxonomies
-			$sharedTaxonomies = array_filter($sharedData, function ($key) {
-				return $key !== 'post_title' && $key !== 'post_thumbnail';
-			}, ARRAY_FILTER_USE_KEY);
-
-			// Ensure the parent post exists and is still first in the array
-			$index = array_search((string)$parent_id, array_column($post_data['timeline'], 'id'));
-
-			if ($index === false) {
-				return [
-					'success' => false,
-					'message' => 'Missing parent id. This should not have happened'
-				];
-			}
-
-			if ($index !== 0) {
-				$new_parent_id = $post_data['timeline'][0]['id'];
-
-				if (is_numeric($new_parent_id) && (int)$new_parent_id > 0) {
-					$new_parent_id = (int)$new_parent_id;
-					wp_update_post([
-						'ID' => $new_parent_id,
-						'post_parent' => 0
-					]);
-
-					wp_update_post([
-						'ID' => $parent_id,
-						'post_parent' => $new_parent_id
-					]);
-
-					$existing_children = get_children([
-						'post_parent' => $parent_id,
-						'fields' => 'ids'
-					]);
-
-					foreach ($existing_children as $child_id) {
-						if ($child_id !== $new_parent_id) {
-							wp_update_post([
-								'ID' => $child_id,
-								'post_parent' => $new_parent_id
-							]);
-						}
-					}
-
-					// Update parent references
-					$parent_id = $new_parent_id;
-					$parent_post = get_post($parent_id);
-					$parent_title = $parent_post->post_title;
-					$parent_is_published = ($parent_post->post_status === 'publish');
-				} else {
-					$item = $post_data['timeline'][$index];
-					unset($post_data['timeline'][$index]);
-					array_unshift($post_data['timeline'], $item);
-				}
-			}
-
-			$errors = [];
-			$success = [];
-			$existing_children = get_children([
-				'post_parent' => $parent_id,
-				'orderby' => 'menu_order',
-				'post_status' => ['publish', 'draft'],
-				'fields' => 'ids'
-			]);
-
-			$prevDate = null;
-
-			foreach ($post_data['timeline'] as $order => $timeline) {
-				// Get unique fields for this specific timeline entry
-				$allowedFields = array_filter($timeline, function ($key) use ($ignore) {
-					return in_array($key, $this->timelineUniqueFields) && !in_array($key, $ignore);
-				}, ARRAY_FILTER_USE_KEY);
-
-				// Determine the post title
-				$is_parent = ((int)$timeline['id'] === $parent_id);
-				$provided_title = $timeline['post_title'] ?? '';
-				$auto_generated_pattern = '/^.+Treatment #?\d+$/'; // Matches "Title - Treatment #1" or "Title - Treatment 1"
-
-				if ($is_parent) {
-					// Parent keeps its own title or uses shared title
-					$allowedFields['post_title'] = $provided_title ?: ($sharedData['post_title'] ?? $parent_title);
-				} else {
-					// For child posts, auto-generate if:
-					// 1. No title provided, OR
-					// 2. Title matches auto-generated pattern (meaning it wasn't customized)
-					if (empty($provided_title) || preg_match($auto_generated_pattern, $provided_title)) {
-						$allowedFields['post_title'] = 'Treatment ' . $order;
-					} else {
-						// Keep custom title
-						$allowedFields['post_title'] = $provided_title;
-					}
-				}
-
-				// Merge with shared taxonomies AFTER setting unique fields
-				$allowedFields = array_merge($sharedTaxonomies, $allowedFields);
-
-				// Handle post creation if needed
-				if (!array_key_exists('id', $timeline) || !is_numeric($timeline['id'])) {
-					$newChild = wp_insert_post([
-						'post_author' => $this->user_id,
-						'post_type' => jvbCheckBase($post_data['content']),
-						'post_title' => $allowedFields['post_title'],
-						'post_parent' => $parent_id,
-						'menu_order' => $order,
-						'post_status' => $parent_is_published ? 'publish' : 'draft'
-					]);
-					if (!$newChild || is_wp_error($newChild)) {
-						$errors[] = [
-							'message' => 'Could not create child post',
-							'data' => $timeline
-						];
-						continue;
-					}
-					$timeline['id'] = $newChild;
-				}
-
-				if (in_array((int)$timeline['id'], $existing_children)) {
-					unset($existing_children[array_search((int)$timeline['id'], $existing_children)]);
-				}
-
-				// Update post status and menu order
-				$post_updates = ['ID' => $timeline['id']];
-
-				if (!$is_parent) {
-					$post_updates['menu_order'] = $order;
-
-					// Auto-publish child if parent is published
-					if ($parent_is_published) {
-						$current_post = get_post($timeline['id']);
-						if ($current_post && $current_post->post_status !== 'publish') {
-							$post_updates['post_status'] = 'publish';
-						}
-					}
-				}
-
-				if (count($post_updates) > 1) {
-					$result = wp_update_post($post_updates);
-					error_log('Updated post ' . $timeline['id'] . ' with: ' . print_r($post_updates, true) . ' Result: ' . $result);
-					$clearParent = true;
-				}
-
-				// Update metadata
-				$meta = new MetaManager($timeline['id'], 'post');
-				$oldValues = $meta->getAll(array_keys($allowedFields));
-
-				// Set number taxonomy to menu_order (always update for reordering)
-				if (!$is_parent) {
-					$number_value = $order;
-					$term = get_term_by('name', (string)$number_value, BASE . 'number');
-					if (!$term) {
-						$result = wp_insert_term((string)$number_value, BASE . 'number');
-						if ($result && !is_wp_error($result)) {
-							$term = $result['term_id'];
-						}
-					} else {
-						$term = $term->term_id;
-					}
-					$allowedFields['number'] = $term;
-				}
-
-				// Auto-timeline logic
-				if ($prevDate) {
-					$newDate = array_key_exists('date', $oldValues) ? $oldValues['date'] : ((array_key_exists('date', $allowedFields)) ? $allowedFields['date'] : null);
-					if ($newDate) {
-						$date1 = new \DateTime($prevDate);
-						$date2 = new \DateTime($newDate);
-						$weeks = floor($date1->diff($date2)->days / 7);
-						if ($weeks > 0) {
-							$termToCheck = $weeks . ' Weeks';
-							$term = get_term_by('name', $termToCheck, BASE . 'timeline');
-							if (!$term) {
-								$result = wp_insert_term($termToCheck, BASE . 'timeline');
-								if ($result && !is_wp_error($result)) {
-									$term = $result['term_id'];
-								}
-							} else {
-								$term = $term->term_id;
-							}
-							$allowedFields['timeline'] = $term;
-						}
-					}
-				}
-				$prevDate = array_key_exists('date', $oldValues) ? $oldValues['date'] : ((array_key_exists('date', $allowedFields)) ? $allowedFields['date'] : $prevDate);
-
-				$updateValues = array_filter($allowedFields, function ($value, $key) use ($oldValues) {
-					return (!array_key_exists($key, $oldValues) || $value !== $oldValues[$key]);
-				}, ARRAY_FILTER_USE_BOTH);
-				error_log('Setting values for ' . $timeline['id'] . ': ' . print_r($updateValues, true));
-
-				$meta->setAll($updateValues);
-				$timeline['id'] = (int)$timeline['id'];
-
-				$success[] = $timeline['id'];
-			}
-		}
-
-		// Delete any remaining children that no longer exist
-		if (!empty($existing_children)) {
-			foreach ($existing_children as $ID) {
-				wp_delete_post($ID);
-			}
-		}
-
-		if ($clearParent) {
-			$this->cache->clear();
-			CacheManager::onPostSave($parent_id, $parent_post);
-		}
-
-
-		return ['success' => true, 'data' => [
-			'success' => $success,
-			'errors' => $errors
-		]];
-	}
-
-	/**
-	 * 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...',
-			]);
-		}
-
-		$data = $request->get_params();
-
-
-		$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'
-			]
-		);
-
-		return $response;
-	}
 
 	/**
 	 * Generates a post title, based on content type
@@ -934,7 +409,7 @@
 			$this->initTimelineFields($post->post_type);
 			return $this->formatTimeline($post);
 		}
-		$this->meta = new MetaManager($post->ID, 'post');
+		$this->meta = Meta::forPost($post->ID);
 		$data = [
 			'id' => $post->ID,
 			'title' => $post->post_title,
@@ -1004,7 +479,7 @@
 	{
 		$item = $this->prepareItem($post, true, false);
 		//Step 1: Get the fields that apply to all posts
-		$mainMeta = new MetaManager($post->ID, 'post');
+		$mainMeta = Meta::forPost($post->ID);
 		$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
 
 		//Step 2: Get the fields for each individual posts
@@ -1014,7 +489,7 @@
 		$subFields = [];
 		$images = [];
 		foreach ($children as $child) {
-			$meta = new MetaManager($child, 'post');
+			$meta = Meta::forPost($child);
 			$f = $meta->getAll($this->timelineUniqueFields);
 			$f = ['id' => $child] + $f;
 			$subFields[] = $f;
@@ -1023,178 +498,8 @@
 		}
 		$item['fields']['timeline'] = $subFields;
 		$item['images'] = $item['images'] + $images;
-
+		$item['number'] = $mainMeta->get('number');
 
 		return $item;
 	}
-
-	/**
-	 * 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::for($data['content'])->clear();
-					CacheManager::for('feed')->clear();
-				}
-
-				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;
-	}
 }

--
Gitblit v1.10.0