Jake Vanderwerf
2026-01-11 474109a5df0a06f5343ab184838fe2d80e3872a8
inc/rest/routes/ContentRoutes.php
@@ -36,7 +36,7 @@
    {
        $this->cache_name = 'user_content_'.get_current_user_id();
        parent::__construct();
      $this->cache->clear();
        $this->action = 'dash-';
        $this->operation_type = 'content_update';
        add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
@@ -261,17 +261,27 @@
      if (Features::forContent($post_type)->has('is_calendar'))  {
         $args = $this->applyCalendarFilters($args, $params);
      }
      $taxonomies = array_filter($params, function($param) {
         return str_starts_with($param, 'tax_');
      }, ARRAY_FILTER_USE_KEY);
      if (!empty($taxonomies)) {
         $params['taxonomies'] = [];
         foreach ($taxonomies as $taxonomy => $terms) {
            $taxonomy = str_replace('tax_', '', $taxonomy);
            $params['taxonomies'][$taxonomy] = $terms;
         }
      }
      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']);
      }
@@ -287,16 +297,20 @@
        $cache = $this->cache->get($key);
      $cache = false;
        if ($cache) {
            $response = new WP_REST_Response($cache);
         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);
@@ -316,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
@@ -360,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')) {
@@ -863,7 +971,7 @@
      return $images;
   }
   protected function formatTimeline(WP_Post $post):array
   public function formatTimeline(WP_Post $post):array
   {
      $item = $this->prepareItem($post, true, false);
      //Step 1: Get the fields that apply to all posts