From 48721c85ebcfa973ee81719d2467ca80e4253dc9 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 01 May 2026 17:30:03 +0000
Subject: [PATCH] =Edmonton Ink hard test begins! Real testing of the managers and reset routes will commence. So far, just ensuring our classes are all loaded correctly: Site() and its sub-classes Membership, Login, etc. Care should be taken to load conditionally on 'init', as we finish defining most settings by 'plugins_loaded' at priority 5
---
inc/rest/routes/FeedRoutes.php | 613 ++++++++++++++++++++++++-------------------------------
1 files changed, 267 insertions(+), 346 deletions(-)
diff --git a/inc/rest/routes/FeedRoutes.php b/inc/rest/routes/FeedRoutes.php
index d37feea..4efc099 100644
--- a/inc/rest/routes/FeedRoutes.php
+++ b/inc/rest/routes/FeedRoutes.php
@@ -1,13 +1,12 @@
<?php
namespace JVBase\rest\routes;
-use JVBase\managers\CacheManager;
-use JVBase\rest\RestRouteManager;
+use JVBase\meta\Meta;
+use JVBase\registrar\Registrar;
+use JVBase\rest\Rest;
use JVBase\integrations\Umami;
-use JVBase\meta\MetaManager;
-use JVBase\managers\TaxonomyRelationships;
-use JVBase\utility\Checker;
-use JVBase\utility\Features;
+use JVBase\rest\Route;
+use JVBase\base\Site;
use WP_Query;
use WP_Post;
use WP_Term;
@@ -18,53 +17,36 @@
exit; // Exit if accessed directly
}
-class FeedRoutes extends RestRouteManager
+class FeedRoutes extends Rest
{
protected int $per_page = 36;
protected ?Umami $tracker = null;
- protected ?Checker $checker = null;
+
protected ?array $fields = null;
protected ?array $timelineSharedFields = null;
protected ?array $timelineUniqueFields = null;
public function __construct()
{
- $this->cache_name = 'feed';
- $this->cache_ttl = 86400;
+ $this->cacheName = 'feed';
+ $this->cacheTtl = 86400;
parent::__construct();
- $this->cache->clear();
+ $this->cache
+ ->connect('post', true)
+ ->connect('taxonomy', true)
+ ->connect('user', true);
+
+ if (JVB_TESTING) {
+ $this->cache->flush();
+ }
+
}
public function init():void
{
- $this->cache->clear();
- $this->checker = Checker::getInstance();
-
- if (jvbSiteUsesUmami()) {
+ if (Site::hasIntegration('umami')) {
$this->tracker = JVB()->connect('umami');
}
- $this->setupCacheConnections();
- }
-
- /**
- * Set up cache connections for automatic invalidation
- */
- protected function setupCacheConnections(): void
- {
- // Connect to all content types with show_feed
- $contentTypes = Features::getTypesWithFeature('show_feed', 'content');
- foreach ($contentTypes as $type) {
- CacheManager::for('feed_item_'.$type)->connectTo('post');
- $this->cache->connectTo('post', $type);
- }
-
- // Connect to all taxonomies with show_feed
- $taxonomies = Features::getTypesWithFeature('show_feed', 'taxonomy');
- foreach ($taxonomies as $tax) {
- CacheManager::for('feed_item_'.$tax)->connectTo('taxonomy');
- $this->cache->connectTo('taxonomy', $tax);
- }
-
}
/**
@@ -73,17 +55,53 @@
*/
public function registerRoutes(): void
{
- register_rest_route($this->namespace, '/feed', [
- 'methods' => ['GET', 'POST'],
- 'callback' => [$this, 'handleFeedRequest'],
- 'permission_callback' => [$this, 'checkPermission'],
- ]);
+ Route::for('feed')
+ ->get([$this, 'handleFeedRequest'])
+ ->args([
+ 'content' => 'string',
+ 'page' => 'integer|default:1|min:1',
+ 'taxonomy' => 'string',
+ 'match' => 'string|enum:all,any|default:all',
+ 'orderby' => 'string',
+ 'order' => 'string|enum:ASC,DESC',
+ 'date-filter' => 'string',
+ 'dateFrom' => 'string',
+ 'dateTo' => 'string',
+ 'context' => 'string',
+ 'source' => 'string',
+ 'favourites' => 'boolean',
+ 'user' => 'integer',
+ 'highlight' => 'string',
+ ])
+ ->auth('public')
+ ->rateLimit(30, 60)
+ ->post([$this, 'handleFeedRequest'])
+ ->args([
+ 'content' => 'string',
+ 'page' => 'integer|default:1|min:1',
+ 'taxonomy' => 'string',
+ 'match' => 'string|enum:all,any|default:all',
+ 'orderby' => 'string',
+ 'order' => 'string|enum:ASC,DESC',
+ 'date-filter' => 'string',
+ 'dateFrom' => 'string',
+ 'dateTo' => 'string',
+ 'context' => 'string',
+ 'source' => 'string',
+ 'favourites' => 'boolean',
+ 'user' => 'integer',
+ 'highlight' => 'string',
+ ])
+ ->auth('public')
+ ->rateLimit(30)
+ ->register();
- register_rest_route($this->namespace, 'feed/types', [
- 'permission_callback' => [$this, 'checkPermission'],
- 'methods' => 'GET',
- 'callback' => [$this, 'getFeedTypes']
- ]);
+ // Feed types endpoint
+ Route::for('feed/types')
+ ->get([$this, 'getFeedTypes'])
+ ->auth('public')
+ ->rateLimit()
+ ->register();
}
/**
@@ -99,46 +117,51 @@
$post = get_post($postID);
$type = jvbNoBase($post->post_type);
$metaType = 'post';
- $cache = CacheManager::for('feed_item_'.$type);
break;
default:
$post = get_term($postID, jvbCheckBase($type));
$type = jvbNoBase($type);
$metaType = 'term';
- $cache = CacheManager::for('feed_item_'.$type);
break;
}
if (!$post || is_wp_error($post)) {
return [];
}
-//
-// return $cache->remember($postID,
-// function() use ($postID, $type, $metaType, $post, $skip) {
- $config = null;
+
+ return $this->cache->remember(
+ $postID,
+ function() use ($postID, $type, $metaType, $post, $skip) {
+ $registrar = null;
switch ($metaType) {
case 'post':
- $config = JVB_CONTENT[$type];
- if (!$skip && array_key_exists('is_timeline', $config) && $config['is_timeline']) {
+ $registrar = Registrar::getInstance($type);
+ $meta = Meta::forPost($postID);
+ if (!$skip && $registrar->isTimeline()) {
return $this->formatTimeline($postID, $post);
}
break;
case 'term':
- $config = JVB_TAXONOMY[$type];
+
+ $meta = Meta::forTerm($postID);
+ $registrar = Registrar::getInstance($type);
+ break;
+ case 'user':
+ $meta = Meta::forUser($postID);
+ $registrar = Registrar::getInstance($type);
break;
}
- if (!$config) {
+ if (!$registrar) {
return [];
}
- $fields = $config['fields'];
+ $fields = $registrar->getFields();
//Allow custom filtering for public fields
- if (array_key_exists('feed', $config) && array_key_exists('fields', $config['feed'])) {
- $fields = array_filter($fields, function($field) use ($config) {
- return in_array($field, $config['feed']['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);
}
- $meta = new MetaManager($postID, $metaType);
$values = $meta->getAll(array_keys($fields));
$out = [
@@ -146,33 +169,7 @@
];
//Format Taxonomies
- $temp = array_filter($fields, function($field) {
- return $field['type'] === 'taxonomy';
- });
- foreach ($temp as $key => $config) {
- if (array_key_exists($key, $out['fields']) && $out['fields'][$key] !== '') {
- $IDs = array_map('absint', explode(',', $out['fields'][$key]));
- $data = [];
- $icon = JVB_TAXONOMY[$config['taxonomy']]['icon']??jvbDefaultIcon();
- foreach ($IDs as $ID) {
- $term = get_term($ID, jvbCheckBase($config['taxonomy']));
- if ($term && !is_wp_error($term)) {
- $data[$ID] = [
- 'id' => $ID,
- 'icon' => $icon,
- 'name' => $term->name,
- 'url' => get_term_link($ID, jvbCheckBase($config['taxonomy'])),
- ];
- if ($this->tracker) {
- $data[$ID]['umami_click'] = $this->tracker->trackClick($ID, $config['taxonomy'], ['from' => $type.'_'.$postID]);
- }
- }
- }
- if (!empty($data)) {
- $out['fields'][$key] = $data;
- }
- }
- }
+ $out['taxonomies'] = $this->extractTaxonomies($values, $postID, $type);
//Add images
$imgIDs = [];
@@ -192,7 +189,10 @@
$out['id'] = $postID;
$out['content'] = $type;
- $out['icon'] = $config['icon']??jvbDefaultIcon();
+ $out['icon'] = $registrar->getIcon()??jvbDefaultIcon();
+ if ($out['icon'] === '') {
+ $out['icon'] = jvbDefaultIcon();
+ }
if ($this->tracker) {
$args = ($metaType === 'post') ? ['owner_id' => $post->post_author] : [];
@@ -203,34 +203,36 @@
switch ($metaType) {
case 'term':
- $owner = (in_array($type, jvbContentTaxonomies()) ? $meta->getValue('owner') : null);
+
+ $owner = $registrar->hasFeature('is_content') ? $meta->get('owner') : null;
if (!is_null($owner)) {
$out['user_id'] = $owner;
}
$out['url'] = get_term_link($postID, $type);
+ $out['title'] = html_entity_decode($post->name);
break;
case 'post':
$out['date'] = $post->post_date;
+ $out['modified'] = $post->post_modified;
$out['user_id'] = (int)$post->post_author;
$out['url'] = get_the_permalink($postID);
+ $out['title']= get_the_title($postID);
break;
}
-// return $out;
-// }
-// );
- return $out;
+ return $out;
+ }
+ );
}
protected function initTimelineFields(string $content):void
{
- $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);
- $this->fields = $config['fields'];
+ $this->fields = $registrar->getFields();
$this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) {
if (!array_key_exists('for_all', $field) || $field['for_all'] === false){
@@ -256,80 +258,139 @@
}
$item = $this->formatItem($postID, 'post', true);
//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
$children = get_children(['post_parent' => $post->ID, 'orderby' => 'date', 'order' => 'ASC', 'post_status' => ['publish'], 'fields'=> 'ids']);
array_unshift($children, $post->ID);
+ $item['taxonomies'] = $this->extractTaxonomies($item['fields'], $postID, jvbNoBase($post->post_type));
+
$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;
-
+ $item['taxonomies'] = array_merge($item['taxonomies'], $this->extractTaxonomies($f, $postID, jvbNoBase($post->post_type)));
$images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']);
}
+ $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'] = $subFields;
$item['images'] = $item['images'] + $images;
+
return $item;
}
+ protected function extractTaxonomies(array $fields, int $postID, string $content):array {
+ $taxonomies = [];
+ foreach ($fields as $key => $value) {
+ if (empty($value)) {
+ continue;
+ }
- protected function formatTaxonomy(WP_Term $term, int $postID, string $type)
+ $registrar = Registrar::getInstance($key);
+ if (!$registrar || $registrar->registrar->public === false){
+ continue;
+ }
+
+ $terms = array_map('absint', explode(',', $value));
+ $terms = array_filter($terms); // Remove 0 values
+
+ if (empty($terms)) {
+ continue;
+ }
+ foreach($terms as $termID) {
+ $term = get_term($termID, jvbCheckBase($key));
+ if ($term && !is_wp_error($term)) {
+ $taxonomies[$key][$termID] = $this->formatTaxonomy($term, $postID, $content);
+ }
+ }
+ }
+ return $taxonomies;
+ }
+
+ protected function formatTaxonomy(WP_Term|int $term, int $postID, string $type)
{
- return [
- 'ID' => $term->term_id,
- 'title' => htmlspecialchars_decode($term->name),
- 'url' => get_term_link($term->term_id, $term->taxonomy),
- 'umami_click' => $this->tracker->trackTaxonomyClick($term->term_id, $term->taxonomy, [
- 'from' => $type . '_' . $postID
- ])
- ];
+ return $this->cache->remember(
+ $term->term_id,
+ function () use ($term, $postID, $type) {
+ $base = [
+ 'ID' => $term->term_id,
+ 'title' => html_entity_decode($term->name),
+ 'url' => get_term_link($term->term_id, $term->taxonomy),
+ ];
+ if ($this->tracker) {
+ $base['umami_click'] =$this->tracker->trackTaxonomyClick($term->term_id, $term->taxonomy, [
+ 'from' => $type . '_' . $postID
+ ]);
+ }
+ return $base;
+ }
+ );
}
protected function getAuthorData(WP_Post $post)
{
- $author = $this->cache->get($post->post_author, 'author_data');
- if (!$author) {
- $author = [
- 'id' => $post->post_author,
- 'label' => 'Artist',
- 'value' => get_the_author_meta('display_name', $post->post_author),
- 'icon' => 'artist',
- 'url' => get_the_permalink(get_user_meta($post->post_author, BASE . 'link', true)),
- ];
- $this->cache->set($post->post_author, $author, 'author_data');
- }
- return $author;
+ $author = $post->post_author;
+ $userLink = get_user_meta($author, BASE.'profile_link', true);
+ return $this->cache->remember(
+ $userLink,
+ function () use ($userLink, $author) {
+ $label = jvbUserRole($author);
+ $registrar = Registrar::getInstance($label);
+ if ($registrar) {
+ $label = $registrar->getSingular();
+ } else {
+ $label = 'Artist';
+ }
+ return [
+ 'id' => $userLink,
+ 'label' => $label,
+ 'value' => get_the_title($userLink),
+ 'icon' => 'user',
+ 'url' => get_the_permalink($userLink),
+ ];
+ }
+ );
}
protected function getTaxonomies(int $postID, string $content): array
{
- $taxonomies = jvbTaxonomiesForContent($content);
+ $registrar = Registrar::getInstance($content)??false;
+ $taxonomies = $registrar->registrar->taxonomies;
$out = [];
foreach ($taxonomies as $tax) {
$terms = get_the_terms($postID, $tax);
$t = [];
if ($terms && !is_wp_error($terms)) {
- $config = jvbNoBase($tax);
+ $config = Registrar::getInstance($tax);
$out[] = [
- 'icon' => $config,
- 'title' => JVB_TAXONOMY[$config]['plural'],
+ 'icon' => $config->getIcon(),
+ 'title' => $config->getPlural(),
'terms' => array_map(function ($term) use ($tax, $postID, $content) {
- return [
- 'ID' => $term->term_id,
- 'title' => htmlspecialchars_decode($term->name),
- 'url' => get_term_link($term->term_id, $tax),
- 'umami_click' => $this->tracker->trackTaxonomyClick($term->term_id, $tax, [
- 'from' => $content . '_' . $postID
- ])
- ];
+ $item = $this->cache->remember(
+ $term->term_id,
+ function() use ($term, $tax, $content, $postID) {
+ return [
+ 'ID' => $term->term_id,
+ 'title' => html_entity_decode($term->name),
+ 'url' => get_term_link($term->term_id, $tax),
+ ];
+ }
+ );
+ $item['umami_click'] = $this->tracker->trackTaxonomyClick($term->term_id, $tax, [
+ 'from' => $content.'_'.$postID
+ ]);
+ return $item;
}, $terms),
];
+
}
}
return $out;
@@ -341,8 +402,7 @@
$data = $request->get_params();
$args = [
'post_type' => (array_key_exists($data['content'], $this->buildFeedTypesConfig())) ?
- BASE . $data['content'] :
- BASE . array_key_first(JVB_CONTENT),
+ jvbCheckBase($data['content']) : null,
'paged' => intval($data['page'] ?? 1),
'posts_per_page' => $this->per_page,
];
@@ -363,46 +423,7 @@
$args = $this->applyOrderFilters($args, $data);
$args = $this->applyDateFilters($args, $data);
- $args = $this->applyFavouritesFilter($args, $data);
- return $args;
- }
-
- protected function applyTaxonomyFilters(array $args, array $data): array
- {
- if (!isset($data['taxonomy']) || empty($data['taxonomy'])) {
- return $args;
- }
-
- $taxonomyFilters = $data['taxonomy'];
-
- // Validate taxonomies exist and sanitize
- $validFilters = [];
- foreach ($taxonomyFilters as $taxonomy => $terms) {
- if (!taxonomy_exists(jvbCheckBase($taxonomy))) {
- continue;
- }
-
- $validFilters[] = [
- 'taxonomy' => jvbCheckBase($taxonomy),
- 'field' => 'term_id',
- 'terms' => array_map('absint', (array)$terms),
- 'operator' => 'IN'
- ];
- }
-
- if (empty($validFilters)) {
- return $args;
- }
-
- // Determine relation based on match filter
- $relation = ($data['match'] ?? 'all') === 'all' ? 'AND' : 'OR';
-
- $args['tax_query'] = array_merge(
- ['relation' => $relation],
- $validFilters
- );
-
- return $args;
+ return $this->applyFavouritesFilter($args, $data);
}
/**
@@ -413,19 +434,17 @@
public function handleFeedRequest(WP_REST_Request $request): WP_REST_Response
{
$args = $this->buildRequestArgs($request);
- $cacheContext = $this->buildCacheContext($args, $request);
+ $key = $this->cache->generateKey($args);
// Check HTTP cache headers first
$cache_check = $this->checkHeaders(
$request,
- $cacheContext['content_types'],
- $cacheContext['additional_params']
+ $key
);
if ($cache_check) {
return $cache_check; // Returns 304 Not Modified
}
- $key = $this->cache->generateKey($args);
$cached = $this->cache->get($key);
if ($cached) {
if ($request->get_param('highlight')) {
@@ -433,13 +452,13 @@
$args['highlight'] = $highlight;
}
$cached['items'] = $this->processHighlightedItem($cached['items'], $args);
- $response = new WP_REST_Response($cached);
+ $response = $this->success($cached);
return $this->addCacheHeaders($response);
}
// Fetch and format items
$items = $this->fetchFeedItems($args);
- $ttl = (str_contains($args['orderby'], 'RAND')) ? 1800 : $this->cache_ttl;
+ $ttl = (str_contains($args['orderby'], 'RAND')) ? 300 : $this->cacheTtl;
$this->cache->set($key, $items, $ttl);
if ($request->get_param('highlight')) {
@@ -448,86 +467,12 @@
}
$items['items'] = $this->processHighlightedItem($items['items'], $args);
- $response = new WP_REST_Response($items);
+ $response = $this->success($items);
return $this->addCacheHeaders($response);
}
/**
- * Build cache context from query args
- * Extracts content types and parameters needed for proper cache checking
- *
- * @param array $args Built WP_Query arguments
- * @param WP_REST_Request $request Original request
- * @return array Cache context with content_types and additional_params
- */
- protected function buildCacheContext(array $args, WP_REST_Request $request): array
- {
- // Extract content types from post_type in args
- $post_types = is_array($args['post_type'])
- ? $args['post_type']
- : [$args['post_type']];
-
- $content_types = array_map('jvbNoBase', $post_types);
- $content_types[] = 'feed'; // Always include base feed type
-
- // Build additional params for ETag uniqueness
- $additional_params = [
- 'order' => $args['orderby'] ?? 'date',
- 'direction' => $args['order'] ?? 'DESC',
- 'page' => $args['paged'] ?? 1,
- ];
-
- if ($request->get_param('favourites')) {
- $additional_params['user'] = (int)$request->get_param('user');
- }
-
- // Include author filter if present (from context or favourites)
- if (!empty($args['author'])) {
- $additional_params['author'] = $args['author'];
- }
-
- if (!empty($args['author__in'])) {
- $additional_params['author__in'] = $args['author__in'];
- }
-
- // Include taxonomy filters if present
- if (!empty($args['tax_query'])) {
- $tax_filters = [];
- foreach ($args['tax_query'] as $key => $query) {
- if ($key === 'relation' || !is_array($query)) {
- continue;
- }
-
- $taxonomy = jvbNoBase($query['taxonomy'] ?? '');
- if ($taxonomy) {
- $tax_filters[$taxonomy] = $query['terms'] ?? [];
- // Also add taxonomy to content_types for timestamp checking
- $content_types[] = $taxonomy;
- }
- }
- if (!empty($tax_filters)) {
- $additional_params['taxonomies'] = $tax_filters;
- }
- }
-
- // Include date filters if present
- if (!empty($args['date_query'])) {
- $additional_params['date_filter'] = md5(serialize($args['date_query']));
- }
-
- // Include meta queries if present
- if (!empty($args['meta_query'])) {
- $additional_params['meta_filter'] = md5(serialize($args['meta_query']));
- }
-
- return [
- 'content_types' => array_unique($content_types),
- 'additional_params' => $additional_params
- ];
- }
-
- /**
- * @param array $args Formatted Args for WP_Query
+ * @param array $items Formatted Args for WP_Query
* @param array $data parsed Request Data
*
* @return array|null
@@ -546,15 +491,8 @@
// Extract key and value
$key = array_keys($data['highlight'])[0] ?? false;
$value = array_values($data['highlight'])[0] ?? false;
- error_log('Highlighted item: ' . $key);
- error_log('Highlighted item: ' . $value);
- error_log('No Single Content Types: ' . print_r(jvbNoSingleContentTypes(), true));
- error_log('Page: ' . print_r($data['paged'], true));
- if (in_array($key, jvbNoSingleContentTypes()) && $value && $data['paged'] === 1) {
- error_log('Formatted Highlighted item: ' . print_r($this->formatItem($value), true));
- error_log('Items: ' . print_r($items, true));
+ if ($key && $data['paged'] === 1) {
array_unshift($items, $this->formatItem($value));
- error_log('Items after unshift: ' . print_r($items, true));
}
return $items;
}
@@ -571,45 +509,41 @@
return $args;
}
+ $registrar = Registrar::getInstance($context['type']);
switch (true) {
- case contentIsJVBUserType($context['type']):
+ case $registrar->hasFeature('profile_link'):
$args['author'] = (int)get_post_meta($context['id'], BASE . 'link', true);
break;
- case taxIsJVBContentTax($context['type']):
+ case $registrar->getType() === 'term' && $registrar->hasFeature('is_content'):
$args['post_type'] = is_array($args['post_type'])
? $args['post_type']
: explode(',', $args['post_type']);
// Check if filtering global feed content
- $globalFeedTypes = array_map('jvbCheckBase',
- array_keys(Features::getTypesWithFeature('show_feed', 'content'))
- );
+ if (in_array(jvbNoBase($context['type']), Registrar::getFeatured('is_content', 'term'))) {
+ // Global: show posts from any content type with this taxonomy
+ $for_content = Registrar::getInstance($context['type'])->registrar->for ?? [];
- if (array_intersect($args['post_type'], $globalFeedTypes)) {
- $artists = jvbGetContentUsers($context['id']);
- if (!empty($artists)) {
- $args['author__in'] = $artists;
- }
- } else {
- $args['tax_query'] = [
- 'relation' => 'AND',
- [
- 'taxonomy' => BASE . $context['type'],
- 'terms' => $context['id'],
- ]
- ];
+ // Convert to full post types with BASE prefix
+ $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');
+ $args['post_type'] = array_intersect(
+ $post_types,
+ array_map(fn($type) => jvbCheckBase($type), $show_feed_types)
+ );
}
- break;
- case taxonomy_exists(jvbCheckBase($context['type'])):
- $args['tax_query'] = [
- 'relation' => 'AND',
- [
- 'taxonomy' => BASE . $context['type'],
- 'terms' => $context['id'],
- ]
+
+ // Add term to tax query
+ $args['tax_query'][] = [
+ 'taxonomy' => jvbCheckBase($context['type']),
+ 'field' => 'term_id',
+ 'terms' => [(int)$context['id']],
];
break;
}
+
return $args;
}
@@ -619,38 +553,34 @@
*
* @return array
*/
- protected function applyFavouritesFilter(array $args, array $filters): array
+ protected function applyFavouritesFilter(array $args, array $data): array
{
- if (!array_key_exists('favourites', $filters)) {
+ if (empty($data['favourites']) || empty($data['user'])) {
return $args;
}
- global $wpdb;
- // Get post types for the current filter
- $post_types = is_array($args['post_type'])
- ? $args['post_type']
- : [$args['post_type']];
+ $user_id = (int)$data['user'];
+ $content = jvbNoBase($args['post_type']);
- $favourites_table = $wpdb->prefix . BASE . 'favourites';
- $placeholders = implode(',', array_fill(0, count($post_types), '%s'));
- $favourited_ids = $wpdb->get_col($wpdb->prepare(
- "SELECT target_id FROM {$favourites_table}
- WHERE user_id = %d AND type IN ($placeholders)",
- array_merge(
- [get_current_user_id()],
- $post_types
- )
- ));
+ // Get user's favourites for this content type
+ $fav_key = BASE . 'favourites_' . $content;
+ $favourites = get_user_meta($user_id, $fav_key, true);
- if (empty($favourited_ids)) {
- // Force empty results
+ if (empty($favourites)) {
+ // No favourites - return empty result
+ $args['post__in'] = [0]; // Will return no results
+ return $args;
+ }
+
+ $fav_ids = array_filter(array_map('intval', explode(',', $favourites)));
+
+ if (empty($fav_ids)) {
$args['post__in'] = [0];
return $args;
}
- $args['post__in'] = isset($args['post__in'])
- ? array_intersect($args['post__in'], $favourited_ids)
- : $favourited_ids;
+ $args['post__in'] = $fav_ids;
+ $args['orderby'] = 'post__in'; // Preserve favourite order
return $args;
}
@@ -664,11 +594,11 @@
{
$postType = is_array($args['post_type']) ? $args['post_type'][0] : $args['post_type'];
$slug = jvbNoBase($postType);
-
- if (Features::forContent($slug)->has('is_timeline')) {
+ $registrar = Registrar::getInstance($slug);
+ if ($registrar && $registrar->hasFeature('is_timeline')) {
$args['post_parent'] = 0;
}
- if (in_array($slug, Features::getTypesWithFeature('is_content', 'taxonomy'))) {
+ if ($registrar && $registrar->hasFeature('is_content')) {
return $this->handleContentTaxonomies($args);
}
$args['fields'] = 'ids';
@@ -1195,15 +1125,6 @@
}
}
- /**
- * Get custom table fields for a taxonomy
- * @param string $taxonomy Taxonomy type
- * @return array Field definitions
- */
- protected function getCustomTableFields(string $taxonomy): array
- {
- return jvbContentTaxonomiesTableFields($taxonomy)['fields'] ?? [];
- }
/**
* Get available feed types (for block editor)
@@ -1212,14 +1133,14 @@
public function getFeedTypes(WP_REST_Request $request): WP_REST_Response
{
// Check HTTP cache
- $cache_check = $this->checkHeaders($request, ['feed_types']);
+ $cache_check = $this->checkHeaders($request, 'feed_types');
if ($cache_check) {
return $cache_check;
}
$feedTypes = $this->buildFeedTypesConfig();
- $response = new WP_REST_Response($feedTypes);
+ $response = $this->success($feedTypes);
return $this->addCacheHeaders($response);
}
@@ -1232,44 +1153,44 @@
*/
protected function buildFeedTypesConfig(): array
{
- if (!$this->checker) {
- $this->checker = Checker::getInstance();
- }
return $this->cache->remember(
'contentTypes',
function () {
$config = [];
// Get content types with show_feed
- $contentTypes = Features::getTypesWithFeature('show_feed', 'content');
+ $contentTypes = Registrar::getFeatured('show_feed', 'post');
foreach ($contentTypes as $slug) {
- $contentConfig = JVB_CONTENT[$slug] ?? null;
- if (!$contentConfig) continue;
+ $this->cache->tag('content:'.$slug);
+ $registrar = Registrar::getInstance($slug);
+ if (!$registrar) continue;
$config[$slug] = [
'type' => 'content',
- 'singular' => $contentConfig['singular'] ?? ucfirst($slug),
- 'plural' => $contentConfig['plural'] ?? ucfirst($slug) . 's',
- 'icon' => $slug,
- 'taxonomies' => $this->checker->getTaxonomiesForContent($slug),
+ 'singular' => $registrar->getSingular(),
+ 'plural' => $registrar->getPlural(),
+ 'icon' => $registrar->getIcon(),
+ 'taxonomies' => $registrar->registrar->taxonomies,
];
}
// Get taxonomies with show_feed (content taxonomies)
- $taxonomies = Features::getTypesWithFeature('show_feed', 'taxonomy');
+ $taxonomies = Registrar::getFeatured('show_feed', 'term');
foreach ($taxonomies as $slug) {
- $taxConfig = JVB_TAXONOMY[$slug] ?? null;
- if (!$taxConfig || !($taxConfig['is_content'] ?? false)) {
+ $registrar = Registrar::getInstance($slug);
+ if (!$registrar || !($registrar->hasFeature('is_content') ?? false)) {
continue;
}
+ $this->cache->tag('taxonomy:'.$slug);
+
$config[$slug] = [
'type' => 'taxonomy',
- 'singular' => $taxConfig['singular'] ?? ucfirst($slug),
- 'plural' => $taxConfig['plural'] ?? ucfirst($slug) . 's',
- 'icon' => $slug,
+ 'singular' => $registrar->getSingular(),
+ 'plural' => $registrar->getPlural(),
+ 'icon' => $registrar->getIcon(),
'taxonomies' => [], // Content taxonomies don't have sub-taxonomies
- 'for_content' => $taxConfig['for_content'] ?? [],
+ 'for_content' => $registrar->registrar->for ?? []
];
}
--
Gitblit v1.10.0