From 3d25ad7de4fdbb3eba2cde2a62422e6be5d79a0a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 25 Aug 2026 22:57:22 +0000
Subject: [PATCH] =It's been a minute. I went through the Routes and associated Managers and ensured they played together well. Reworked the Cache.php, and I think I caught all the references. Moving away from JVBase() root class, to individuall classes that are Instanced. Double checked for the ABSPATH check in every file, and added index.php to every directory that didn't already have it. Also still working on the MenuBlock to work with WooCommerce for now for Northeh

---
 inc/rest/routes/ContentRoutes.php |  325 +++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 222 insertions(+), 103 deletions(-)

diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index 119b3a1..7d5ef42 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -2,21 +2,23 @@
 
 namespace JVBase\rest\routes;
 
+use JVBase\Instance;
+use JVBase\managers\Cache;
 use JVBase\managers\queue\executors\ContentExecutor;
+use JVBase\managers\queue\Queue;
 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
@@ -24,6 +26,7 @@
 
 class ContentRoutes extends Rest
 {
+	use Instance;
 	protected array $fields = [];
 	protected array $taxonomies = [];
 	protected string $post_type = '';
@@ -34,15 +37,21 @@
 	protected array $timelineUniqueFields = [];
 	protected static ?string $action = 'dash-';
 	protected Meta $meta;
+	protected Cache $termCache;
 
 	public function __construct()
 	{
-		$this->cacheName = 'user_content_' . get_current_user_id();
+		$this->cacheName = 'user_content';
 		parent::__construct();
+		$this->cache = Cache::for($this->cacheName, 'post', DAY_IN_SECONDS);
+		$this->cache->user();
+		$this->termCache = Cache::for($this->cacheName.'_terms', 'term', DAY_IN_SECONDS);
+		$this->termCache->user();
+
+
 		if (JVB_TESTING) {
 			$this->cache->flush();
 		}
-		$this->cache->connect('post', true);
 		add_action('init', [$this, 'registerContentExecutors'], 5);
 	}
 
@@ -51,7 +60,7 @@
 	 */
 	public function registerContentExecutors(): void
 	{
-		$registry = JVB()->queue()->registry();
+		$registry = Queue::getInstance()->registry();
 		$executor = new ContentExecutor();
 
 		// Content updates - chunked at 10 posts
@@ -76,20 +85,38 @@
 		Route::for('content')
 			->get([$this, 'getContent'])
 			->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
-			->rateLimit(20)
+			->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(30)
 			->post([$this, 'postContent'])
 			->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
-			->rateLimit(30);
+			->rateLimit()
+			->args([
+				'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 +129,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 +147,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;
@@ -145,28 +171,24 @@
 	public function postContent(WP_REST_Request $request): WP_REST_Response
 	{
 		$data = $request->get_params();
-		$user_id = $data['user'];
+		$user_id = get_current_user_id();
 
 		if (!array_key_exists('posts', $data) || !is_array($data['posts'])) {
 			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 = Queue::getInstance();
 		$queue->queueOperation(
 			'content_update',
 			$user_id,
 			$data,
 			[
-				'count' => $count,
-				'chunk_key' => 'posts',
-				'chunk_size' => 10,
 				'operation_id' => $operationId
 			]
 		);
@@ -184,6 +206,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 +234,6 @@
 		}
 		$post_type = str_replace('-', '_', jvbCheckBase($params['content']));
 
-
 		// Build query args
 		$args = [
 			'post_type' => $post_type,
@@ -205,13 +244,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,22 +279,14 @@
 			$args['s'] = sanitize_text_field($params['search']);
 		}
 
-
 		$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;
-		}
-
-		$cache = $this->cache->get($key);
-		if ($cache) {
-			$response = Response::success($cache);
-			return $this->addCacheHeaders($response);
+		$cached = $this->cache->get($key);
+		if ($cached) {
+			return $cached;
 		}
 
 		$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);
 		}
@@ -261,10 +294,11 @@
 		// Run query
 		$query = new WP_Query($args);
 
-
-		$this->fields = jvbGetFields(str_replace('-', '_', $this->post_type));
+		$registrar = Registrar::getInstance($this->post_type);
+		$this->fields = $registrar->getFields()??[];
 		$this->taxonomies = $this->getTaxonomies($this->post_type);
-		$posts = array_map([$this, 'prepareItem'], $query->posts);
+
+		$posts = array_map([$this, 'preparePost'], $query->posts);
 
 		$data = [
 			'items' => $posts,
@@ -276,8 +310,88 @@
 
 		$this->cache->set($key, $data);
 
-		$response = Response::success($data);
-		return $this->addCacheHeaders($response);
+		return Response::success($data);
+	}
+	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
+//		$cache_check = $this->checkHeaders($request, $key);
+//		if ($cache_check) {
+//			return $cache_check;
+//		}
+
+		$cache = $this->cache->get($key);
+		if ($cache) {
+			return Response::success($cache);
+		}
+		$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);
+		}
+
+		// Run query
+		$query = new WP_Term_Query($args);
+
+		$terms = $query->get_terms();
+		$data = [
+			'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);
+
+		return Response::success($data);
 	}
 
 	protected function applySearchFilters(array $args, array $params): array
@@ -369,16 +483,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 +520,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,72 +537,72 @@
 			'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);
 
 		//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']);
+		$children = jvbTimelinePoints($post->ID, $post->post_type,['publish', 'draft']);
 		array_unshift($children, $post->ID);
 
 		$subFields = [];
@@ -496,7 +615,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