From e9967fa22781d922ba4eb8fb44fe72d200ac4b14 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 10 Nov 2025 21:04:10 +0000
Subject: [PATCH] =IconsManager.php update

---
 inc/rest/routes/ContentRoutes.php |  255 ++++++++++++++++++++++++++++++---------------------
 1 files changed, 150 insertions(+), 105 deletions(-)

diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index ff056a9..dfa9cb6 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -25,14 +25,18 @@
     protected string $post_type = '';
     protected string $user_id = '';
 
+	//For Timeline-specific posts
+	protected array $timelineSharedFields = [];
+	protected array $timelineUniqueFields = [];
+
     //TODO: Ensure we are handling the bulk operations for all processes
-    //TODO: be sure to clear cache ($this->>cache->invalidateGroup($this->>cache_name)) on content update/create
     //TODO: Also invalidate feed caches on updates!!
 
     public function __construct()
     {
         $this->cache_name = 'user_content_'.get_current_user_id();
         parent::__construct();
+
         $this->action = 'dash-';
         $this->operation_type = 'content_update';
         add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
@@ -75,6 +79,32 @@
         ]);
     }
 
+	protected function initTimelineFields(string $content):void
+	{
+		$content = jvbNoBase($content);
+		if (!Features::forContent($content)->has('is_timeline')){
+			return;
+		}
+		$config = Features::getConfig($content);
+		$this->fields = $config['fields'];
+
+		$this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) {
+			if (!array_key_exists('for_all', $field) || $field['for_all'] === false){
+				return true;
+			}
+			return false;
+		}));
+		array_unshift($this->timelineSharedFields, 'post_thumbnail');
+		array_unshift($this->timelineSharedFields, 'post_title');
+
+		$this->timelineUniqueFields = array_keys(array_filter($this->fields, function ($field) {
+			if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
+				return true;
+			}
+			return false;
+		}));
+	}
+
     /**
      * Handle content update/creation
      * @param WP_REST_Request $request
@@ -187,9 +217,6 @@
         }
         $post_type = str_replace('-', '_',jvbCheckBase($params['content']));
 
-		$config = Features::getConfig($params['content']);
-
-
 
         // Build query args
         $args = [
@@ -396,14 +423,12 @@
 
 			}
 
-            CacheManager::invalidateGroup($post_data['content']);
+            CacheManager::for($post_data['content'])->clear();
 			if (jvbSiteUsesFeedBlock()) {
-				CacheManager::invalidateGroup($post_data['feed']);
+				CacheManager::for('feed')->clear();
 			}
         }
 
-
-        CacheManager::invalidateGroup('user_content');
 		if (jvbSiteHasNotifications()) {
 			$this->notifications = JVB()->notification();
 			$this->notifications->addNotification(
@@ -433,83 +458,109 @@
 			return ['success' => false, 'message' => 'No permission'];
 		}
 
-		$rows = $post_data['fields'] ?? [];
-		if (empty($rows)) {
-			return ['success' => false, 'message' => 'No data'];
-		}
+		$this->fields = jvbGetFields($post_data['content']);
+		$this->initTimelineFields($post_data['content']);
+		error_log('Received Data: '.print_r($post_data, true));
 
-		$fields = jvbGetFields($post_data['content']);
-
-		// First row = parent post
-		$parent_row = array_shift($rows);
-		if (($parent_row['id'] ?? null) != $parent_id) {
-			return ['success' => false, 'message' => 'Parent ID mismatch'];
-		}
-
-		$allowedFields = array_filter($parent_row, function($key) use ($fields) {
-			return array_key_exists($key, $fields);
+		// First, process the main fields that will apply to all posts
+		$sharedData = array_filter($post_data, function ($key) {
+			return in_array($key, $this->timelineSharedFields);
 		}, ARRAY_FILTER_USE_KEY);
 
-		$parentMeta = new MetaManager($parent_id, 'post');
-		$parentMeta->setAll($allowedFields);
+		//Next, process any individual posts, including any menu order changes
+		if (array_key_exists('timeline', $post_data) && is_array($post_data['timeline'])) {
+			$sharedTaxonomies = $sharedData;
+			unset($sharedTaxonomies['post_title']);
 
-		// Get existing children to track deletions
-		$existing_children = get_children([
-			'post_parent' => $parent_id,
-			'post_type' => jvbCheckBase($post_data['content']),
-			'fields' => 'ids'
-		]);
-
-		$processed_ids = [];
-
-		// Process remaining rows as children
-		foreach ($rows as $index => $row_data) {
-			$row_id = $row_data['id'] ?? null;
-
-			// New child post
-			if (!$row_id || str_starts_with($row_id, 'new')) {
-				$child_id = wp_insert_post([
-					'post_type' => jvbCheckBase($post_data['content']),
-					'post_parent' => $parent_id,
-					'post_author' => $this->user_id,
-					'post_status' => $post_data['status'] ?? 'draft',
-					'menu_order' => $index
-				]);
+			//Ensure the parent post exists and is still first in the array
+			$index = array_search((string)$parent_id, array_column($post_data['timeline'], 'id'));
+			error_log('Found index: '.print_r($index, true));
+			if ($index === false) {
+				return [
+					'success' => false,
+					'message'	=> 'Missing parent id. This should not have happened'
+				];
+			} elseif ($index !== 0) {
+				// Move that element to the start of the array
+				$item = $post_data['timeline'][$index];
+				unset($post_data['timeline'][$index]);
+				array_unshift($post_data['timeline'], $item);
 			}
-			// Existing child post
-			else {
-				$child_id = (int) $row_id;
+			$errors = [];
+			$success = [];
+			// Get existing children to track deletions
+			$existing_children = get_children([
+				'post_parent' => $parent_id,
+				'post_type' => jvbCheckBase($post_data['content']),
+				'fields' => 'ids'
+			]);
+			//Iterate through the timeline posts
+			foreach($post_data['timeline'] as $order => $timeline) {
+				$allowedFields = array_filter($timeline, function($key) {
+					return in_array($key, $this->timelineUniqueFields);
+				}, ARRAY_FILTER_USE_KEY);
 
-				// Verify ownership via parent
-				if (!in_array($child_id, $existing_children)) {
-					continue; // Skip if not actually a child of this parent
+				$allowedFields['post_title'] = $allowedFields['post_title'] ?? $sharedData['post_title'].' - Treatment #'.$order;
+				$allowedFields = array_merge($allowedFields, $sharedTaxonomies);
+				if (!array_key_exists('id', $timeline) || !is_numeric($timeline['id'])) {
+					$newChild = wp_insert_post([
+						'post_author'	=> $this->user_id,
+						'post_type'		=> jvbCheckBase($post_data['content']),
+						'post_title'	=> $allowedFields['post_title'],
+						'post_parent'	=> $parent_id,
+						'menu_order'	=> $order
+					]);
+					if (!$newChild || is_wp_error($newChild)) {
+						$errors[] = [
+							'message'	=> 'Could not create child post',
+							'data'		=> $timeline
+						];
+						continue;
+					}
+					$timeline['id'] = $newChild;
 				}
 
-				// Update menu_order (position may have changed)
-				wp_update_post([
-					'ID' => $child_id,
-					'menu_order' => $index
-				]);
+				if (in_array((int)$timeline['id'], $existing_children)) {
+					unset($existing_children[array_search((int)$timeline['id'], $existing_children)]);
+				}
+
+				//Determine which fields to update
+				$meta = new MetaManager($timeline['id'], 'post');
+				$oldValues = $meta->getAll(array_keys($allowedFields));
+				$updateValues = array_filter($allowedFields, function($value, $key) use ($oldValues) {
+					return (!array_key_exists($key, $oldValues) || $value !== $oldValues[$key]);
+				}, ARRAY_FILTER_USE_BOTH);
+				$meta->setAll($updateValues);
+				//Update Menu Order, if applicable
+				if ((int) $timeline['id'] !== $parent_id) {
+					$post = get_post((int) $timeline['id']);
+					if ($post && $post->menu_order !== $order) {
+						$updated = wp_update_post([
+							'ID'			=> $post->ID,
+							'menu_order'	=> $order,
+						]);
+						if (!$updated || is_wp_error($updated)) {
+							$errors[] = [
+								'message'	=> 'Could not update timeline order for post',
+								'data'		=> $timeline
+							];
+						}
+					}
+				}
+				$success[] = $timeline['id'];
 			}
-
-			// Update child meta
-			$allowedChildFields = array_filter($row_data, function($key) use ($fields) {
-				return array_key_exists($key, $fields) && $key !== 'id' && $key !== 'draggable';
-			}, ARRAY_FILTER_USE_KEY);
-
-			$childMeta = new MetaManager($child_id, 'post');
-			$childMeta->setAll($allowedChildFields);
-
-			$processed_ids[] = $child_id;
+		}
+		//Delete any remaining children that no longer exist
+		if (!empty($existing_children)) {
+			foreach ($existing_children as $ID) {
+				wp_delete_post($ID);
+			}
 		}
 
-		// Delete removed children
-		$deleted_ids = array_diff($existing_children, $processed_ids);
-		foreach ($deleted_ids as $delete_id) {
-			wp_delete_post($delete_id, true);
-		}
-
-		return ['success' => true, 'processed' => $processed_ids];
+		return ['success' => true, 'data' => [
+			'success'	=> $success,
+			'errors'	=> $errors
+		]];
 	}
 
     /**
@@ -590,9 +641,10 @@
      *
      * @return array
      */
-    protected function prepareItem(WP_Post $post, $skip = false):array
+    protected function prepareItem(WP_Post $post, bool $skip = false, bool $fields = true):array
     {
 		if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) {
+			$this->initTimelineFields($post->post_type);
 			return $this->formatTimeline($post);
 		}
         $this->meta = new MetaManager($post->ID, 'post');
@@ -605,7 +657,7 @@
             'alt'       => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true),
             'icon'      => $this->post_type,
             'taxonomies'=> [],
-            'fields'    => $this->meta->getAll(),
+            'fields'    => ($fields) ? $this->meta->getAll() : [],
 			'images'	=> [],
         ];
 
@@ -633,12 +685,13 @@
 
         return $data;
     }
-	protected function extractImages():array
+	protected function extractImages(array $fields = []):array
 	{
 		//Extract images
 		$images = [];
 		$get = [];
-		foreach ($this->fields as $field => $config) {
+		$fields = (empty($fields)) ? $this->fields : $fields;
+		foreach ($fields as $field => $config) {
 			if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') {
 				$get[] = $field;
 			}
@@ -660,36 +713,29 @@
 
 	protected function formatTimeline(WP_Post $post):array
 	{
-		$data = $this->prepareItem($post, true);
-		$firstRow = $data['fields'];
-		$firstRow['id'] = $post->ID;
-		$firstRow['draggable'] = false;
-		$fields = [$firstRow];
+		$item = $this->prepareItem($post, true, false);
+		//Step 1: Get the fields that apply to all posts
+		$mainMeta = new MetaManager($post->ID, 'post');
+		$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
 
-		$children = get_children(['post_parent' => $post->ID, 'orderby' => 'menu_order']);
-		$allImages = [];
+		//Step 2: Get the fields for each individual posts
+		$children = get_children(['post_parent' => $post->ID, 'orderby' => 'menu_order', 'post_status' => ['publish', 'draft'], 'fields'=> 'ids']);
+		array_unshift($children, $post->ID);
 
+		$subFields = [];
+		$images = [];
 		foreach ($children as $child) {
-			$this->meta = new MetaManager($child->ID, 'post');
-			$row = $this->meta->getAll();  // Store in variable first
-			$row['id'] = $child->ID;       // Add ID to the row
-			$row['draggable'] = true;      // Mark as draggable
-			$fields[] = $row;              // Then append to fields
+			$meta = new MetaManager($child, 'post');
+			$f = $meta->getAll($this->timelineUniqueFields);
+			$f =  ['id' => $child] + $f;
+			$subFields[$f['post_thumbnail']] = $f;
 
-			$images = $this->extractImages();
-			if (!empty($images)) {
-				$allImages = $allImages + $images;
-			}
+			$images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']);
 		}
+		$item['fields']['timeline'] = $subFields;
+		$item['images'] = $item['images'] + $images;
 
-		if (!empty($allImages)) {
-			if (!array_key_exists('images', $data)) {
-				$data['images'] = [];
-			}
-			$data['images'] = $data['images'] + $allImages;
-		}
-		$data['fields']['timeline'] = $fields;
-		return $data;
+		return $item;
 	}
 
     /**
@@ -839,9 +885,8 @@
                     }
 
                     //Clear cache
-                    CacheManager::invalidateGroup($data['content']);
-                    CacheManager::invalidateGroup('feed');
-                    CacheManager::invalidateGroup('user_content');
+					CacheManager::for($data['content'])->clear();
+                    CacheManager::for('feed')->clear();
                 }
 
 				return [

--
Gitblit v1.10.0