From 474109a5df0a06f5343ab184838fe2d80e3872a8 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 11 Jan 2026 19:23:20 +0000
Subject: [PATCH] =Fixed timeline CRUD.js issue where this.activeItem was set null when we still needed it

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

diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index 9a1eae9..38f9ddc 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -274,14 +274,14 @@
 		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']);
 		}
 
@@ -302,10 +302,15 @@
 			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);
+		}
+
         // Run query
         $query = new WP_Query($args);
 
-        $this->post_type = $params['content']??$params['type'];
 
         $this->fields = jvbGetFields(str_replace('-','_',$this->post_type));
         $this->taxonomies = $this->getTaxonomies($this->post_type);
@@ -325,6 +330,87 @@
 		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 (!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 {
+				$args['tax_query'] = [
+					'relation' => 'OR',
+					...$term_queries
+				];
+			}
+		}
+
+		return $args;
+	}
+
     /**
      * Gets allowed taxonomies for a particular content
      * @param string $content
@@ -369,7 +455,20 @@
 
         foreach ($posts as $ID => $post_data) {
 			if (Features::forContent($post_data['content'])->has('is_timeline') && array_key_exists('timeline', $post_data)) {
-				$results[$ID] =$this->processTimelinePost($ID, $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')) {

--
Gitblit v1.10.0