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/FeedRoutes.php |  181 ++++++++++++++++-----------------------------
 1 files changed, 65 insertions(+), 116 deletions(-)

diff --git a/inc/rest/routes/FeedRoutes.php b/inc/rest/routes/FeedRoutes.php
index 76f6b61..5d43c6a 100644
--- a/inc/rest/routes/FeedRoutes.php
+++ b/inc/rest/routes/FeedRoutes.php
@@ -1,17 +1,19 @@
 <?php
 namespace JVBase\rest\routes;
 
+use JVBase\base\Site;
+use JVBase\Instance;
+use JVBase\integrations\services\Umami;
+use JVBase\managers\Cache;
 use JVBase\meta\Meta;
 use JVBase\registrar\Registrar;
 use JVBase\rest\Rest;
-use JVBase\integrations\Umami;
 use JVBase\rest\Route;
-use JVBase\base\Site;
-use WP_Query;
 use WP_Post;
-use WP_Term;
+use WP_Query;
 use WP_REST_Request;
 use WP_REST_Response;
+use WP_Term;
 
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
@@ -19,6 +21,7 @@
 
 class FeedRoutes extends Rest
 {
+	use Instance;
 	protected int $per_page = 36;
 	protected ?Umami $tracker = null;
 
@@ -31,10 +34,7 @@
 		$this->cacheName = 'feed';
 		$this->cacheTtl = 86400;
 		parent::__construct();
-		$this->cache
-			->connect('post', true)
-			->connect('taxonomy', true)
-			->connect('user', true);
+		$this->cache = Cache::for('feed');
 
 		if (JVB_TESTING) {
 			$this->cache->flush();
@@ -45,7 +45,7 @@
 	public function init():void
 	{
 		if (Site::hasIntegration('umami')) {
-			$this->tracker = JVB()->connect('umami');
+			$this->tracker = Umami::getInstance();
 		}
 	}
 
@@ -70,7 +70,6 @@
 				'context' => 'string',
 				'contextId' => 'string',
 				'favourites' => 'boolean',
-				'user' => 'integer',
 				'highlight' => 'string',
 			])
 			->auth('public')
@@ -89,7 +88,6 @@
 				'context' => 'string',
 				'contextId' => 'string',
 				'favourites' => 'boolean',
-				'user' => 'integer',
 				'highlight' => 'string',
 			])
 			->auth('public')
@@ -117,11 +115,13 @@
 				$post = get_post($postID);
 				$type = jvbNoBase($post->post_type);
 				$metaType = 'post';
+				$this->cache = Cache::for('feedPosts', 'post');
 				break;
 			default:
 				$post = get_term($postID, jvbCheckBase($type));
 				$type = jvbNoBase($type);
 				$metaType = 'term';
+				$this->cache = Cache::for('feedTerms', 'term');
 				break;
 		}
 		if (!$post || is_wp_error($post)) {
@@ -149,21 +149,16 @@
 						$meta = Meta::forUser($postID);
 						$registrar = Registrar::getInstance($type);
 						break;
+					default:
+						$meta = false;
 				}
-				if (!$registrar) {
+				if (!$registrar || !$meta) {
 					return [];
 				}
-				$fields = $registrar->getFields();
+				[$images, $fields] = $registrar->getFeedFields();
 
-				//Allow custom filtering for public fields
-				if (!empty($registrar->getConfig('feed')['fields'])) {
-					$fields = array_filter($fields, function($field) use ($registrar) {
-						return in_array($field, $registrar->getConfig('feed')['fields']);
-					}, ARRAY_FILTER_USE_KEY);
-				}
-
-				$values = $meta->getAll(array_keys($fields));
-
+				$all = array_merge($images, $fields);
+				$values = $meta->getAll(array_map(function($f) { return $f['name']; }, $all));
 				$out = [
 					'fields' => $values,
 				];
@@ -173,23 +168,18 @@
 
 				//Add images
 				$imgIDs = [];
-				$temp = array_filter($fields, function($field) {
-					return in_array($field['type'], [ 'upload', 'image', 'gallery']);
-				});
-
-				foreach ($temp as $key => $config) {
-					if (array_key_exists($key, $out['fields']) && $out['fields'][$key] !== '') {
-						$IDs = array_map('absint', explode(',',$out['fields'][$key]));
-						foreach ($IDs as $ID) {
-							$imgIDs[$ID] = jvbImageData($ID);
-						}
+				$imgs = $meta->getAll(array_map(function($f) {return $f['name'];}, $images));
+				foreach ($imgs as $img) {
+					$IDs = array_map('absint', explode(',',$img));
+					foreach ($IDs as $ID) {
+						$imgIDs[$ID] = jvbImageData($ID);
 					}
 				}
 				$out['images'] = $imgIDs;
 
 				$out['id'] = $postID;
 				$out['content'] = $type;
-				$out['icon'] = $registrar->getIcon()??jvbDefaultIcon();
+				$out['icon'] = $registrar->getIcon();
 				if ($out['icon'] === '') {
 					$out['icon'] = jvbDefaultIcon();
 				}
@@ -212,8 +202,6 @@
 						$out['title'] = html_entity_decode($post->name);
 						break;
 					case 'post':
-						$out['date'] = $post->post_date;
-						$out['date_modified'] = $post->post_modified;
 						$out['user_id'] = (int)$post->post_author;
 						$out['url'] = get_the_permalink($postID);
 						$out['title']= get_the_title($postID);
@@ -253,37 +241,30 @@
 
 	protected function formatTimeline(int $postID, WP_Post $post):array
 	{
-		if (!$this->timelineSharedFields || !$this->timelineUniqueFields){
-			$this->initTimelineFields($post->post_type);
-		}
-		$item = $this->formatItem($postID, 'post', true);
 		//Step 1: Get the fields that apply to all posts
-		$mainMeta = Meta::forPost($post->ID);
-		$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
+		$item = $this->formatItem($postID, 'post', true);
 
 		//Step 2: Get the fields for each individual posts
-		$children = get_children(['post_parent' => $post->ID, 'orderby' => 'date', 'order' => 'ASC', 'post_status' => ['publish'], 'fields'=> 'ids']);
-		array_unshift($children, $post->ID);
+		$children = jvbTimelinePoints($postID, $post->post_type);
+		$last = $children[array_key_last($children)];
+			$lastMeta = Meta::forPost($last);
+			$lastImg = $lastMeta->get('post_thumbnail');
 
-		$item['taxonomies'] = $this->extractTaxonomies($item['fields'], $postID, jvbNoBase($post->post_type));
+			$item['fields']['after'] = $lastImg;
+			$item['images'][$lastImg] = jvbImageData((int) $lastImg);
+			$item['fields']['post_modified'] = $lastMeta->get('post_date');
 
-		$subFields = [];
-		$images = [];
-		foreach ($children as $child) {
-			$meta = Meta::forPost($child);
-			$f = $meta->getAll($this->timelineUniqueFields);
-			$f =  ['id' => $child] + $f;
-			$subFields[] = $f;
-			$item['taxonomies'] = array_merge($item['taxonomies'], $this->extractTaxonomies($f, $postID, jvbNoBase($post->post_type)));
-			$images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']);
+			$count = count(array_filter($children, function ($child) {
+				$tmp = Meta::forPost($child);
+				return empty($tmp->get('is_update')) || $tmp->get('is_update') !==1;
+			}));
+
+		foreach($item['taxonomies'] as $tax => $items) {
+			$item[$tax] = array_keys($items);
 		}
-		$item['number'] = (int)get_post_meta($post->ID,BASE.'number', true);
-		$item['fields']['before'] = get_post_thumbnail_id($children[0]);
-		$item['fields']['after'] = get_post_thumbnail_id($children[array_key_last($children)]);
 
-		$item['fields']['timeline_gallery'] = $subFields;
-		$item['images'] = $item['images'] + $images;
-
+		$item['number'] = $count;
+		$item['fields']['before'] = $item['fields']['post_thumbnail'];
 
 		return $item;
 	}
@@ -317,6 +298,7 @@
 
 	protected function formatTaxonomy(WP_Term|int $term, int $postID, string $type)
 	{
+		$this->cache = Cache::for('feedTaxonomies', 'term');
 		return $this->cache->remember(
 			$term->term_id,
 			function () use ($term, $postID, $type) {
@@ -337,6 +319,8 @@
 
 	protected function getAuthorData(WP_Post $post)
 	{
+		//Connects to their profile, not user id
+		$this->cache = Cache::for('feedUsers', 'post');
 		$author = $post->post_author;
 		$userLink = get_user_meta($author, BASE.'profile_link', true);
 		return $this->cache->remember(
@@ -363,6 +347,10 @@
 	protected function getTaxonomies(int $postID, string $content): array
 	{
 		$registrar = Registrar::getInstance($content)??false;
+		if (!$registrar) {
+			return [];
+		}
+		$this->cache = Cache::for('feedTaxonomyLinks', 'term');
 		$taxonomies = $registrar->registrar->taxonomies;
 		$out = [];
 		foreach ($taxonomies as $tax) {
@@ -434,41 +422,16 @@
 	public function handleFeedRequest(WP_REST_Request $request): WP_REST_Response
 	{
 		$args = $this->buildRequestArgs($request);
-		$key = $this->cache->generateKey($args);
 
-		// Check HTTP cache headers first
-		$cache_check = $this->checkHeaders(
-			$request,
-			$key
-		);
-		if ($cache_check) {
-			return $cache_check; // Returns 304 Not Modified
-		}
-
-		$cached = $this->cache->get($key);
-		if ($cached) {
-			if ($request->get_param('highlight')) {
-				$highlight = json_decode($request->get_param('highlight'), true);
-				$args['highlight'] = $highlight;
-			}
-			$cached['items'] = $this->processHighlightedItem($cached['items'], $args);
-			$response = $this->success($cached);
-			return $this->addCacheHeaders($response);
-		}
-		// Fetch and format items
 		$items = $this->fetchFeedItems($args);
-
-		$ttl = (str_contains($args['orderby'], 'RAND')) ? 300 : $this->cacheTtl;
-		$this->cache->set($key, $items, $ttl);
-
-		if ($request->get_param('highlight')) {
-			$highlight = json_decode($request->get_param('highlight'), true);
+		$highlight = $request->get_param('highlight');
+		if (!empty($highlight)) {
+			$highlight = json_decode($highlight, true);
 			$args['highlight'] = $highlight;
+			$items['items'] = $this->processHighlightedItem($items['items'], $args);
 		}
 
-		$items['items'] = $this->processHighlightedItem($items['items'], $args);
-		$response = $this->success($items);
-		return $this->addCacheHeaders($response);
+		return $this->success($items);
 	}
 
 	/**
@@ -520,7 +483,7 @@
 					: explode(',', $args['post_type']);
 
 				// Check if filtering global feed content
-				if (in_array(jvbNoBase($context['type']), Registrar::getFeatured('is_content', 'term'))) {
+				if (in_array(jvbNoBase($context['type']), Registrar::withFeature('is_content', 'term'))) {
 					// Global: show posts from any content type with this taxonomy
 					$for_content = Registrar::getInstance($context['type'])->registrar->for ?? [];
 
@@ -528,7 +491,7 @@
 					$post_types = array_map(fn($type) => jvbCheckBase($type), $for_content);
 
 					// Filter to only show_feed content types
-					$show_feed_types = Registrar::getFeatured('show_feed', 'post');
+					$show_feed_types = Registrar::withFeature('show_feed', 'post');
 					$args['post_type'] = array_intersect(
 						$post_types,
 						array_map(fn($type) => jvbCheckBase($type), $show_feed_types)
@@ -546,7 +509,6 @@
 				// Add term to tax query
 				$args['tax_query'][] = [
 					'taxonomy' => $registrar->getBased(),
-//					'field' => 'term_id',
 					'terms' => [(int)$context['id']],
 				];
 				break;
@@ -563,11 +525,15 @@
 	 */
 	protected function applyFavouritesFilter(array $args, array $data): array
 	{
-		if (empty($data['favourites']) || empty($data['user'])) {
+		if (empty($data['favourites'])) {
 			return $args;
 		}
 
-		$user_id = (int)$data['user'];
+		$user_id = get_current_user_id();
+		if (!$user_id) {
+			//Shouldn't happen, but whatever
+			return $args;
+		}
 		$content = jvbNoBase($args['post_type']);
 
 		// Get user's favourites for this content type
@@ -612,15 +578,11 @@
 		$args['fields'] = 'ids';
 		// Get post IDs
 		$query = new WP_Query($args);
-
-		// Batch prefetch related data
-		update_meta_cache('post', $query->posts);
-		update_object_term_cache($query->posts, $args['post_type']);
-
 		// Format regular items
 		$items = array_map(fn($post) => $this->formatItem($post), $query->posts);
 
 		wp_reset_postdata();
+
 		return [
 			'items' => $items,
 			'has_more' => $query->max_num_pages > $args['paged'],
@@ -630,7 +592,7 @@
 
 	protected function handleContentTaxonomies(array $args): array
 	{
-
+		//TODO: This needs to be better. We have CustomTable now, maybe pass it along to that?
 		$taxonomy = jvbNoBase($args['post_type']);
 		global $wpdb;
 		$table = $wpdb->prefix . BASE . 'content_' . $taxonomy;
@@ -1140,16 +1102,7 @@
 	 */
 	public function getFeedTypes(WP_REST_Request $request): WP_REST_Response
 	{
-		// Check HTTP cache
-		$cache_check = $this->checkHeaders($request, 'feed_types');
-		if ($cache_check) {
-			return $cache_check;
-		}
-
-		$feedTypes = $this->buildFeedTypesConfig();
-
-		$response = $this->success($feedTypes);
-		return $this->addCacheHeaders($response);
+		return $this->success($this->buildFeedTypesConfig());
 	}
 
 	public function getFeedTypesConfig():array
@@ -1167,9 +1120,8 @@
 				$config = [];
 
 				// Get content types with show_feed
-				$contentTypes = Registrar::getFeatured('show_feed', 'post');
+				$contentTypes = Registrar::withFeature('show_feed', 'post');
 				foreach ($contentTypes as $slug) {
-					$this->cache->tag('content:'.$slug);
 					$registrar = Registrar::getInstance($slug);
 					if (!$registrar) continue;
 
@@ -1183,15 +1135,12 @@
 				}
 
 				// Get taxonomies with show_feed (content taxonomies)
-				$taxonomies = Registrar::getFeatured('show_feed', 'term');
+				$taxonomies = Registrar::withFeature('show_feed', 'term');
 				foreach ($taxonomies as $slug) {
 					$registrar = Registrar::getInstance($slug);
 					if (!$registrar || !($registrar->hasFeature('is_content') ?? false)) {
 						continue;
 					}
-
-					$this->cache->tag('taxonomy:'.$slug);
-
 					$config[$slug] = [
 						'type' => 'taxonomy',
 						'singular' => $registrar->getSingular(),

--
Gitblit v1.10.0